From 5f4accaf3a89a391f55a27f6c750c87c3e9e5aa3 Mon Sep 17 00:00:00 2001 From: Cyber-SiKu Date: Sat, 6 May 2023 16:50:20 +0800 Subject: [PATCH] [feat] Adjust help output 1. Separate global flags from local flags 2. Add a function to automatically generate finalcmd example 3 take `bs check operator` and `bs update throttle` ad example Signed-off-by: Cyber-SiKu --- tools-v2/README.md | 8 +- tools-v2/internal/utils/cobra.go | 82 +++++++++++++++++-- tools-v2/internal/utils/{align.go => flag.go} | 13 +++ tools-v2/internal/utils/proto.go | 9 -- tools-v2/internal/utils/string.go | 9 -- tools-v2/pkg/cli/command/base.go | 1 - .../curvebs/check/operator/operator.go | 6 +- .../curvebs/update/throttle/throttle.go | 5 -- .../curvefs/status/metaserver/metaserver.go | 2 +- tools-v2/pkg/cli/curvecli.go | 2 + tools-v2/pkg/config/bs.go | 48 +++++++++-- tools-v2/pkg/config/config.go | 15 +++- tools-v2/pkg/config/fs.go | 3 +- 13 files changed, 157 insertions(+), 46 deletions(-) rename tools-v2/internal/utils/{align.go => flag.go} (76%) diff --git a/tools-v2/README.md b/tools-v2/README.md index a6eeea9c23..51c18a6684 100644 --- a/tools-v2/README.md +++ b/tools-v2/README.md @@ -50,6 +50,7 @@ A tool for CurveFS & CurveBs. - [list space](#list-space) - [query](#query-1) - [query file](#query-file) + - [clean-recycle](#clean-recycle) - [query chunk](#query-chunk) - [query segment](#query-segment) - [status](#status-1) @@ -63,9 +64,8 @@ A tool for CurveFS & CurveBs. - [update file](#update-file) - [update throttle](#update-throttle) - [create dir](#create-dir) - - [clean-recycle](#clean-recycle) - [check](#check-1) - - [check copyset](#check-copyset) + - [check copyset](#check-copyset-1) - [Comparison of old and new commands](#comparison-of-old-and-new-commands) - [curve fs](#curve-fs) - [curve bs](#curve-bs) @@ -1268,9 +1268,10 @@ Output: | space | curve bs list space | | update-throttle | curve bs update throttle | | check-copyset | curve bs check copyset | +| client-status | curve bs status client | +| check-operator | curve bs check operator | | status | | | chunkserver-status | | -| client-status | curve bs status client | | snapshot-clone-status | | | copysets-status | | | chunkserver-list | | @@ -1282,7 +1283,6 @@ Output: | do-snapshot-all | | | check-chunkserver | | | check-server | | -| check-operator | | | list-may-broken-vol | | | set-copyset-availflag | | | rapid-leader-schedule | | diff --git a/tools-v2/internal/utils/cobra.go b/tools-v2/internal/utils/cobra.go index c8e211afbc..90a08c081c 100644 --- a/tools-v2/internal/utils/cobra.go +++ b/tools-v2/internal/utils/cobra.go @@ -28,6 +28,7 @@ import ( "github.com/docker/cli/cli/command" "github.com/moby/term" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var ( @@ -62,18 +63,26 @@ Commands: {{- end}} {{- end}} -{{- if .HasAvailableFlags}} +{{- if .HasAvailableLocalFlags}} Flags: -{{ wrappedFlagUsages . | trimRightSpace}} - +{{ wrapLocalFlagUsages . | trimRightSpace}} {{- end}} +{{- if .HasAvailableInheritedFlags}} +Global Flags: +{{ wrapInheritedFlagUsages . | trimRightSpace}} +{{- end}} {{- if .HasExample}} Examples: {{ .Example }} +{{ else if not .HasSubCommands}} + +Examples: +{{ genExample .}} + {{- end}} {{- if .HasSubCommands }} @@ -97,12 +106,28 @@ func hasSubCommands(cmd *cobra.Command) bool { return len(subCommands(cmd)) > 0 } -func wrappedFlagUsages(cmd *cobra.Command) string { +// func wrappedFlagUsages(cmd *cobra.Command) string { +// width := 80 +// if ws, err := term.GetWinsize(0); err == nil { +// width = int(ws.Width) +// } +// return cmd.Flags().FlagUsagesWrapped(width - 1) +// } + +func wrapLocalFlagUsages(cmd *cobra.Command) string { + width := 80 + if ws, err := term.GetWinsize(0); err == nil { + width = int(ws.Width) + } + return cmd.LocalFlags().FlagUsagesWrapped(width - 1) +} + +func wrapInheritedFlagUsages(cmd *cobra.Command) string { width := 80 if ws, err := term.GetWinsize(0); err == nil { width = int(ws.Width) } - return cmd.Flags().FlagUsagesWrapped(width - 1) + return cmd.InheritedFlags().FlagUsagesWrapped(width - 1) } func SetFlagErrorFunc(cmd *cobra.Command) { @@ -119,9 +144,54 @@ func SetHelpTemplate(cmd *cobra.Command) { cmd.SetHelpTemplate(helpTemplate) } +type cmdType int + +const ( + BSNAME = "bs" + FSNAME = "fs" + Unknown cmdType = iota + RootCmd + BsCmd + FsCmd +) + +// return the type of command (bs or fs or root) +func GetCmdType(cmd *cobra.Command) cmdType { + if !cmd.HasParent() { + return RootCmd + } + if cmd.Parent().HasParent() { + return GetCmdType(cmd.Parent()) + } + switch cmd.Name() { + case BSNAME: + return BsCmd + case FSNAME: + return FsCmd + default: + return Unknown + } +} + +func genExample(cmd *cobra.Command) string { + ret := cmd.CommandPath() + if cmd.HasLocalFlags() { + lFlags := cmd.LocalFlags() + lFlags.VisitAll(func(flag *pflag.Flag) { + required := flag.Annotations[cobra.BashCompOneRequiredFlag] + if len(required) > 0 && required[0] == "true" { + ret += fmt.Sprintf(" --%s %v", flag.Name, AvailableValueStr(flag, GetCmdType(cmd))) + } + }) + } + return ret +} + func SetUsageTemplate(cmd *cobra.Command) { cobra.AddTemplateFunc("subCommands", subCommands) cobra.AddTemplateFunc("hasSubCommands", hasSubCommands) - cobra.AddTemplateFunc("wrappedFlagUsages", wrappedFlagUsages) + cobra.AddTemplateFunc("wrapLocalFlagUsages", wrapLocalFlagUsages) + cobra.AddTemplateFunc("wrapInheritedFlagUsages", wrapInheritedFlagUsages) + cobra.AddTemplateFunc("genExample", genExample) cmd.SetUsageTemplate(usageTemplate) } diff --git a/tools-v2/internal/utils/align.go b/tools-v2/internal/utils/flag.go similarity index 76% rename from tools-v2/internal/utils/align.go rename to tools-v2/internal/utils/flag.go index 8dcbe72cd3..e5f7da6b40 100644 --- a/tools-v2/internal/utils/align.go +++ b/tools-v2/internal/utils/flag.go @@ -22,6 +22,19 @@ package cobrautil +import ( + "github.com/opencurve/curve/tools-v2/pkg/config" + "github.com/spf13/pflag" +) + func IsAligned(value uint64, alignment uint64) bool { return value&(alignment-1) == 0 } + +func AvailableValueStr(flag *pflag.Flag, cmdtype cmdType) string { + switch cmdtype { + case BsCmd: + return config.BsAvailableValueStr(flag.Name) + } + return "" +} diff --git a/tools-v2/internal/utils/proto.go b/tools-v2/internal/utils/proto.go index 0a0d325302..fc53053a9e 100644 --- a/tools-v2/internal/utils/proto.go +++ b/tools-v2/internal/utils/proto.go @@ -212,15 +212,6 @@ func TranslateFileType(fileType string) (nameserver2.FileType, *cmderror.CmdErro return nameserver2.FileType_INODE_DIRECTORY, retErr } -const ( - IOPS_TOTAL = "iops_total" - IOPS_READ = "iops_read" - IOPS_WRITE = "iops_write" - BPS_TOTAL = "bps_total" - BPS_READ = "bps_read" - BPS_WRITE = "bps_write" -) - func ParseThrottleType(typeStr string) (nameserver2.ThrottleType, *cmderror.CmdError) { throttleType := nameserver2.ThrottleType_value[strings.ToUpper(typeStr)] var retErr *cmderror.CmdError diff --git a/tools-v2/internal/utils/string.go b/tools-v2/internal/utils/string.go index 7d21b69b92..1e6269eeeb 100644 --- a/tools-v2/internal/utils/string.go +++ b/tools-v2/internal/utils/string.go @@ -38,7 +38,6 @@ import ( ) const ( - IP_PORT_REGEX = "((\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5]):([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{4}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5]))|(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])" PATH_REGEX = `^(/[^/ ]*)+/?$` FS_NAME_REGEX = "^([a-z0-9]+\\-?)+$" K_STRING_TRUE = "true" @@ -47,14 +46,6 @@ const ( RECYCLEBIN_PATH = "/RecycleBin" ) -func IsValidAddr(addr string) bool { - matched, err := regexp.MatchString(IP_PORT_REGEX, addr) - if err != nil || !matched { - return false - } - return true -} - func IsValidFsname(fsName string) bool { matched, err := regexp.MatchString(FS_NAME_REGEX, fsName) if err != nil || !matched { diff --git a/tools-v2/pkg/cli/command/base.go b/tools-v2/pkg/cli/command/base.go index 94811be63b..1ed234b637 100644 --- a/tools-v2/pkg/cli/command/base.go +++ b/tools-v2/pkg/cli/command/base.go @@ -125,7 +125,6 @@ func NewFinalCurveCli(cli *FinalCurveCmd, funcs FinalCurveCmdFunc) *cobra.Comman }, SilenceUsage: false, } - config.AddFormatFlag(cli.Cmd) funcs.AddFlags() cobrautil.SetFlagErrorFunc(cli.Cmd) diff --git a/tools-v2/pkg/cli/command/curvebs/check/operator/operator.go b/tools-v2/pkg/cli/command/curvebs/check/operator/operator.go index b84087cee3..61c9c89914 100644 --- a/tools-v2/pkg/cli/command/curvebs/check/operator/operator.go +++ b/tools-v2/pkg/cli/command/curvebs/check/operator/operator.go @@ -126,9 +126,9 @@ func (oCmd *OperatorCommand) ResultPlainOutput() error { func NewCheckOperatorCommand() *OperatorCommand { operatorCmd := &OperatorCommand{ FinalCurveCmd: basecmd.FinalCurveCmd{ - Use: "operator", - Short: "check the operators", - Example: operatorExample, + Use: "operator", + Short: "check the operators", + // Example: operatorExample, }} basecmd.NewFinalCurveCli(&operatorCmd.FinalCurveCmd, operatorCmd) return operatorCmd diff --git a/tools-v2/pkg/cli/command/curvebs/update/throttle/throttle.go b/tools-v2/pkg/cli/command/curvebs/update/throttle/throttle.go index a9b533e256..dc561a61b1 100644 --- a/tools-v2/pkg/cli/command/curvebs/update/throttle/throttle.go +++ b/tools-v2/pkg/cli/command/curvebs/update/throttle/throttle.go @@ -35,10 +35,6 @@ import ( "google.golang.org/grpc" ) -const ( - throttleExample = `$ curve bs update throttle --path /test --throttleType iops_total|iops_read|iops_write|bps_total|bps_read|bps_write --limit 20000 [--burst 30000] [--burstlength 10]` -) - type UpdateFileThrottleRpc struct { Info *basecmd.Rpc Request *nameserver2.UpdateFileThrottleParamsRequest @@ -68,7 +64,6 @@ func NewUpdateThrottleCommand() *ThrottleCommand { FinalCurveCmd: basecmd.FinalCurveCmd{ Use: "throttle", Short: "update file throttle params", - Example: throttleExample, }, } basecmd.NewFinalCurveCli(&throttleCmd.FinalCurveCmd, throttleCmd) diff --git a/tools-v2/pkg/cli/command/curvefs/status/metaserver/metaserver.go b/tools-v2/pkg/cli/command/curvefs/status/metaserver/metaserver.go index fab2e65c5f..26e264e5fa 100644 --- a/tools-v2/pkg/cli/command/curvefs/status/metaserver/metaserver.go +++ b/tools-v2/pkg/cli/command/curvefs/status/metaserver/metaserver.go @@ -87,7 +87,7 @@ func (mCmd *MetaserverCommand) Init(cmd *cobra.Command, args []string) error { )) for i, addr := range externalAddrs { - if !cobrautil.IsValidAddr(addr) { + if !config.IsValidAddr(addr) { return fmt.Errorf("invalid metaserver external addr: %s", addr) } diff --git a/tools-v2/pkg/cli/curvecli.go b/tools-v2/pkg/cli/curvecli.go index 2cce4d4a7e..30eb20d7f8 100644 --- a/tools-v2/pkg/cli/curvecli.go +++ b/tools-v2/pkg/cli/curvecli.go @@ -71,6 +71,8 @@ func newCurveCommand() *cobra.Command { config.AddShowErrorPFlag(rootCmd) rootCmd.PersistentFlags().BoolP("verbose", "", false, "show some log") viper.BindPFlag("useViper", rootCmd.PersistentFlags().Lookup("viper")) + // config.AddFormatFlag(rootCmd) + rootCmd.PersistentFlags().StringP("format", "f", config.FORMAT_PLAIN, "output format (json|plain)") addSubCommands(rootCmd) setupRootCommand(rootCmd) diff --git a/tools-v2/pkg/config/bs.go b/tools-v2/pkg/config/bs.go index 31d4dbffb0..760a7b6118 100644 --- a/tools-v2/pkg/config/bs.go +++ b/tools-v2/pkg/config/bs.go @@ -22,6 +22,7 @@ package config import ( + "fmt" "strings" "time" @@ -29,7 +30,6 @@ import ( "github.com/gookit/color" cmderror "github.com/opencurve/curve/tools-v2/internal/error" - cobrautil "github.com/opencurve/curve/tools-v2/internal/utils" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -44,7 +44,7 @@ const ( VIPER_CURVEBS_ETCDADDR = "curvebs.etcdAddr" CURVEBS_PATH = "path" VIPER_CURVEBS_PATH = "curvebs.path" - CURVEBS_DEFAULT_PATH = "/" + CURVEBS_DEFAULT_PATH = "/test" CURVEBS_USER = "user" VIPER_CURVEBS_USER = "curvebs.root.user" CURVEBS_DEFAULT_USER = "root" @@ -148,6 +148,44 @@ var ( } ) +const ( + CURVEBS_OP_OPERATOR = "operator" + CURVEBS_OP_CHANGE_PEER = "change_peer" + CURVEBS_OP_ADD_PEER = "add_peer" + CURVEBS_OP_REMOVE_PEER = "remove_peer" + CURVEBS_OP_TRANSFER__LEADER = "transfer_leader" + + CURVEBS_IOPS_TOTAL = "iops_total" + CURVEBS_IOPS_READ = "iops_read" + CURVEBS_IOPS_WRITE = "iops_write" + CURVEBS_BPS_TOTAL = "bps_total" + CURVEBS_BPS_READ = "bps_read" + CURVEBS_BPS_WRITE = "bps_write" +) + +var ( + CURVEBS_OP_VALUE_SLICE = []string{CURVEBS_OP_OPERATOR, CURVEBS_OP_CHANGE_PEER, CURVEBS_OP_ADD_PEER, CURVEBS_OP_REMOVE_PEER, CURVEBS_OP_TRANSFER__LEADER} + + CURVEBS_THROTTLE_TYPE_SLICE = []string{CURVEBS_IOPS_TOTAL, CURVEBS_IOPS_READ, CURVEBS_IOPS_WRITE, CURVEBS_BPS_TOTAL, CURVEBS_BPS_READ, CURVEBS_BPS_WRITE} +) + +var ( + BS_STRING_FLAG2AVAILABLE = map[string][]string{ + CURVEBS_OP: CURVEBS_OP_VALUE_SLICE, + CURVEBS_TYPE: CURVEBS_THROTTLE_TYPE_SLICE, + } +) + +func BsAvailableValueStr(flagName string) string { + ret := "" + if slice, ok := BS_STRING_FLAG2AVAILABLE[flagName]; ok { + ret = strings.Join(slice, "|") + } else if ret, ok = BSFLAG2DEFAULT[flagName].(string); !ok { + ret = "" + } + return ret +} + // curvebs // add bs option flag func AddBsStringSliceOptionFlag(cmd *cobra.Command, name string, usage string) { @@ -372,7 +410,7 @@ func AddBsFileTypeRequiredFlag(cmd *cobra.Command) { } func AddBsThrottleTypeRequiredFlag(cmd *cobra.Command) { - AddBsStringRequiredFlag(cmd, CURVEBS_TYPE, "throttle type, iops_total|iops_read|iops_write|bps_total|bps_read|bps_write") + AddBsStringRequiredFlag(cmd, CURVEBS_TYPE, fmt.Sprintf("throttle type, %s", BsAvailableValueStr(CURVEBS_TYPE))) } func AddBsLimitRequiredFlag(cmd *cobra.Command) { @@ -380,7 +418,7 @@ func AddBsLimitRequiredFlag(cmd *cobra.Command) { } func AddBsOpRequiredFlag(cmd *cobra.Command) { - AddBsStringRequiredFlag(cmd, CURVEBS_OP, "check operator name, operator|change_peer|add_peer|remove_peer|transfer_leader") + AddBsStringRequiredFlag(cmd, CURVEBS_OP, fmt.Sprintf("check operator name, %s", BsAvailableValueStr(CURVEBS_OP))) } // get stingslice flag @@ -452,7 +490,7 @@ func GetBsAddrSlice(cmd *cobra.Command, addrType string) ([]string, *cmderror.Cm } addrslice := strings.Split(addrsStr, ",") for _, addr := range addrslice { - if !cobrautil.IsValidAddr(addr) { + if !IsValidAddr(addr) { err := cmderror.ErrGetAddr() err.Format(addrType, addr) return addrslice, err diff --git a/tools-v2/pkg/config/config.go b/tools-v2/pkg/config/config.go index c4d2d1b892..b67741896e 100644 --- a/tools-v2/pkg/config/config.go +++ b/tools-v2/pkg/config/config.go @@ -23,6 +23,7 @@ package config import ( "os" + "regexp" "time" "github.com/spf13/cobra" @@ -92,7 +93,7 @@ const ( ) func AddFormatFlag(cmd *cobra.Command) { - cmd.Flags().StringP("format", "f", FORMAT_PLAIN, "output format (json|plain)") + cmd.PersistentFlags().StringP("format", "f", FORMAT_PLAIN, "output format (json|plain)") err := viper.BindPFlag("format", cmd.Flags().Lookup("format")) if err != nil { cobra.CheckErr(err) @@ -173,3 +174,15 @@ func GetFlagChanged(cmd *cobra.Command, flagName string) bool { } return false } + +const ( + IP_PORT_REGEX = "((\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5]):([0-9]|[1-9]\\d{1,3}|[1-5]\\d{4}|6[0-4]\\d{4}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5]))|(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])" +) + +func IsValidAddr(addr string) bool { + matched, err := regexp.MatchString(IP_PORT_REGEX, addr) + if err != nil || !matched { + return false + } + return true +} diff --git a/tools-v2/pkg/config/fs.go b/tools-v2/pkg/config/fs.go index 232c743b95..304400db1c 100644 --- a/tools-v2/pkg/config/fs.go +++ b/tools-v2/pkg/config/fs.go @@ -27,7 +27,6 @@ import ( "github.com/gookit/color" cmderror "github.com/opencurve/curve/tools-v2/internal/error" - cobrautil "github.com/opencurve/curve/tools-v2/internal/utils" "github.com/spf13/cobra" "github.com/spf13/viper" ) @@ -375,7 +374,7 @@ func GetAddrSlice(cmd *cobra.Command, addrType string) ([]string, *cmderror.CmdE } addrslice := strings.Split(addrsStr, ",") for _, addr := range addrslice { - if !cobrautil.IsValidAddr(addr) { + if !IsValidAddr(addr) { err := cmderror.ErrGetAddr() err.Format(addrType, addr) return addrslice, err