[#9999] adm: Add dry-run flag to zombie scan command
Some checks failed
DCO action / DCO (pull_request) Successful in 45s
Vulncheck / Vulncheck (pull_request) Failing after 1m14s
Build / Build Components (pull_request) Successful in 1m21s
Tests and linters / Staticcheck (pull_request) Successful in 2m50s
Tests and linters / Lint (pull_request) Successful in 2m55s
Tests and linters / Tests (pull_request) Successful in 3m0s
Tests and linters / Tests with -race (pull_request) Successful in 3m25s
Tests and linters / gopls check (pull_request) Successful in 3m56s

This allows to just print object addresses that should be moved to quarantine.

Change-Id: I551979d8bffaf45fe21d92f6edadfaadcb5d6e25
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
Dmitrii Stepanov 2025-03-31 12:25:57 +03:00
parent 6278986d62
commit 87c9e53c29
Signed by: dstepanov-yadro
GPG key ID: 237AF1A763293BC0
2 changed files with 39 additions and 23 deletions

View file

@ -18,6 +18,8 @@ const (
walletFlagUsage = "Path to the wallet or binary key" walletFlagUsage = "Path to the wallet or binary key"
addressFlag = "address" addressFlag = "address"
addressFlagUsage = "Address of wallet account" addressFlagUsage = "Address of wallet account"
dryRunFlag = "dry-run"
dryRunFlagUsage = "Print addresses of objects - candidates for quarantine without actually moving"
) )
var ( var (
@ -36,6 +38,7 @@ var (
_ = viper.BindPFlag(walletFlag, cmd.Flags().Lookup(walletFlag)) _ = viper.BindPFlag(walletFlag, cmd.Flags().Lookup(walletFlag))
_ = viper.BindPFlag(addressFlag, cmd.Flags().Lookup(addressFlag)) _ = viper.BindPFlag(addressFlag, cmd.Flags().Lookup(addressFlag))
_ = viper.BindPFlag(flagBatchSize, cmd.Flags().Lookup(flagBatchSize)) _ = viper.BindPFlag(flagBatchSize, cmd.Flags().Lookup(flagBatchSize))
_ = viper.BindPFlag(dryRunFlag, cmd.Flags().Lookup(dryRunFlag))
}, },
Run: scan, Run: scan,
} }
@ -92,6 +95,7 @@ func initScanCmd() {
scanCmd.Flags().Uint32(flagBatchSize, 1000, flagBatchSizeUsage) scanCmd.Flags().Uint32(flagBatchSize, 1000, flagBatchSizeUsage)
scanCmd.Flags().StringP(walletFlag, walletFlagShorthand, "", walletFlagUsage) scanCmd.Flags().StringP(walletFlag, walletFlagShorthand, "", walletFlagUsage)
scanCmd.Flags().String(addressFlag, "", addressFlagUsage) scanCmd.Flags().String(addressFlag, "", addressFlagUsage)
scanCmd.Flags().Bool(dryRunFlag, false, dryRunFlagUsage)
} }
func initListCmd() { func initListCmd() {

View file

@ -34,6 +34,7 @@ func scan(cmd *cobra.Command, _ []string) {
if batchSize == 0 { if batchSize == 0 {
commonCmd.ExitOnErr(cmd, "invalid batch size: %w", errors.New("batch size must be positive value")) commonCmd.ExitOnErr(cmd, "invalid batch size: %w", errors.New("batch size must be positive value"))
} }
dryRun, _ := cmd.Flags().GetBool(dryRunFlag)
storageEngine := newEngine(cmd, appCfg) storageEngine := newEngine(cmd, appCfg)
morphClient := createMorphClient(cmd, appCfg) morphClient := createMorphClient(cmd, appCfg)
@ -76,7 +77,7 @@ func scan(cmd *cobra.Command, _ []string) {
}() }()
go func() { go func() {
defer wg.Done() 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) close(stopCh)
}() }()
wg.Wait() wg.Wait()
@ -156,8 +157,8 @@ func checkAddr(ctx context.Context, cnrCli *cntClient.Client, nmCli *netmap.Clie
return statusQuarantine, nil return statusQuarantine, nil
} }
func scanStorageEngine(ctx context.Context, batchSize uint32, storageEngine *engine.StorageEngine, ps *processStatus, 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, appCfg *config.Config, cnrCli *cntClient.Client, nmCli *netmap.Client, q *quarantine, pk *ecdsa.PrivateKey, dryRun bool,
) error { ) error {
cc := cache.NewSDKClientCache(cache.ClientCacheOpts{ cc := cache.NewSDKClientCache(cache.ClientCacheOpts{
DialTimeout: apiclientconfig.DialTimeout(appCfg), DialTimeout: apiclientconfig.DialTimeout(appCfg),
@ -166,6 +167,7 @@ func scanStorageEngine(ctx context.Context, batchSize uint32, storageEngine *eng
Key: pk, Key: pk,
AllowExternal: apiclientconfig.AllowExternal(appCfg), AllowExternal: apiclientconfig.AllowExternal(appCfg),
}) })
ctx := cmd.Context()
var cursor *engine.Cursor var cursor *engine.Cursor
for { for {
@ -197,30 +199,17 @@ func scanStorageEngine(ctx context.Context, batchSize uint32, storageEngine *eng
eg.Go(func() error { eg.Go(func() error {
result, err := checkAddr(egCtx, cnrCli, nmCli, cc, addr.Address) result, err := checkAddr(egCtx, cnrCli, nmCli, cc, addr.Address)
if err != nil { 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) ps.add(result)
if dryRun && result == statusQuarantine {
cmd.Println(addr)
return nil
}
if result == statusQuarantine { if result == statusQuarantine {
var getPrm engine.GetPrm return moveToQuarantine(egCtx, storageEngine, q, addr.Address)
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 nil 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 { type processStatus struct {
guard sync.RWMutex guard sync.RWMutex
statusCount map[status]uint64 statusCount map[status]uint64