diff --git a/CHANGELOG.md b/CHANGELOG.md index 76c04c59e7..2403376078 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Changelog for NeoFS Node - `node` config option `storage.ignore_uninited_shards` (#2953) - For `neofs-cli container create`, add `--global-name` flag, that sets name attribute as the value of `__NEOFS__NAME` attribute, which is used for container domain name in NNS contracts (#2954) +- `neofs-cli control object revive` command (#2968) ### Fixed - Do not search for tombstones when handling their expiration, use local indexes instead (#2929) diff --git a/cmd/neofs-cli/modules/control/object.go b/cmd/neofs-cli/modules/control/object.go index 831a477a7f..dcb05dc934 100644 --- a/cmd/neofs-cli/modules/control/object.go +++ b/cmd/neofs-cli/modules/control/object.go @@ -4,6 +4,8 @@ import ( "github.com/spf13/cobra" ) +const objectFlag = "object" + var objectCmd = &cobra.Command{ Use: "object", Short: "Direct object operations with storage engine", @@ -12,7 +14,9 @@ var objectCmd = &cobra.Command{ func initControlObjectsCmd() { objectCmd.AddCommand(listObjectsCmd) objectCmd.AddCommand(objectStatusCmd) + objectCmd.AddCommand(reviveObjectCmd) + initControlObjectReviveCmd() initControlObjectsListCmd() initObjectStatusFlags() } diff --git a/cmd/neofs-cli/modules/control/object_revive.go b/cmd/neofs-cli/modules/control/object_revive.go new file mode 100644 index 0000000000..9fb2c6394e --- /dev/null +++ b/cmd/neofs-cli/modules/control/object_revive.go @@ -0,0 +1,89 @@ +package control + +import ( + "fmt" + + rawclient "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" + "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/commonflags" + "github.com/nspcc-dev/neofs-node/cmd/neofs-cli/internal/key" + "github.com/nspcc-dev/neofs-node/pkg/services/control" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + "github.com/spf13/cobra" +) + +var reviveObjectCmd = &cobra.Command{ + Use: "revive", + Short: "Forcefully revive object", + Long: "Purge removal marks from metabases", + Args: cobra.NoArgs, + RunE: reviveObject, +} + +func initControlObjectReviveCmd() { + initControlFlags(reviveObjectCmd) + + flags := reviveObjectCmd.Flags() + flags.String(objectFlag, "", "Object address") +} + +func reviveObject(cmd *cobra.Command, _ []string) error { + ctx, cancel := commonflags.GetCommandContext(cmd) + defer cancel() + + pk, err := key.Get(cmd) + if err != nil { + return err + } + addressRaw, err := cmd.Flags().GetString(objectFlag) + if err != nil { + return fmt.Errorf("reading %s flag: %w", objectFlag, err) + } + + var sdkAddr oid.Address + err = sdkAddr.DecodeString(addressRaw) + if err != nil { + return fmt.Errorf("validating address (%s): %w", addressRaw, err) + } + + var resp *control.ReviveObjectResponse + req := &control.ReviveObjectRequest{ + Body: &control.ReviveObjectRequest_Body{ + ObjectAddress: addressRaw, + }, + } + err = signRequest(pk, req) + if err != nil { + return err + } + + cli, err := getClient(ctx) + if err != nil { + return err + } + + err = cli.ExecRaw(func(client *rawclient.Client) error { + resp, err = control.ReviveObject(client, req) + return err + }) + if err != nil { + return fmt.Errorf("rpc error: %w", err) + } + + err = verifyResponse(resp.GetSignature(), resp.GetBody()) + if err != nil { + return err + } + + shards := resp.GetBody().GetShards() + if len(shards) == 0 { + cmd.Println("") + return nil + } + + for _, shard := range shards { + cmd.Printf("Shard ID: %s\n", shard.ShardId) + cmd.Printf("Revival status: %s\n", shard.Status) + } + + return nil +} diff --git a/cmd/neofs-cli/modules/control/object_status.go b/cmd/neofs-cli/modules/control/object_status.go index 5eed21e866..6d976c9d94 100644 --- a/cmd/neofs-cli/modules/control/object_status.go +++ b/cmd/neofs-cli/modules/control/object_status.go @@ -11,8 +11,6 @@ import ( "github.com/spf13/cobra" ) -const objectFlag = "object" - var objectStatusCmd = &cobra.Command{ Use: "status", Short: "Check current object status",