Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
name: Compile Test

on:
pull_request:
branches: [ master ]
types: [ opened ]

push:
paths: [ '**.go' ]
branches-ignore:
- master

Expand All @@ -15,7 +20,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.23.5
go-version: 1.24.0
check-latest: true

- name: Go Format
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.23.5
go-version: 1.24.0
check-latest: true

- name: Checkout Repo
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
name: Vulnerability Check

on:
pull_request:
branches: [ master ]
types: [ opened ]

push:
paths: [ '**.go' ]
branches-ignore:
Expand All @@ -16,7 +20,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.23.5
go-version: 1.24.0
check-latest: true

- name: Go Format
Expand Down
17 changes: 14 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
name: Go Tests

on:
pull_request:
branches: [ master ]
types: [ opened ]

push:
paths: [ '**.go' ]
branches-ignore:
Expand All @@ -16,13 +20,20 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: 1.23.5
go-version: 1.24.0
check-latest: true

- name: Go Format
run: gofmt -s -w . && git diff --exit-code

- name: Get Dependencies
run: go get ./...

- name: Go Tests
run: go test -v -count=1 -race -shuffle=on -coverprofile=coverage.txt ./...
# - name: Go Tests
# run: go test -v -count=1 -race -shuffle=on -coverprofile=coverage.txt ./...

- name: Test ./internal/distlog
run: go test -v -count=1 -race -shuffle=on ./internal/distlog

- name: Test ./internal/conf
run: go test -v -count=1 -race -shuffle=on ./internal/conf
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
*.log
scripts/
docs/test/*
playground/
test/
terraform/
bin/*
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ build: install
run: build

test: install build
@go test ./internal/conf
@./bin/mac/s3p use -f "docs/test/test_profile_aws.yaml"
@go test -v ./internal/conf
@go test -v ./internal/distlog
@./bin/mac/s3p use -f "test/configs/aws.yaml"
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# s3p - A configurable profile-based S3 backup and upload tool.
# s3p - A configurable profile-based object backup and upload tool.

**CLI for Linux/MacOS** **supports** Amazon S3 **|** Google Cloud Storage **|** Linode (Akamai) Object Storage **|**
Oracle Cloud Object Storage
Expand All @@ -8,20 +8,26 @@ Oracle Cloud Object Storage
[![Go Report Card][go_report_img]][go_report_url]
[![Repo License][repo_license_img]][repo_license_url]

[![Code Quality](https://github.com/orme292/s3packer/actions/workflows/golang.yml/badge.svg)](https://github.com/orme292/s3packer/actions/workflows/golang.yml)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/orme292/s3packer/tests.yml?style=for-the-badge&label=Tests&labelColor=blue)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/orme292/s3packer/quality.yml?style=for-the-badge&label=Vulnerabilities&labelColor=blue)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/orme292/s3packer/compile.yml?style=for-the-badge&label=Compiles&labelColor=blue)





---

[![Jetbrains_OSS][jetbrains_logo]][jetbrains_oss_url] [![Jetbrains_GoLand][jetbrains_goland_logo]][jetbrains_goland_url]

Special thanks to JetBrains! **s3p** is developed with help from JetBrains' Open Source program.
Special thanks to JetBrains! **s3p** was developed with help from JetBrains' Open Source program.

---
## About

s3p is an S3 / Object Storage upload and backup tool. It uses YAML-based configs that tell it what to upload, where to
upload, how to name, and how to tag the objects. s3p makes backup redundancy easier by using separate profiles
for buckets and providers. Currently it supports AWS, OCI (Oracle Cloud), and Linode (Akamai).
for buckets and providers. Currently, it supports AWS, Google Cloud, Linode (Akamai), OCI (Oracle Cloud).

---

Expand Down Expand Up @@ -302,9 +308,11 @@ issue on [GitHub][issue_repo_url].
[releases_url]: https://github.com/orme292/s3packer/releases
[issue_repo_url]: https://github.com/orme292/s3packer/issues/new/choose

[go_version_url]: https://golang.org/doc/go1.23
[go_version_url]: https://golang.org/doc/go1.24
[go_report_url]: https://goreportcard.com/report/github.com/orme292/s3packer
[repo_license_url]: https://github.com/orme292/s3packer/blob/master/LICENSE
[go_tests_url]: https://img.shields.io/github/actions/workflow/status/orme292/s3packer/tests.yml?style=for-the-badge&label=Tests

[s3_acl_url]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/acl-overview.html#canned-acl

[s3packer_aws_readme_url]: https://github.com/orme292/s3packer/blob/master/docs/using_aws.md
Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module s3p

go 1.23.5
go 1.24.0

require (
cloud.google.com/go/storage v1.50.0
Expand All @@ -16,6 +16,7 @@ require (
github.com/orme292/symwalker v0.2.2
github.com/rs/zerolog v1.33.0
github.com/spf13/cobra v1.8.1
github.com/stretchr/testify v1.10.0
golang.org/x/text v0.21.0
google.golang.org/api v0.214.0
gopkg.in/yaml.v3 v3.0.1
Expand Down Expand Up @@ -47,6 +48,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/sts v1.33.9 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
Expand All @@ -62,9 +64,9 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sony/gobreaker v0.5.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/objx v0.5.2 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions internal/distlog/log_level.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ func ParseLevel(n any) zerolog.Level {
return WARN
}

if lvl == zerolog.NoLevel {
return WARN
}

return lvl

}
74 changes: 74 additions & 0 deletions internal/distlog/log_level_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package distlog

import (
"testing"

"github.com/rs/zerolog"
"github.com/stretchr/testify/require"
)

func TestParseLevel(t *testing.T) {
tests := []struct {
name string
input any
expected zerolog.Level
}{
{
name: "int 0 => debug",
input: 0,
expected: zerolog.DebugLevel, // "0" => debug
},
{
name: "int 1 => info",
input: 1,
expected: zerolog.InfoLevel, // "1" => info
},
{
name: "int 2 => warn",
input: 2,
expected: zerolog.WarnLevel, // "2" => warn
},
{
name: "int 3 => error",
input: 3,
expected: zerolog.ErrorLevel, // "3" => error
},
{
name: "bool true => warn (parses as '2')",
input: true,
expected: zerolog.WarnLevel, // "2" => warn
},
{
name: "bool false => warn (parses as '2')",
input: false,
expected: zerolog.WarnLevel, // "2" => warn
},
{
name: "any string => returns warn (code sets str = \"\" and fails parse)",
input: "",
expected: zerolog.WarnLevel, // because it never sets str=v, so parseLevel("") => error => default to warn
},
{
name: "invalid type => warn (sets '2')",
input: struct{}{},
expected: zerolog.WarnLevel, // "2" => warn
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := ParseLevel(tt.input)
require.Equal(t, tt.expected, result, "ParseLevel(%v) did not return expected level", tt.input)
})
}
}

func TestZerologNumericLevels(t *testing.T) {
require.Equal(t, -1, int(zerolog.TraceLevel), "Trace level should be -1")
require.Equal(t, 0, int(zerolog.DebugLevel), "Debug level should be 0")
require.Equal(t, 1, int(zerolog.InfoLevel), "Info level should be 1")
require.Equal(t, 2, int(zerolog.WarnLevel), "Warn level should be 2")
require.Equal(t, 3, int(zerolog.ErrorLevel), "Error level should be 3")
require.Equal(t, 4, int(zerolog.FatalLevel), "Fatal level should be 4")
require.Equal(t, 5, int(zerolog.PanicLevel), "Panic level should be 5")
}
50 changes: 32 additions & 18 deletions internal/distlog/type_logbot.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import (
"github.com/rs/zerolog/log"
)

const (
EMPTY = ""
SPACE = " "
NEWLINE = "\n"
)

type LogOutput struct {
Console bool
File bool
Expand All @@ -21,6 +27,10 @@ type LogBot struct {
Logfile string
}

// exitFunc is a function pointer that normally points to os.Exit. We override
// it in tests so we can verify calls to RouteLogMsg that would exit.
var exitFunc = os.Exit

func (lb *LogBot) SetLogLevel(lvl zerolog.Level) {
zerolog.SetGlobalLevel(lvl)
lb.Level = lvl
Expand All @@ -35,7 +45,7 @@ func (lb *LogBot) BuildLogger(lvl zerolog.Level) zerolog.Logger {
logFile, err := os.OpenFile(lb.Logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0o640)
if err != nil {
log.Fatal().Msg("Unable to open log file.")
os.Exit(1)
exitFunc(1)
}

if lb.Output.Console {
Expand Down Expand Up @@ -68,62 +78,66 @@ func (lb *LogBot) RouteLogMsg(lvl zerolog.Level, msg string) {
z.WithLevel(lvl).Msg(msg)
}

if lvl == zerolog.FatalLevel || lvl == zerolog.PanicLevel {
os.Exit(1)
if lvl == zerolog.FatalLevel {
exitFunc(1)
} else if lvl == zerolog.PanicLevel {
panic(fmt.Sprintf("unrecoverable error: %s", msg))
}

}

/*
Blast takes a string and passes it to LogBot.RouteLogMsg with zerolog.NoLevel.
This ensures the message is logged regardless of the global log level.
*/
func (lb *LogBot) Blast(format string, a ...any) {
msg := fmt.Sprintf(format, a...)
lb.RouteLogMsg(BLAST, msg)
lb.RouteLogMsg(BLAST, getMsg(format, a...))
}

func (lb *LogBot) Panic(format string, a ...any) {
msg := fmt.Sprintf(format, a...)
lb.RouteLogMsg(PANIC, msg)
lb.RouteLogMsg(PANIC, getMsg(format, a...))
}

/*
Fatal takes a string and passes it to LogBot.RouteLogMsg with zerolog.FatalLevel.
*/
func (lb *LogBot) Fatal(format string, a ...any) {
msg := fmt.Sprintf(format, a...)
lb.RouteLogMsg(FATAL, msg)
lb.RouteLogMsg(FATAL, getMsg(format, a...))
}

/*
Error takes a string and passes it to LogBot.RouteLogMsg with zerolog.ErrorLevel.
*/
func (lb *LogBot) Error(format string, a ...any) {
msg := fmt.Sprintf(format, a...)
lb.RouteLogMsg(ERROR, msg)
lb.RouteLogMsg(ERROR, getMsg(format, a...))
}

/*
Warn takes a string and passes it to LogBot.RouteLogMsg with zerolog.WarnLevel.
*/
func (lb *LogBot) Warn(format string, a ...any) {
msg := fmt.Sprintf(format, a...)
lb.RouteLogMsg(WARN, msg)
lb.RouteLogMsg(WARN, getMsg(format, a...))
}

/*
Info takes a string and passes it to LogBot.RouteLogMsg with zerolog.InfoLevel.
*/
func (lb *LogBot) Info(format string, a ...any) {
msg := fmt.Sprintf(format, a...)
lb.RouteLogMsg(INFO, msg)
lb.RouteLogMsg(INFO, getMsg(format, a...))
}

/*
Debug takes a string and passes it to LogBot.RouteLogMsg with zerolog.DebugLevel.
*/
func (lb *LogBot) Debug(format string, a ...any) {
msg := fmt.Sprintf(format, a...)
lb.RouteLogMsg(DEBUG, msg)
lb.RouteLogMsg(DEBUG, getMsg(format, a...))
}

func getMsg(format string, a ...any) string {
var msg string
if len(a) == 0 {
msg = format
} else {
msg = fmt.Sprintf(format, a...)
}
return msg
}
Loading
Loading