diff --git a/CHANGELOG.md b/CHANGELOG.md index 4da70062..ddb262bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Changelog for NeoFS Node ## [Unreleased] ### Added +- `morph list-containers` in `neofs-adm` (#1689) + ### Changed ### Fixed - Open FSTree in sync mode by default (#1992) diff --git a/cmd/neofs-adm/README.md b/cmd/neofs-adm/README.md index 6d28fe19..a80d114e 100644 --- a/cmd/neofs-adm/README.md +++ b/cmd/neofs-adm/README.md @@ -90,6 +90,8 @@ info. These commands **do not migrate actual objects**. - `restore-containers` restores previously saved containers by their repeated registration in the container contract. +- `list-containers` output all containers ids. + #### Network info - `dump-config` prints NeoFS network configuration. diff --git a/cmd/neofs-adm/internal/modules/morph/container.go b/cmd/neofs-adm/internal/modules/morph/container.go index 7c6ce122..811d71be 100644 --- a/cmd/neofs-adm/internal/modules/morph/container.go +++ b/cmd/neofs-adm/internal/modules/morph/container.go @@ -22,6 +22,37 @@ import ( var errInvalidContainerResponse = errors.New("invalid response from container contract") +func getContainerContractHash(cmd *cobra.Command, inv *invoker.Invoker, c Client) (util.Uint160, error) { + s, err := cmd.Flags().GetString(containerContractFlag) + var ch util.Uint160 + if err == nil { + ch, err = util.Uint160DecodeStringLE(s) + } + if err != nil { + nnsCs, err := c.GetContractStateByID(1) + if err != nil { + return util.Uint160{}, fmt.Errorf("can't get NNS contract state: %w", err) + } + ch, err = nnsResolveHash(inv, nnsCs.Hash, containerContract+".neofs") + if err != nil { + return util.Uint160{}, err + } + } + return ch, nil +} + +func getContainersList(inv *invoker.Invoker, ch util.Uint160) ([][]byte, error) { + res, err := inv.Call(ch, "list", "") + if err != nil { + return nil, fmt.Errorf("%w: %v", errInvalidContainerResponse, err) + } + itm, err := unwrap.Item(res, err) + if _, ok := itm.(stackitem.Null); !ok { + return unwrap.ArrayOfBytes(res, err) + } + return nil, nil +} + func dumpContainers(cmd *cobra.Command, _ []string) error { filename, err := cmd.Flags().GetString(containerDumpFlag) if err != nil { @@ -35,24 +66,12 @@ func dumpContainers(cmd *cobra.Command, _ []string) error { inv := invoker.New(c, nil) - nnsCs, err := c.GetContractStateByID(1) + ch, err := getContainerContractHash(cmd, inv, c) if err != nil { - return fmt.Errorf("can't get NNS contract state: %w", err) + return fmt.Errorf("unable to get contaract hash: %w", err) } - var ch util.Uint160 - s, err := cmd.Flags().GetString(containerContractFlag) - if err == nil { - ch, err = util.Uint160DecodeStringLE(s) - } - if err != nil { - ch, err = nnsResolveHash(inv, nnsCs.Hash, containerContract+".neofs") - if err != nil { - return err - } - } - - cids, err := unwrap.ArrayOfBytes(inv.Call(ch, "list", "")) + cids, err := getContainersList(inv, ch) if err != nil { return fmt.Errorf("%w: %v", errInvalidContainerResponse, err) } @@ -104,6 +123,35 @@ func dumpContainers(cmd *cobra.Command, _ []string) error { return os.WriteFile(filename, out, 0o660) } +func listContainers(cmd *cobra.Command, _ []string) error { + c, err := getN3Client(viper.GetViper()) + if err != nil { + return fmt.Errorf("can't create N3 client: %w", err) + } + + inv := invoker.New(c, nil) + + ch, err := getContainerContractHash(cmd, inv, c) + if err != nil { + return fmt.Errorf("unable to get contaract hash: %w", err) + } + + cids, err := getContainersList(inv, ch) + if err != nil { + return fmt.Errorf("%w: %v", errInvalidContainerResponse, err) + } + + for _, id := range cids { + var idCnr cid.ID + err = idCnr.Decode(id) + if err != nil { + return fmt.Errorf("unable to decode container id: %w", err) + } + cmd.Println(idCnr) + } + return nil +} + func restoreContainers(cmd *cobra.Command, _ []string) error { filename, err := cmd.Flags().GetString(containerDumpFlag) if err != nil { diff --git a/cmd/neofs-adm/internal/modules/morph/root.go b/cmd/neofs-adm/internal/modules/morph/root.go index 699a6fdf..a33a99ef 100644 --- a/cmd/neofs-adm/internal/modules/morph/root.go +++ b/cmd/neofs-adm/internal/modules/morph/root.go @@ -209,6 +209,15 @@ var ( RunE: restoreContainers, } + listContainersCmd = &cobra.Command{ + Use: "list-containers", + Short: "List NeoFS containers", + PreRun: func(cmd *cobra.Command, _ []string) { + _ = viper.BindPFlag(endpointFlag, cmd.Flags().Lookup(endpointFlag)) + }, + RunE: listContainers, + } + depositNotaryCmd = &cobra.Command{ Use: "deposit-notary", Short: "Deposit GAS for notary service", @@ -294,6 +303,10 @@ func init() { restoreContainersCmd.Flags().String(containerDumpFlag, "", "File to restore containers from") restoreContainersCmd.Flags().StringSlice(containerIDsFlag, nil, "Containers to restore") + RootCmd.AddCommand(listContainersCmd) + listContainersCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint") + listContainersCmd.Flags().String(containerContractFlag, "", "Container contract hash (for networks without NNS)") + RootCmd.AddCommand(refillGasCmd) refillGasCmd.Flags().String(alphabetWalletsFlag, "", "Path to alphabet wallets dir") refillGasCmd.Flags().StringP(endpointFlag, "r", "", "N3 RPC node endpoint")