diff --git a/cmd/frostfs-adm/internal/commonflags/auto_complete.go b/cmd/frostfs-adm/internal/commonflags/auto_complete.go new file mode 100644 index 000000000..dcbdffdee --- /dev/null +++ b/cmd/frostfs-adm/internal/commonflags/auto_complete.go @@ -0,0 +1,26 @@ +package commonflags + +import ( + "slices" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func AutoCompleteFlags(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + var availableFlags []string + cmd.Flags().VisitAll(func(f *pflag.Flag) { + availableFlags = append(availableFlags, f.Name) + }) + + var usedFlags []string + cmd.Flags().Visit(func(f *pflag.Flag) { + usedFlags = append(usedFlags, f.Name) + }) + + unusedFlags := slices.DeleteFunc(availableFlags, func(name string) bool { + return slices.Contains(usedFlags, name) + }) + + return unusedFlags, cobra.ShellCompDirectiveDefault +} diff --git a/cmd/frostfs-adm/internal/commonflags/flags.go b/cmd/frostfs-adm/internal/commonflags/flags.go index 87692d013..48b6364cd 100644 --- a/cmd/frostfs-adm/internal/commonflags/flags.go +++ b/cmd/frostfs-adm/internal/commonflags/flags.go @@ -16,6 +16,13 @@ const ( EndpointFlagDesc = "N3 RPC node endpoint" EndpointFlagShort = "r" + WalletPath = "wallet" + WalletPathShorthand = "w" + WalletPathUsage = "Path to the wallet" + + AdminWalletPath = "wallet-admin" + AdminWalletUsage = "Path to the admin wallet" + AlphabetWalletsFlag = "alphabet-wallets" AlphabetWalletsFlagDesc = "Path to alphabet wallets dir" diff --git a/cmd/frostfs-adm/internal/modules/morph/frostfsid/frostfsid.go b/cmd/frostfs-adm/internal/modules/morph/frostfsid/frostfsid.go index b229d0436..3eaff0f6c 100644 --- a/cmd/frostfs-adm/internal/modules/morph/frostfsid/frostfsid.go +++ b/cmd/frostfs-adm/internal/modules/morph/frostfsid/frostfsid.go @@ -1,11 +1,13 @@ package frostfsid import ( + "errors" "fmt" "math/big" "sort" frostfsidclient "git.frostfs.info/TrueCloudLab/frostfs-contract/frostfsid/client" + "git.frostfs.info/TrueCloudLab/frostfs-contract/nns" frostfsidrpclient "git.frostfs.info/TrueCloudLab/frostfs-contract/rpcclient/frostfsid" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/modules/morph/constants" @@ -38,6 +40,11 @@ const ( groupIDFlag = "group-id" rootNamespacePlaceholder = "" + + keyFlag = "key" + keyDescFlag = "Key for storing a value in the subject's KV storage" + valueFlag = "value" + valueDescFlag = "Value to be stored in the subject's KV storage" ) var ( @@ -151,6 +158,15 @@ var ( }, Run: frostfsidListGroupSubjects, } + + frostfsidSetKVCmd = &cobra.Command{ + Use: "set-kv", + Short: "Store a key-value pair in the subject's KV storage", + PreRun: func(cmd *cobra.Command, _ []string) { + _ = viper.BindPFlag(commonflags.EndpointFlag, cmd.Flags().Lookup(commonflags.EndpointFlag)) + }, + Run: frostfsidSetKV, + } ) func initFrostfsIDCreateNamespaceCmd() { @@ -236,6 +252,25 @@ func initFrostfsIDListGroupSubjectsCmd() { frostfsidListGroupSubjectsCmd.Flags().Bool(includeNamesFlag, false, "Whether include subject name (require additional requests)") } +func initFrostfsIDSetKVCmd() { + Cmd.AddCommand(frostfsidSetKVCmd) + frostfsidSetKVCmd.Flags().StringP(commonflags.EndpointFlag, commonflags.EndpointFlagShort, "", commonflags.EndpointFlagDesc) + frostfsidSetKVCmd.Flags().String(subjectAddressFlag, "", "Subject address") + frostfsidSetKVCmd.Flags().String(keyFlag, "", keyDescFlag) + frostfsidSetKVCmd.Flags().String(valueFlag, "", valueDescFlag) + + frostfsidSetKVCmd.ValidArgsFunction = commonflags.AutoCompleteFlags + + _ = frostfsidSetKVCmd.RegisterFlagCompletionFunc(keyFlag, func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) { + flags := make([]string, 0, len(wellKnownFlags)) + for k := range wellKnownFlags { + flags = append(flags, k) + } + + return flags, cobra.ShellCompDirectiveDefault + }) +} + func frostfsidCreateNamespace(cmd *cobra.Command, _ []string) { ns := getFrostfsIDNamespace(cmd) @@ -403,6 +438,37 @@ func frostfsidRemoveSubjectFromGroup(cmd *cobra.Command, _ []string) { commonCmd.ExitOnErr(cmd, "remove subject from group error: %w", err) } +var wellKnownFlags = map[string]string{ + nns.FrostfsIDNNSTLDPermissionKey: nns.FrostfsIDTLDRegistrationAllowed, +} + +func frostfsidSetKV(cmd *cobra.Command, _ []string) { + subjectAddress := getFrostfsIDSubjectAddress(cmd) + key, _ := cmd.Flags().GetString(keyFlag) + value, _ := cmd.Flags().GetString(valueFlag) + + for wellKnownKey, wellKnownValue := range wellKnownFlags { + if key == wellKnownKey && value == "" { + value = wellKnownValue + cmd.Println("Use the default value for a well-known flag.") + } + } + + if key == "" { + commonCmd.ExitOnErr(cmd, "", errors.New("key can't be empty")) + } + + ffsid, err := newFrostfsIDClient(cmd) + commonCmd.ExitOnErr(cmd, "init contract client: %w", err) + + method, args := ffsid.roCli.SetSubjectKVCall(subjectAddress, key, value) + + ffsid.addCall(method, args) + + err = ffsid.sendWait() + commonCmd.ExitOnErr(cmd, "failed to set: %w", err) +} + func frostfsidListGroupSubjects(cmd *cobra.Command, _ []string) { ns := getFrostfsIDNamespace(cmd) groupID := getFrostfsIDGroupID(cmd) diff --git a/cmd/frostfs-adm/internal/modules/morph/frostfsid/root.go b/cmd/frostfs-adm/internal/modules/morph/frostfsid/root.go index 6ffcaa487..930865f81 100644 --- a/cmd/frostfs-adm/internal/modules/morph/frostfsid/root.go +++ b/cmd/frostfs-adm/internal/modules/morph/frostfsid/root.go @@ -12,6 +12,7 @@ func init() { initFrostfsIDAddSubjectToGroupCmd() initFrostfsIDRemoveSubjectFromGroupCmd() initFrostfsIDListGroupSubjectsCmd() + initFrostfsIDSetKVCmd() initFrostfsIDAddSubjectKeyCmd() initFrostfsIDRemoveSubjectKeyCmd() }