feat: handle multiple commands sync and list duplicates

This commit is contained in:
Urko 2023-07-18 12:05:22 +02:00
parent 2ca045913d
commit 022d56dc8e
4 changed files with 151 additions and 21 deletions

52
cmd/duplicate_versions.go Normal file
View File

@ -0,0 +1,52 @@
package cmd
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"gitea.urkob.com/urko/backblaze-backup/internal/services"
"gitea.urkob.com/urko/backblaze-backup/kit/config"
"github.com/spf13/cobra"
)
var Versions = &cobra.Command{
Use: "versions",
Short: "Handle versions of files in Backblaze",
Run: func(cmd *cobra.Command, args []string) {
log.SetFlags(log.Lmicroseconds)
ctx, cancel := context.WithCancel(signalContext(cmd.Context()))
defer cancel()
envFile := ""
if os.Getenv("BACKBLAZE_ENV") == "dev" {
envFile = ".env"
}
cfg := config.NewConfig(envFile)
bbService := services.NewBackBlaze(cfg.BbId, cfg.BbKey)
if err := bbService.ListDuplicateVersions(ctx); err != nil {
log.Fatalln("bbService.ListDuplicateVersions()", err)
}
},
}
func signalContext(ctx context.Context) context.Context {
ctx, cancel := context.WithCancel(ctx)
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
go func() {
log.Println("listening for shutdown signal")
<-sigs
log.Println("shutdown signal received")
signal.Stop(sigs)
close(sigs)
cancel()
}()
return ctx
}

View File

@ -1,7 +1,7 @@
package main package cmd
import ( import (
"fmt" "context"
"log" "log"
"os" "os"
@ -10,11 +10,14 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
var rootCmd = &cobra.Command{ var Sync = &cobra.Command{
Use: "backblaze-backup", Use: "sync",
Short: "Backblaze backup tool", Short: "Sync files or directories to Backblaze",
Long: `A tool to backup files and directories to Backblaze.`, Long: `A tool to backup files and directories to Backblaze.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
ctx, cancel := context.WithCancel(signalContext(cmd.Context()))
defer cancel()
log.SetFlags(log.Lmicroseconds) log.SetFlags(log.Lmicroseconds)
filePath, err := cmd.Flags().GetString("file") filePath, err := cmd.Flags().GetString("file")
if err != nil { if err != nil {
@ -36,21 +39,8 @@ var rootCmd = &cobra.Command{
cfg := config.NewConfig(envFile) cfg := config.NewConfig(envFile)
bbService := services.NewBackBlaze(cfg.BbId, cfg.BbKey).WithBucket(bucketName).WithDir(dir).WithFile(filePath) bbService := services.NewBackBlaze(cfg.BbId, cfg.BbKey).WithBucket(bucketName).WithDir(dir).WithFile(filePath)
if err := bbService.Sync(); err != nil { if err := bbService.Sync(ctx); err != nil {
log.Fatalln("bbService.Sync()", err) log.Fatalln("bbService.Sync()", err)
} }
}, },
} }
func init() {
rootCmd.PersistentFlags().String("file", "", "absolute path of the file you want to upload to backblaze")
rootCmd.PersistentFlags().String("dir", "", "absolute path of the directory you want to upload to backblaze")
rootCmd.PersistentFlags().String("bucket", "", "backblaze bucket name")
}
func main() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View File

@ -50,7 +50,7 @@ func (b *BackBalze) WithFile(filePath string) *BackBalze {
b.filePath = filePath b.filePath = filePath
return b return b
} }
func (b *BackBalze) Sync() error { func (b *BackBalze) Sync(ctx context.Context) error {
if b.bucketName == "" && (b.filePath == "" || b.dir == "") { if b.bucketName == "" && (b.filePath == "" || b.dir == "") {
return fmt.Errorf("bucket name is %v | filePath is %v | dir is %v", b.bucketName, b.filePath, b.dir) return fmt.Errorf("bucket name is %v | filePath is %v | dir is %v", b.bucketName, b.filePath, b.dir)
} }
@ -59,7 +59,6 @@ func (b *BackBalze) Sync() error {
return errors.New("you must select just 1 option, dir or file") return errors.New("you must select just 1 option, dir or file")
} }
ctx := context.Background()
b2Client, err := b2.NewClient(ctx, b.bbID, b.bbKey) b2Client, err := b2.NewClient(ctx, b.bbID, b.bbKey)
if err != nil { if err != nil {
return fmt.Errorf("b2.NewClient %w", err) return fmt.Errorf("b2.NewClient %w", err)
@ -302,3 +301,63 @@ func bucketFiles(ctx context.Context, bucket *b2.Bucket) ([]string, error) {
} }
return files, nil return files, nil
} }
type duplicate struct {
bucket string
file string
count int
}
func (b *BackBalze) ListDuplicateVersions(ctx context.Context) error {
b2Client, err := b2.NewClient(ctx, b.bbID, b.bbKey)
if err != nil {
return fmt.Errorf("b2.NewClient %w", err)
}
log.Println("b2Client ok")
buckets, err := b2Client.ListBuckets(ctx)
if err != nil {
return fmt.Errorf("b2Client.Bucket %w", err)
}
dups := make([]duplicate, 0)
log.Println("len(buckets)", len(buckets))
for _, bc := range buckets {
files := make(map[string]int, 0)
bucketIter := bc.List(ctx, b2.ListHidden())
if bucketIter == nil {
return fmt.Errorf("bucket list cannot be nil")
}
for {
if !bucketIter.Next() {
if bucketIter.Err() != nil {
return fmt.Errorf("bucketIter err %w", bucketIter.Err())
}
break
}
if bucketIter.Object() == nil {
log.Println("bucketIter Object is nil")
continue
}
files[bucketIter.Object().Name()]++
}
// Search duplicates
for file, count := range files {
if count > 1 {
dups = append(dups, duplicate{
bucket: bc.Name(),
file: file,
count: count,
})
}
}
}
if len(dups) > 0 {
return fmt.Errorf("found duplicates: %+v", dups)
}
return nil
}

29
main.go Normal file
View File

@ -0,0 +1,29 @@
package main
import (
"fmt"
"os"
"gitea.urkob.com/urko/backblaze-backup/cmd"
"github.com/spf13/cobra"
)
var rootCmd = &cobra.Command{
Use: "backblaze-backup",
Short: "Backblaze backup tool",
Long: `A tool to backup files and directories to Backblaze.`,
}
func init() {
rootCmd.AddCommand(cmd.Sync, cmd.Versions)
cmd.Sync.PersistentFlags().String("file", "", "absolute path of the file you want to upload to backblaze")
cmd.Sync.PersistentFlags().String("dir", "", "absolute path of the directory you want to upload to backblaze")
cmd.Sync.PersistentFlags().String("bucket", "", "backblaze bucket name")
}
func main() {
if err := cmd.Sync.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}