Beautify kubectl help flag commands

Kubernetes-commit: 1c031d4fa8450550fa9a6bd5abe7fac45816156e
This commit is contained in:
Chok Yip Lau 2021-09-02 13:41:07 -04:00 committed by Kubernetes Publisher
parent 9e4cf278d1
commit 6a5a78c26b
7 changed files with 140 additions and 38 deletions

View File

@ -105,7 +105,7 @@ func NewCmdAPIResources(f cmdutil.Factory, ioStreams genericclioptions.IOStreams
}
cmd.Flags().BoolVar(&o.NoHeaders, "no-headers", o.NoHeaders, "When using the default or custom-column output format, don't print headers (default print headers).")
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "Output format. One of: wide|name.")
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, `Output format. One of: (wide, name).`)
cmd.Flags().StringVar(&o.APIGroup, "api-group", o.APIGroup, "Limit to resources in the specified API group.")
cmd.Flags().BoolVar(&o.Namespaced, "namespaced", o.Namespaced, "If false, non-namespaced resources will be returned, otherwise returning namespaced resources by default.")

View File

@ -85,7 +85,7 @@ func NewCmdApplyViewLastApplied(f cmdutil.Factory, ioStreams genericclioptions.I
},
}
cmd.Flags().StringVarP(&options.OutputFormat, "output", "o", options.OutputFormat, "Output format. Must be one of yaml|json")
cmd.Flags().StringVarP(&options.OutputFormat, "output", "o", options.OutputFormat, `Output format. Must be one of (yaml, json)`)
cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources in the namespace of the specified resource types")
usage := "that contains the last-applied-configuration annotations"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)

View File

@ -80,7 +80,7 @@ func NewCmdConfigGetContexts(streams genericclioptions.IOStreams, configAccess c
}
cmd.Flags().Bool("no-headers", false, "When using the default or custom-column output format, don't print headers (default print headers).")
cmd.Flags().StringP("output", "o", "", "Output format. One of: name")
cmd.Flags().StringP("output", "o", "", `Output format. One of: (name).`)
return cmd
}

View File

@ -162,7 +162,7 @@ func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
f.CustomColumnsFlags.AddFlags(cmd)
if f.OutputFormat != nil {
cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, fmt.Sprintf("Output format. One of: %s See custom columns [https://kubernetes.io/docs/reference/kubectl/overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [https://kubernetes.io/docs/reference/kubectl/jsonpath/].", strings.Join(f.AllowedFormats(), "|")))
cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, fmt.Sprintf(`Output format. One of: (%s). See custom columns [https://kubernetes.io/docs/reference/kubectl/overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [https://kubernetes.io/docs/reference/kubectl/jsonpath/].`, strings.Join(f.AllowedFormats(), ", ")))
util.CheckErr(cmd.RegisterFlagCompletionFunc(
"output",
func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

View File

@ -0,0 +1,76 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package templates
import (
"bytes"
"fmt"
"io"
"strings"
"github.com/mitchellh/go-wordwrap"
flag "github.com/spf13/pflag"
)
const offset = 10
// HelpFlagPrinter is a printer that
// processes the help flag and print
// it to i/o writer
type HelpFlagPrinter struct {
wrapLimit uint
out io.Writer
}
// NewHelpFlagPrinter will initialize a HelpFlagPrinter given the
// i/o writer
func NewHelpFlagPrinter(out io.Writer, wrapLimit uint) *HelpFlagPrinter {
return &HelpFlagPrinter{
wrapLimit: wrapLimit,
out: out,
}
}
// PrintHelpFlag will beautify the help flags and print it out to p.out
func (p *HelpFlagPrinter) PrintHelpFlag(flag *flag.Flag) {
formatBuf := new(bytes.Buffer)
writeFlag(formatBuf, flag)
wrappedStr := formatBuf.String()
flagAndUsage := strings.Split(formatBuf.String(), "\n")
flagStr := flagAndUsage[0]
// if the flag usage is longer than one line, wrap it again
if len(flagAndUsage) > 1 {
nextLines := strings.Join(flagAndUsage[1:], " ")
wrappedUsages := wordwrap.WrapString(nextLines, p.wrapLimit-offset)
wrappedStr = flagStr + "\n" + wrappedUsages
}
appendTabStr := strings.ReplaceAll(wrappedStr, "\n", "\n\t")
fmt.Fprintf(p.out, appendTabStr+"\n\n")
}
// writeFlag will output the help flag based
// on the format provided by getFlagFormat to i/o writer
func writeFlag(out io.Writer, f *flag.Flag) {
deprecated := ""
if f.Deprecated != "" {
deprecated = fmt.Sprintf(" (DEPRECATED: %s)", f.Deprecated)
}
fmt.Fprintf(out, getFlagFormat(f), f.Shorthand, f.Name, f.DefValue, f.Usage, deprecated)
}

View File

@ -23,10 +23,10 @@ import (
"text/template"
"unicode"
"k8s.io/kubectl/pkg/util/term"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"k8s.io/kubectl/pkg/util/term"
)
type FlagExposer interface {
@ -160,7 +160,7 @@ func (t *templater) cmdGroupsString(c *cobra.Command) string {
cmds := []string{cmdGroup.Message}
for _, cmd := range cmdGroup.Commands {
if cmd.IsAvailableCommand() {
cmds = append(cmds, " "+rpad(cmd.Name(), cmd.NamePadding())+" "+cmd.Short)
cmds = append(cmds, " "+rpad(cmd.Name(), cmd.NamePadding())+" "+cmd.Short)
}
}
groups = append(groups, strings.Join(cmds, "\n"))
@ -218,34 +218,40 @@ func (t *templater) usageLine(c *cobra.Command) string {
return usage
}
func flagsUsages(f *flag.FlagSet) string {
x := new(bytes.Buffer)
// flagsUsages will print out the kubectl help flags
func flagsUsages(f *flag.FlagSet) (string, error) {
flagBuf := new(bytes.Buffer)
wrapLimit, err := term.GetWordWrapperLimit()
if err != nil {
return "", err
}
printer := NewHelpFlagPrinter(flagBuf, wrapLimit)
f.VisitAll(func(flag *flag.Flag) {
if flag.Hidden {
return
}
format := "--%s=%s: %s%s\n"
if flag.Value.Type() == "string" {
format = "--%s='%s': %s%s\n"
}
if len(flag.Shorthand) > 0 {
format = " -%s, " + format
} else {
format = " %s " + format
}
deprecated := ""
if flag.Deprecated != "" {
deprecated = fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated)
}
fmt.Fprintf(x, format, flag.Shorthand, flag.Name, flag.DefValue, flag.Usage, deprecated)
printer.PrintHelpFlag(flag)
})
return x.String()
return flagBuf.String(), nil
}
// getFlagFormat will output the flag format
func getFlagFormat(f *flag.Flag) string {
var format string
format = "--%s=%s:\n%s%s"
if f.Value.Type() == "string" {
format = "--%s='%s':\n%s%s"
}
if len(f.Shorthand) > 0 {
format = " -%s, " + format
} else {
format = " %s" + format
}
return format
}
func rpad(s string, padding int) string {

View File

@ -17,11 +17,14 @@ limitations under the License.
package term
import (
"errors"
"io"
"os"
wordwrap "github.com/mitchellh/go-wordwrap"
"github.com/moby/term"
"k8s.io/client-go/tools/remotecommand"
)
type wordWrapWriter struct {
@ -51,16 +54,7 @@ func NewResponsiveWriter(w io.Writer) io.Writer {
if terminalSize == nil {
return w
}
var limit uint
switch {
case terminalSize.Width >= 120:
limit = 120
case terminalSize.Width >= 100:
limit = 100
case terminalSize.Width >= 80:
limit = 80
}
limit := getTerminalLimitWidth(terminalSize)
return NewWordWrapWriter(w, limit)
}
@ -74,6 +68,32 @@ func NewWordWrapWriter(w io.Writer, limit uint) io.Writer {
}
}
func getTerminalLimitWidth(terminalSize *remotecommand.TerminalSize) uint {
var limit uint
switch {
case terminalSize.Width >= 120:
limit = 120
case terminalSize.Width >= 100:
limit = 100
case terminalSize.Width >= 80:
limit = 80
}
return limit
}
func GetWordWrapperLimit() (uint, error) {
stdout := os.Stdout
fd := stdout.Fd()
if !term.IsTerminal(fd) {
return 0, errors.New("file descriptor is not a terminal")
}
terminalSize := GetSize(fd)
if terminalSize == nil {
return 0, errors.New("terminal size is nil")
}
return getTerminalLimitWidth(terminalSize), nil
}
func (w wordWrapWriter) Write(p []byte) (nn int, err error) {
if w.limit == 0 {
return w.writer.Write(p)