diff --git a/cli/app.go b/cli/app.go index 73abe71d3..db554aa70 100644 --- a/cli/app.go +++ b/cli/app.go @@ -184,6 +184,20 @@ func maybeRunMaintenance(ctx context.Context, rep repo.Repository) error { return err } +func advancedCommand() { + if os.Getenv("KOPIA_ADVANCED_COMMANDS") != "enabled" { + //nolint:errcheck + errorColor.Printf(` +This command could be dangerous or lead to repository corruption when used improperly. + +Running this command is not needed for using Kopia. Instead, most users should rely on periodic repository maintenance. See https://kopia.io/docs/maintenance/ for more information. +To run this command despite the warning, set KOPIA_ADVANCED_COMMANDS=enabled + +`) + os.Exit(1) + } +} + // App returns an instance of command-line application object. func App() *kingpin.Application { return app diff --git a/cli/command_blob_delete.go b/cli/command_blob_delete.go index 551a2ce63..9f2c06534 100644 --- a/cli/command_blob_delete.go +++ b/cli/command_blob_delete.go @@ -15,6 +15,8 @@ ) func runDeleteBlobs(ctx context.Context, rep *repo.DirectRepository) error { + advancedCommand() + for _, b := range *blobDeleteBlobIDs { err := rep.Blobs.DeleteBlob(ctx, blob.ID(b)) if err != nil { diff --git a/cli/command_blob_gc.go b/cli/command_blob_gc.go index b14bcdfec..68af14dbd 100644 --- a/cli/command_blob_gc.go +++ b/cli/command_blob_gc.go @@ -17,6 +17,8 @@ ) func runBlobGarbageCollectCommand(ctx context.Context, rep *repo.DirectRepository) error { + advancedCommand() + opts := maintenance.DeleteUnreferencedBlobsOptions{ DryRun: *blobGarbageCollectCommandDelete != "yes", MinAge: *blobGarbageCollectMinAge, diff --git a/cli/command_content_rewrite.go b/cli/command_content_rewrite.go index e31e19a40..5d695c5ef 100644 --- a/cli/command_content_rewrite.go +++ b/cli/command_content_rewrite.go @@ -22,6 +22,8 @@ ) func runContentRewriteCommand(ctx context.Context, rep *repo.DirectRepository) error { + advancedCommand() + return maintenance.RewriteContents(ctx, rep, &maintenance.RewriteContentsOptions{ ContentIDRange: contentIDRange(), ContentIDs: toContentIDs(*contentRewriteIDs), diff --git a/cli/command_content_rm.go b/cli/command_content_rm.go index e7753c5bd..9673b5734 100644 --- a/cli/command_content_rm.go +++ b/cli/command_content_rm.go @@ -13,6 +13,8 @@ ) func runContentRemoveCommand(ctx context.Context, rep *repo.DirectRepository) error { + advancedCommand() + for _, contentID := range toContentIDs(*contentRemoveIDs) { if err := rep.Content.DeleteContent(ctx, contentID); err != nil { return err diff --git a/cli/command_index_optimize.go b/cli/command_index_optimize.go index 95971c2cb..43fe11faa 100644 --- a/cli/command_index_optimize.go +++ b/cli/command_index_optimize.go @@ -17,6 +17,8 @@ ) func runOptimizeCommand(ctx context.Context, rep *repo.DirectRepository) error { + advancedCommand() + opt := content.CompactOptions{ MaxSmallBlobs: *optimizeMaxSmallBlobs, AllIndexes: *optimizeAllIndexes, diff --git a/cli/command_index_recover.go b/cli/command_index_recover.go index 01f92a64c..ef0c5b5ec 100644 --- a/cli/command_index_recover.go +++ b/cli/command_index_recover.go @@ -17,6 +17,8 @@ ) func runRecoverBlockIndexesAction(ctx context.Context, rep *repo.DirectRepository) error { + advancedCommand() + var totalCount int defer func() { diff --git a/cli/command_manifest_rm.go b/cli/command_manifest_rm.go index 701ced432..a8b7a1120 100644 --- a/cli/command_manifest_rm.go +++ b/cli/command_manifest_rm.go @@ -12,6 +12,8 @@ ) func runManifestRemoveCommand(ctx context.Context, rep repo.Repository) error { + advancedCommand() + for _, it := range toManifestIDs(*manifestRemoveItems) { if err := rep.DeleteManifest(ctx, it); err != nil { return err diff --git a/cli/command_snapshot_gc.go b/cli/command_snapshot_gc.go index d775a3ecc..68867d02d 100644 --- a/cli/command_snapshot_gc.go +++ b/cli/command_snapshot_gc.go @@ -10,7 +10,7 @@ ) var ( - snapshotGCCommand = snapshotCommands.Command("gc", "Remove contents not used by any snapshot") + snapshotGCCommand = snapshotCommands.Command("gc", "Mark contents as deleted which are not used by any snapshot").Hidden() snapshotGCMinContentAge = snapshotGCCommand.Flag("min-age", "Minimum content age to allow deletion").Default("24h").Duration() snapshotGCDelete = snapshotGCCommand.Flag("delete", "Delete unreferenced contents").Bool() ) diff --git a/tests/testenv/cli_test_env.go b/tests/testenv/cli_test_env.go index 60e9ffae1..21ae0a61a 100644 --- a/tests/testenv/cli_test_env.go +++ b/tests/testenv/cli_test_env.go @@ -92,12 +92,15 @@ func NewCLITest(t *testing.T) *CLITest { } return &CLITest{ - startTime: clock.Now(), - RepoDir: RepoDir, - ConfigDir: ConfigDir, - Exe: filepath.FromSlash(exe), - fixedArgs: fixedArgs, - Environment: []string{"KOPIA_PASSWORD=" + repoPassword}, + startTime: clock.Now(), + RepoDir: RepoDir, + ConfigDir: ConfigDir, + Exe: filepath.FromSlash(exe), + fixedArgs: fixedArgs, + Environment: []string{ + "KOPIA_PASSWORD=" + repoPassword, + "KOPIA_ADVANCED_COMMANDS=enabled", + }, } }