Merge pull request #104736 from lauchokyip/improveFlags

Beautify kubectl help flag commands

Kubernetes-commit: 750cb93e62bad7bc798b75c96eb4ff0f6f1883b9
This commit is contained in:
Kubernetes Publisher 2022-03-24 17:33:50 -07:00
commit ef04426c7b
9 changed files with 144 additions and 42 deletions

4
go.mod
View File

@ -33,7 +33,7 @@ require (
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.0.0-20220324210930-df53a95c65aa
k8s.io/apimachinery v0.0.0-20220324210734-b68ae5efb0e8
k8s.io/cli-runtime v0.0.0-20220324213237-c15667ffec05
k8s.io/cli-runtime v0.0.0-20220325012647-524d19e1c796
k8s.io/client-go v0.0.0-20220324211221-af0252eed9c0
k8s.io/component-base v0.0.0-20220324211736-57e5b231b63e
k8s.io/component-helpers v0.0.0-20220324211837-3d0aa6c934c2
@ -50,7 +50,7 @@ require (
replace (
k8s.io/api => k8s.io/api v0.0.0-20220324210930-df53a95c65aa
k8s.io/apimachinery => k8s.io/apimachinery v0.0.0-20220324210734-b68ae5efb0e8
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20220324213237-c15667ffec05
k8s.io/cli-runtime => k8s.io/cli-runtime v0.0.0-20220325012647-524d19e1c796
k8s.io/client-go => k8s.io/client-go v0.0.0-20220324211221-af0252eed9c0
k8s.io/code-generator => k8s.io/code-generator v0.0.0-20220324210542-671bd3e7b101
k8s.io/component-base => k8s.io/component-base v0.0.0-20220324211736-57e5b231b63e

4
go.sum
View File

@ -852,8 +852,8 @@ k8s.io/api v0.0.0-20220324210930-df53a95c65aa h1:NzCktMsy22Y/ddoBN/kf96kFaklITIH
k8s.io/api v0.0.0-20220324210930-df53a95c65aa/go.mod h1:Ea6ReZGsqdWOZc66t7sHO58lTcUGyQcMJ3UfsqOnKfM=
k8s.io/apimachinery v0.0.0-20220324210734-b68ae5efb0e8 h1:LULUz36bFy2WqAjJVZAbn4yVJ92Xn7oBaUCF6BR8HDk=
k8s.io/apimachinery v0.0.0-20220324210734-b68ae5efb0e8/go.mod h1:V4ECjDypP1xQpnL3N9yFzlbGZgd8tLKouJnRyAn/Zyw=
k8s.io/cli-runtime v0.0.0-20220324213237-c15667ffec05 h1:XElf3z4AiZ6ZBDGs+hR/xTWH+4QRXbIMzL2dwaTmV54=
k8s.io/cli-runtime v0.0.0-20220324213237-c15667ffec05/go.mod h1:O2DkSiS1T4zJvcVaGWoTeMtiuEeAuKgE0vJ5yNMJJRo=
k8s.io/cli-runtime v0.0.0-20220325012647-524d19e1c796 h1:Y7FlIv0av0nqBIfnCIPkqqRw4absoKoxg0rFQlC4k2w=
k8s.io/cli-runtime v0.0.0-20220325012647-524d19e1c796/go.mod h1:O2DkSiS1T4zJvcVaGWoTeMtiuEeAuKgE0vJ5yNMJJRo=
k8s.io/client-go v0.0.0-20220324211221-af0252eed9c0 h1:1CEitsCQXwuKQ22HYRYCgZPi0+3RXCQsiQlOAkkGAAI=
k8s.io/client-go v0.0.0-20220324211221-af0252eed9c0/go.mod h1:sVLZTMIBHUCD7MFbyyUPejZNIlOLYZ2aZcZuP0z1aI0=
k8s.io/code-generator v0.0.0-20220324210542-671bd3e7b101/go.mod h1:PF9AME2o5S390iHxUI2/febDrY7GRb+AECnBzIWxIO0=

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)