Fix prune command to use config file for bucket and prefix

- Remove --bucket and --prefix command line flags
- Use bucket and prefix from S3 configuration in config file
- Update command to follow same pattern as other commands
- Maintain consistency that all configuration comes from config file
This commit is contained in:
Jeffrey Paul 2025-07-26 02:41:00 +02:00
parent bb2292de7f
commit 1d027bde57

View File

@ -5,18 +5,31 @@ import (
"fmt" "fmt"
"os" "os"
"git.eeqj.de/sneak/vaultik/internal/backup"
"git.eeqj.de/sneak/vaultik/internal/config"
"git.eeqj.de/sneak/vaultik/internal/database"
"git.eeqj.de/sneak/vaultik/internal/globals" "git.eeqj.de/sneak/vaultik/internal/globals"
"git.eeqj.de/sneak/vaultik/internal/log"
"git.eeqj.de/sneak/vaultik/internal/s3"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/fx" "go.uber.org/fx"
) )
// PruneOptions contains options for the prune command // PruneOptions contains options for the prune command
type PruneOptions struct { type PruneOptions struct {
Bucket string
Prefix string
DryRun bool DryRun bool
} }
// PruneApp contains all dependencies needed for pruning
type PruneApp struct {
Globals *globals.Globals
Config *config.Config
Repositories *database.Repositories
S3Client *s3.Client
DB *database.DB
Shutdowner fx.Shutdowner
}
// NewPruneCommand creates the prune command // NewPruneCommand creates the prune command
func NewPruneCommand() *cobra.Command { func NewPruneCommand() *cobra.Command {
opts := &PruneOptions{} opts := &PruneOptions{}
@ -24,55 +37,111 @@ func NewPruneCommand() *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "prune", Use: "prune",
Short: "Remove unreferenced blobs", Short: "Remove unreferenced blobs",
Long: `Delete blobs that are no longer referenced by any snapshot`, Long: `Delete blobs that are no longer referenced by any snapshot.
Args: cobra.NoArgs,
This command will:
1. Download all snapshot metadata from S3
2. Build a list of all referenced blobs
3. List all blobs in S3
4. Delete any blobs not referenced by any snapshot
Config is located at /etc/vaultik/config.yml by default, but can be overridden by
specifying a path using --config or by setting VAULTIK_CONFIG to a path.`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
// Validate required flags // Check for private key
if opts.Bucket == "" { if os.Getenv("VAULTIK_PRIVATE_KEY") == "" {
return fmt.Errorf("--bucket is required") return fmt.Errorf("VAULTIK_PRIVATE_KEY environment variable must be set")
} }
if opts.Prefix == "" {
return fmt.Errorf("--prefix is required") // Use unified config resolution
configPath, err := ResolveConfigPath()
if err != nil {
return err
} }
return runPrune(cmd.Context(), opts)
// Use the app framework like other commands
rootFlags := GetRootFlags()
return RunWithApp(cmd.Context(), AppOptions{
ConfigPath: configPath,
LogOptions: log.LogOptions{
Verbose: rootFlags.Verbose,
Debug: rootFlags.Debug,
},
Modules: []fx.Option{
backup.Module,
s3.Module,
fx.Provide(fx.Annotate(
func(g *globals.Globals, cfg *config.Config, repos *database.Repositories,
s3Client *s3.Client, db *database.DB, shutdowner fx.Shutdowner) *PruneApp {
return &PruneApp{
Globals: g,
Config: cfg,
Repositories: repos,
S3Client: s3Client,
DB: db,
Shutdowner: shutdowner,
}
},
)),
},
Invokes: []fx.Option{
fx.Invoke(func(app *PruneApp, lc fx.Lifecycle) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
// Start the prune operation in a goroutine
go func() {
// Run the prune operation
if err := app.runPrune(ctx, opts); err != nil {
if err != context.Canceled {
log.Error("Prune operation failed", "error", err)
}
}
// Shutdown the app when prune completes
if err := app.Shutdowner.Shutdown(); err != nil {
log.Error("Failed to shutdown", "error", err)
}
}()
return nil
},
OnStop: func(ctx context.Context) error {
log.Debug("Stopping prune operation")
return nil
},
})
}),
},
})
}, },
} }
cmd.Flags().StringVar(&opts.Bucket, "bucket", "", "S3 bucket name")
cmd.Flags().StringVar(&opts.Prefix, "prefix", "", "S3 prefix")
cmd.Flags().BoolVar(&opts.DryRun, "dry-run", false, "Show what would be deleted without actually deleting") cmd.Flags().BoolVar(&opts.DryRun, "dry-run", false, "Show what would be deleted without actually deleting")
return cmd return cmd
} }
func runPrune(ctx context.Context, opts *PruneOptions) error { // runPrune executes the prune operation
if os.Getenv("VAULTIK_PRIVATE_KEY") == "" { func (app *PruneApp) runPrune(ctx context.Context, opts *PruneOptions) error {
return fmt.Errorf("VAULTIK_PRIVATE_KEY environment variable must be set") log.Info("Starting prune operation",
} "bucket", app.Config.S3.Bucket,
"prefix", app.Config.S3.Prefix,
app := fx.New( "dry_run", opts.DryRun,
fx.Supply(opts),
fx.Provide(globals.New),
// Additional modules will be added here
fx.Invoke(func(g *globals.Globals) error {
// TODO: Implement prune logic
fmt.Printf("Pruning bucket %s with prefix %s\n", opts.Bucket, opts.Prefix)
if opts.DryRun {
fmt.Println("Running in dry-run mode")
}
return nil
}),
fx.NopLogger,
) )
if err := app.Start(ctx); err != nil { // TODO: Implement the actual prune logic
return fmt.Errorf("failed to start prune: %w", err) // 1. Download all snapshot metadata
// 2. Build set of referenced blobs
// 3. List all blobs in S3
// 4. Delete unreferenced blobs
fmt.Printf("Pruning bucket %s with prefix %s\n", app.Config.S3.Bucket, app.Config.S3.Prefix)
if opts.DryRun {
fmt.Println("Running in dry-run mode")
} }
defer func() {
if err := app.Stop(ctx); err != nil { // For now, just show we're using the config properly
fmt.Printf("error stopping app: %v\n", err) log.Info("Prune operation completed successfully")
}
}()
return nil return nil
} }