From 87c9e53c29a1921f7e9fc16f2377cb7c88f2e045 Mon Sep 17 00:00:00 2001 From: Dmitrii Stepanov Date: Mon, 31 Mar 2025 12:25:57 +0300 Subject: [PATCH] [#9999] adm: Add `dry-run` flag to `zombie scan` command This allows to just print object addresses that should be moved to quarantine. Change-Id: I551979d8bffaf45fe21d92f6edadfaadcb5d6e25 Signed-off-by: Dmitrii Stepanov --- .../modules/maintenance/zombie/root.go | 4 ++ .../modules/maintenance/zombie/scan.go | 58 +++++++++++-------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/cmd/frostfs-adm/internal/modules/maintenance/zombie/root.go b/cmd/frostfs-adm/internal/modules/maintenance/zombie/root.go index 6990a0030..e8bc529d8 100644 --- a/cmd/frostfs-adm/internal/modules/maintenance/zombie/root.go +++ b/cmd/frostfs-adm/internal/modules/maintenance/zombie/root.go @@ -18,6 +18,8 @@ const ( walletFlagUsage = "Path to the wallet or binary key" addressFlag = "address" addressFlagUsage = "Address of wallet account" + dryRunFlag = "dry-run" + dryRunFlagUsage = "Print addresses of objects - candidates for quarantine without actually moving" ) var ( @@ -36,6 +38,7 @@ var ( _ = viper.BindPFlag(walletFlag, cmd.Flags().Lookup(walletFlag)) _ = viper.BindPFlag(addressFlag, cmd.Flags().Lookup(addressFlag)) _ = viper.BindPFlag(flagBatchSize, cmd.Flags().Lookup(flagBatchSize)) + _ = viper.BindPFlag(dryRunFlag, cmd.Flags().Lookup(dryRunFlag)) }, Run: scan, } @@ -92,6 +95,7 @@ func initScanCmd() { scanCmd.Flags().Uint32(flagBatchSize, 1000, flagBatchSizeUsage) scanCmd.Flags().StringP(walletFlag, walletFlagShorthand, "", walletFlagUsage) scanCmd.Flags().String(addressFlag, "", addressFlagUsage) + scanCmd.Flags().Bool(dryRunFlag, false, dryRunFlagUsage) } func initListCmd() { diff --git a/cmd/frostfs-adm/internal/modules/maintenance/zombie/scan.go b/cmd/frostfs-adm/internal/modules/maintenance/zombie/scan.go index 18a29ca8d..4a349d7d1 100644 --- a/cmd/frostfs-adm/internal/modules/maintenance/zombie/scan.go +++ b/cmd/frostfs-adm/internal/modules/maintenance/zombie/scan.go @@ -34,6 +34,7 @@ func scan(cmd *cobra.Command, _ []string) { if batchSize == 0 { commonCmd.ExitOnErr(cmd, "invalid batch size: %w", errors.New("batch size must be positive value")) } + dryRun, _ := cmd.Flags().GetBool(dryRunFlag) storageEngine := newEngine(cmd, appCfg) morphClient := createMorphClient(cmd, appCfg) @@ -76,7 +77,7 @@ func scan(cmd *cobra.Command, _ []string) { }() go func() { defer wg.Done() - err = scanStorageEngine(cmd.Context(), batchSize, storageEngine, ps, appCfg, cnrCli, nmCli, q, pk) + err = scanStorageEngine(cmd, batchSize, storageEngine, ps, appCfg, cnrCli, nmCli, q, pk, dryRun) close(stopCh) }() wg.Wait() @@ -156,8 +157,8 @@ func checkAddr(ctx context.Context, cnrCli *cntClient.Client, nmCli *netmap.Clie return statusQuarantine, nil } -func scanStorageEngine(ctx context.Context, batchSize uint32, storageEngine *engine.StorageEngine, ps *processStatus, - appCfg *config.Config, cnrCli *cntClient.Client, nmCli *netmap.Client, q *quarantine, pk *ecdsa.PrivateKey, +func scanStorageEngine(cmd *cobra.Command, batchSize uint32, storageEngine *engine.StorageEngine, ps *processStatus, + appCfg *config.Config, cnrCli *cntClient.Client, nmCli *netmap.Client, q *quarantine, pk *ecdsa.PrivateKey, dryRun bool, ) error { cc := cache.NewSDKClientCache(cache.ClientCacheOpts{ DialTimeout: apiclientconfig.DialTimeout(appCfg), @@ -166,6 +167,7 @@ func scanStorageEngine(ctx context.Context, batchSize uint32, storageEngine *eng Key: pk, AllowExternal: apiclientconfig.AllowExternal(appCfg), }) + ctx := cmd.Context() var cursor *engine.Cursor for { @@ -197,30 +199,17 @@ func scanStorageEngine(ctx context.Context, batchSize uint32, storageEngine *eng eg.Go(func() error { result, err := checkAddr(egCtx, cnrCli, nmCli, cc, addr.Address) if err != nil { - return fmt.Errorf("check object %s status: %w", addr, err) + return fmt.Errorf("check object %s status: %w", addr.Address, err) } ps.add(result) + if dryRun && result == statusQuarantine { + cmd.Println(addr) + return nil + } + if result == statusQuarantine { - var getPrm engine.GetPrm - getPrm.WithAddress(addr.Address) - res, err := storageEngine.Get(egCtx, getPrm) - if err != nil { - return fmt.Errorf("get object %s from storage engine: %w", addr, err) - } - - if err := q.Put(egCtx, res.Object()); err != nil { - return fmt.Errorf("put object %s to quarantine: %w", addr, err) - } - - var delPrm engine.DeletePrm - delPrm.WithForceRemoval() - delPrm.WithAddress(addr.Address) - - _, err = storageEngine.Delete(egCtx, delPrm) - if err != nil { - return fmt.Errorf("delete object %s from storage engine: %w", addr, err) - } + return moveToQuarantine(egCtx, storageEngine, q, addr.Address) } return nil }) @@ -231,6 +220,29 @@ func scanStorageEngine(ctx context.Context, batchSize uint32, storageEngine *eng } } +func moveToQuarantine(ctx context.Context, storageEngine *engine.StorageEngine, q *quarantine, addr oid.Address) error { + var getPrm engine.GetPrm + getPrm.WithAddress(addr) + res, err := storageEngine.Get(ctx, getPrm) + if err != nil { + return fmt.Errorf("get object %s from storage engine: %w", addr, err) + } + + if err := q.Put(ctx, res.Object()); err != nil { + return fmt.Errorf("put object %s to quarantine: %w", addr, err) + } + + var delPrm engine.DeletePrm + delPrm.WithForceRemoval() + delPrm.WithAddress(addr) + + _, err = storageEngine.Delete(ctx, delPrm) + if err != nil { + return fmt.Errorf("delete object %s from storage engine: %w", addr, err) + } + return nil +} + type processStatus struct { guard sync.RWMutex statusCount map[status]uint64