From 77ad6e45f63534590ec45193977f619696b067d4 Mon Sep 17 00:00:00 2001 From: Charlie Drage Date: Mon, 9 Dec 2019 10:02:06 -0500 Subject: [PATCH] Improve the --help usage of helm commands **What kind of PR is this?** Improvement / feature **What does does this PR do / why we need it**: This improves the following: - Adds capitalization to all parameter descriptions - Improves the --help output by putting all logging flags into its own separate section **Which issue(s) this PR fixes**: Fixes https://github.com/helm/helm/issues/7163 **How to test changes / Special notes to the reviewer**: See the below changes: ```sh helm --help ... Flags: --debug Enable verbose output -h, --help Help for helm --kube-context string Name of the kubeconfig context to use --kubeconfig string Path to the kubeconfig file -n, --namespace string Namespace scope for this request --registry-config string Path to the registry config file (default "/home/wikus/.config/helm/registry.json") --repository-cache string Path to the file containing cached repository indexes (default "/home/wikus/.cache/helm/repository") --repository-config string Path to the file containing repository names and URLs (default "/home/wikus/.config/helm/repositories.yaml") Logging Flags: --add-dir-header If true, adds the file directory to the header --alsologtostderr Log to standard error as well as files --log-backtrace-at traceLocation When logging hits line file:N, emit a stack trace (default :0) --log-dir string If non-empty, write log files in this directory --log-file string If non-empty, use this log file --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --logtostderr Log to standard error instead of files (default true) --skip-headers If true, avoid header prefixes in the log messages --skip-log-headers If true, avoid headers when opening log files --stderrthreshold severity Logs at or above this threshold go to stderr (default 2) -v, --v Level Number for the log level verbosity --vmodule moduleSpec Comma-separated list of pattern=N settings for file-filtered logging Use "helm [command] --help" for more information about a command. ``` ```sh helm install ... Flags: --atomic If set, installation process purges chart on fail. The --wait flag will be set automatically if --atomic is used --ca-file string Verify certificates of HTTPS-enabled servers using this CA bundle --cert-file string Identify HTTPS client using this SSL certificate file --dependency-update Run helm dependency update before installing the chart --devel Use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored --dry-run Simulate an install -g, --generate-name Generate the name (and omit the NAME parameter) -h, --help Help for install --key-file string Identify HTTPS client using this SSL key file --keyring string Location of public keys used for verification (default "/home/wikus/.gnupg/pubring.gpg") --name-template string Specify template used to name the release --no-hooks Prevent hooks from running during install -o, --output format Prints the output in the specified format. Allowed values: table, json, yaml (default table) --password string Chart repository password where to locate the requested chart --render-subchart-notes If set, render subchart notes along with the parent --replace Re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production --repo string Chart repository url where to locate the requested chart --set stringArray Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --set-file stringArray Set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2) --set-string stringArray Set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --skip-crds If set, no CRDs will be installed. By default, CRDs are installed if not already present --timeout duration Time to wait for any individual Kubernetes operation (like Jobs for hooks) (default 5m0s) --username string Chart repository username where to locate the requested chart -f, --values strings Specify values in a YAML file or a URL(can specify multiple) --verify Verify the package before installing it --version string Specify the exact chart version to install. If this is not specified, the latest version is installed --wait If set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout Global Flags: --debug Enable verbose output --kube-context string Name of the kubeconfig context to use --kubeconfig string Path to the kubeconfig file -n, --namespace string Namespace scope for this request --registry-config string Path to the registry config file (default "/home/wikus/.config/helm/registry.json") --repository-cache string Path to the file containing cached repository indexes (default "/home/wikus/.cache/helm/repository") --repository-config string Path to the file containing repository names and URLs (default "/home/wikus/.config/helm/repositories.yaml") Logging Flags: --add-dir-header If true, adds the file directory to the header --alsologtostderr Log to standard error as well as files --log-backtrace-at traceLocation When logging hits line file:N, emit a stack trace (default :0) --log-dir string If non-empty, write log files in this directory --log-file string If non-empty, use this log file --log-file-max-size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) --logtostderr Log to standard error instead of files (default true) --skip-headers If true, avoid header prefixes in the log messages --skip-log-headers If true, avoid headers when opening log files --stderrthreshold severity Logs at or above this threshold go to stderr (default 2) -v, --v Level Number for the log level verbosity --vmodule moduleSpec Comma-separated list of pattern=N settings for file-filtered logging ``` Signed-off-by: Charlie Drage --- cmd/helm/helm.go | 28 ++++++++++++++++++ cmd/helm/root.go | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 2c3c9e401..9b86cfd70 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -59,6 +59,34 @@ func initKubeLogs() { klog.InitFlags(gofs) pflag.CommandLine.AddGoFlagSet(gofs) pflag.CommandLine.Set("logtostderr", "true") + + // List of logging parameters used by Helm and klog, see: + // https://github.com/kubernetes/klog/blob/master/klog.go#L422 + var loggingFlags = []string{ + "log_dir", + "log_file", + "log_file_max_size", + "logtostderr", + "alsologtostderr", + "v", + "add_dir_header", + "skip_headers", + "skip_log_headers", + "stderrthreshold", + "vmodule", + "log_backtrace_at", + } + setLoggingFlagAnnotations(loggingFlags) + +} + +// setLoggingFlagAnnotations sets all the logging flags to hidden as well as "hiding" the parameter from the normal +// --help output. The flags are then shown using a custom usage template +func setLoggingFlagAnnotations(flags []string) { + for _, flag := range flags { + pflag.CommandLine.SetAnnotation(flag, "log", []string{"flag"}) + pflag.CommandLine.MarkHidden(flag) + } } func main() { diff --git a/cmd/helm/root.go b/cmd/helm/root.go index f0a034d45..a7b6a2961 100644 --- a/cmd/helm/root.go +++ b/cmd/helm/root.go @@ -19,9 +19,12 @@ package main // import "helm.sh/helm/v3/cmd/helm" import ( "fmt" "io" + "reflect" "strings" + "unicode" "github.com/spf13/cobra" + "github.com/spf13/pflag" "helm.sh/helm/v3/cmd/helm/require" "helm.sh/helm/v3/internal/experimental/registry" @@ -430,5 +433,77 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string // Find and add plugins loadPlugins(cmd, out) + // Add template functions which will be used in the usage template + cobra.AddTemplateFunc("LoggingFlags", LoggingFlags) + cobra.AddTemplateFunc("CapitalizeFlagDescriptions", CapitalizeFlagDescriptions) + cmd.SetUsageTemplate(CmdUsageTemplate) return cmd } + +// LoggingFlags will go through all the logging flags which have the annotation: "log":"flag" and +// get the appropriate usage +func LoggingFlags(cmd *cobra.Command) string { + + globalFlags := cmd.Flags() + loggingFlags := &pflag.FlagSet{} + + // Retrieve all the flags that have that particular annotation + globalFlags.VisitAll(func(f *pflag.Flag) { + if reflect.DeepEqual(f.Annotations["log"], []string{"flag"}) { + loggingFlags.AddFlag(f) + } + }) + + // Weird hack with cobra13. Essentially, cobra doesn't have a function to return a non-pointer-set of flags + // so we'll set the flags as non-hidden, get the descriptions, then set them back to hidden + loggingFlags.VisitAll(func(f *pflag.Flag) { + f.Hidden = false + }) + usage := CapitalizeFlagDescriptions(loggingFlags) + loggingFlags.VisitAll(func(f *pflag.Flag) { + f.Hidden = true + }) + + return usage +} + +// CapitalizeFlagDescriptions adds capitalization to each flag +func CapitalizeFlagDescriptions(f *pflag.FlagSet) string { + f.VisitAll(func(f *pflag.Flag) { + cap := []rune(f.Usage) + cap[0] = unicode.ToUpper(cap[0]) + f.Usage = string(cap) + }) + return f.FlagUsages() +} + +// CmdUsageTemplate uses the default usage template but slightly modifies for capitalization as well as +// putting logging flags in their own section. +// See: https://github.com/spf13/cobra/blob/993cc5372a05240dfd59e3ba952748b36b2cd117/command.go#L464 +var CmdUsageTemplate = `Usage:{{if .Runnable}} + {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} + {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} + +Aliases: + {{.NameAndAliases}}{{end}}{{if .HasExample}} + +Examples: +{{.Example}}{{end}}{{if .HasAvailableSubCommands}} + +Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + +Flags: +{{CapitalizeFlagDescriptions .LocalFlags | trimRightSpace }}{{end}}{{if .HasAvailableInheritedFlags}} + +Global Flags: +{{CapitalizeFlagDescriptions .InheritedFlags | trimTrailingWhitespaces}}{{end}} + +Logging Flags: +{{LoggingFlags . | trimRightSpace }}{{if .HasHelpSubCommands}} + +Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} + +Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} +`