...

Source file src/k8s.io/kubectl/pkg/util/templates/templater.go

Documentation: k8s.io/kubectl/pkg/util/templates

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package templates
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"strings"
    23  	"text/template"
    24  	"unicode"
    25  
    26  	"github.com/spf13/cobra"
    27  	flag "github.com/spf13/pflag"
    28  
    29  	"k8s.io/kubectl/pkg/util/term"
    30  )
    31  
    32  type FlagExposer interface {
    33  	ExposeFlags(cmd *cobra.Command, flags ...string) FlagExposer
    34  }
    35  
    36  func ActsAsRootCommand(cmd *cobra.Command, filters []string, groups ...CommandGroup) FlagExposer {
    37  	if cmd == nil {
    38  		panic("nil root command")
    39  	}
    40  	templater := &templater{
    41  		RootCmd:       cmd,
    42  		UsageTemplate: MainUsageTemplate(),
    43  		HelpTemplate:  MainHelpTemplate(),
    44  		CommandGroups: groups,
    45  		Filtered:      filters,
    46  	}
    47  	cmd.SetFlagErrorFunc(templater.FlagErrorFunc())
    48  	cmd.SilenceUsage = true
    49  	cmd.SetUsageFunc(templater.UsageFunc())
    50  	cmd.SetHelpFunc(templater.HelpFunc())
    51  	return templater
    52  }
    53  
    54  func UseOptionsTemplates(cmd *cobra.Command) {
    55  	templater := &templater{
    56  		UsageTemplate: OptionsUsageTemplate(),
    57  		HelpTemplate:  OptionsHelpTemplate(),
    58  	}
    59  	cmd.SetUsageFunc(templater.UsageFunc())
    60  	cmd.SetHelpFunc(templater.HelpFunc())
    61  }
    62  
    63  type templater struct {
    64  	UsageTemplate string
    65  	HelpTemplate  string
    66  	RootCmd       *cobra.Command
    67  	CommandGroups
    68  	Filtered []string
    69  }
    70  
    71  func (templater *templater) FlagErrorFunc(exposedFlags ...string) func(*cobra.Command, error) error {
    72  	return func(c *cobra.Command, err error) error {
    73  		c.SilenceUsage = true
    74  		switch c.CalledAs() {
    75  		case "options":
    76  			return fmt.Errorf("%s\nRun '%s' without flags.", err, c.CommandPath())
    77  		default:
    78  			return fmt.Errorf("%s\nSee '%s --help' for usage.", err, c.CommandPath())
    79  		}
    80  	}
    81  }
    82  
    83  func (templater *templater) ExposeFlags(cmd *cobra.Command, flags ...string) FlagExposer {
    84  	cmd.SetUsageFunc(templater.UsageFunc(flags...))
    85  	return templater
    86  }
    87  
    88  func (templater *templater) HelpFunc() func(*cobra.Command, []string) {
    89  	return func(c *cobra.Command, s []string) {
    90  		t := template.New("help")
    91  		t.Funcs(templater.templateFuncs())
    92  		template.Must(t.Parse(templater.HelpTemplate))
    93  		out := term.NewResponsiveWriter(c.OutOrStdout())
    94  		err := t.Execute(out, c)
    95  		if err != nil {
    96  			c.Println(err)
    97  		}
    98  	}
    99  }
   100  
   101  func (templater *templater) UsageFunc(exposedFlags ...string) func(*cobra.Command) error {
   102  	return func(c *cobra.Command) error {
   103  		t := template.New("usage")
   104  		t.Funcs(templater.templateFuncs(exposedFlags...))
   105  		template.Must(t.Parse(templater.UsageTemplate))
   106  		out := term.NewResponsiveWriter(c.OutOrStderr())
   107  		return t.Execute(out, c)
   108  	}
   109  }
   110  
   111  func (templater *templater) templateFuncs(exposedFlags ...string) template.FuncMap {
   112  	return template.FuncMap{
   113  		"trim":                strings.TrimSpace,
   114  		"trimRight":           func(s string) string { return strings.TrimRightFunc(s, unicode.IsSpace) },
   115  		"trimLeft":            func(s string) string { return strings.TrimLeftFunc(s, unicode.IsSpace) },
   116  		"gt":                  cobra.Gt,
   117  		"eq":                  cobra.Eq,
   118  		"rpad":                rpad,
   119  		"appendIfNotPresent":  appendIfNotPresent,
   120  		"flagsNotIntersected": flagsNotIntersected,
   121  		"visibleFlags":        visibleFlags,
   122  		"flagsUsages":         flagsUsages,
   123  		"cmdGroups":           templater.cmdGroups,
   124  		"cmdGroupsString":     templater.cmdGroupsString,
   125  		"rootCmd":             templater.rootCmdName,
   126  		"isRootCmd":           templater.isRootCmd,
   127  		"optionsCmdFor":       templater.optionsCmdFor,
   128  		"usageLine":           templater.usageLine,
   129  		"reverseParentsNames": templater.reverseParentsNames,
   130  		"exposed": func(c *cobra.Command) *flag.FlagSet {
   131  			exposed := flag.NewFlagSet("exposed", flag.ContinueOnError)
   132  			if len(exposedFlags) > 0 {
   133  				for _, name := range exposedFlags {
   134  					if flag := c.Flags().Lookup(name); flag != nil {
   135  						exposed.AddFlag(flag)
   136  					}
   137  				}
   138  			}
   139  			return exposed
   140  		},
   141  	}
   142  }
   143  
   144  func (templater *templater) cmdGroups(c *cobra.Command, all []*cobra.Command) []CommandGroup {
   145  	if len(templater.CommandGroups) > 0 && c == templater.RootCmd {
   146  		all = filter(all, templater.Filtered...)
   147  		return AddAdditionalCommands(templater.CommandGroups, "Other Commands:", all)
   148  	}
   149  	all = filter(all, "options")
   150  	return []CommandGroup{
   151  		{
   152  			Message:  "Available Commands:",
   153  			Commands: all,
   154  		},
   155  	}
   156  }
   157  
   158  func (t *templater) cmdGroupsString(c *cobra.Command) string {
   159  	groups := []string{}
   160  	for _, cmdGroup := range t.cmdGroups(c, c.Commands()) {
   161  		cmds := []string{cmdGroup.Message}
   162  		for _, cmd := range cmdGroup.Commands {
   163  			if cmd.IsAvailableCommand() {
   164  				cmds = append(cmds, "  "+rpad(cmd.Name(), cmd.NamePadding())+"   "+cmd.Short)
   165  			}
   166  		}
   167  		groups = append(groups, strings.Join(cmds, "\n"))
   168  	}
   169  	return strings.Join(groups, "\n\n")
   170  }
   171  
   172  func (t *templater) rootCmdName(c *cobra.Command) string {
   173  	return t.rootCmd(c).CommandPath()
   174  }
   175  
   176  func (t *templater) reverseParentsNames(c *cobra.Command) []string {
   177  	reverseParentsNames := []string{}
   178  	parents := t.parents(c)
   179  	for i := len(parents) - 1; i >= 0; i-- {
   180  		reverseParentsNames = append(reverseParentsNames, parents[i].Name())
   181  	}
   182  	return reverseParentsNames
   183  }
   184  
   185  func (t *templater) isRootCmd(c *cobra.Command) bool {
   186  	return t.rootCmd(c) == c
   187  }
   188  
   189  func (t *templater) parents(c *cobra.Command) []*cobra.Command {
   190  	parents := []*cobra.Command{c}
   191  	for current := c; !t.isRootCmd(current) && current.HasParent(); {
   192  		current = current.Parent()
   193  		parents = append(parents, current)
   194  	}
   195  	return parents
   196  }
   197  
   198  func (t *templater) rootCmd(c *cobra.Command) *cobra.Command {
   199  	if c != nil && !c.HasParent() {
   200  		return c
   201  	}
   202  	if t.RootCmd == nil {
   203  		panic("nil root cmd")
   204  	}
   205  	return t.RootCmd
   206  }
   207  
   208  func (t *templater) optionsCmdFor(c *cobra.Command) string {
   209  	if !c.Runnable() {
   210  		return ""
   211  	}
   212  	rootCmdStructure := t.parents(c)
   213  	for i := len(rootCmdStructure) - 1; i >= 0; i-- {
   214  		cmd := rootCmdStructure[i]
   215  		if _, _, err := cmd.Find([]string{"options"}); err == nil {
   216  			return cmd.CommandPath() + " options"
   217  		}
   218  	}
   219  	return ""
   220  }
   221  
   222  func (t *templater) usageLine(c *cobra.Command) string {
   223  	usage := c.UseLine()
   224  	suffix := "[options]"
   225  	if c.HasFlags() && !strings.Contains(usage, suffix) {
   226  		usage += " " + suffix
   227  	}
   228  	return usage
   229  }
   230  
   231  // flagsUsages will print out the kubectl help flags
   232  func flagsUsages(f *flag.FlagSet) (string, error) {
   233  	flagBuf := new(bytes.Buffer)
   234  	wrapLimit, err := term.GetWordWrapperLimit()
   235  	if err != nil {
   236  		wrapLimit = 0
   237  	}
   238  	printer := NewHelpFlagPrinter(flagBuf, wrapLimit)
   239  
   240  	f.VisitAll(func(flag *flag.Flag) {
   241  		if flag.Hidden {
   242  			return
   243  		}
   244  		printer.PrintHelpFlag(flag)
   245  	})
   246  
   247  	return flagBuf.String(), nil
   248  }
   249  
   250  // getFlagFormat will output the flag format
   251  func getFlagFormat(f *flag.Flag) string {
   252  	var format string
   253  	format = "--%s=%s:\n%s%s"
   254  	if f.Value.Type() == "string" {
   255  		format = "--%s='%s':\n%s%s"
   256  	}
   257  
   258  	if len(f.Shorthand) > 0 {
   259  		format = "    -%s, " + format
   260  	} else {
   261  		format = "    %s" + format
   262  	}
   263  
   264  	return format
   265  }
   266  
   267  func rpad(s string, padding int) string {
   268  	template := fmt.Sprintf("%%-%ds", padding)
   269  	return fmt.Sprintf(template, s)
   270  }
   271  
   272  func appendIfNotPresent(s, stringToAppend string) string {
   273  	if strings.Contains(s, stringToAppend) {
   274  		return s
   275  	}
   276  	return s + " " + stringToAppend
   277  }
   278  
   279  func flagsNotIntersected(l *flag.FlagSet, r *flag.FlagSet) *flag.FlagSet {
   280  	f := flag.NewFlagSet("notIntersected", flag.ContinueOnError)
   281  	l.VisitAll(func(flag *flag.Flag) {
   282  		if r.Lookup(flag.Name) == nil {
   283  			f.AddFlag(flag)
   284  		}
   285  	})
   286  	return f
   287  }
   288  
   289  func visibleFlags(l *flag.FlagSet) *flag.FlagSet {
   290  	hidden := "help"
   291  	f := flag.NewFlagSet("visible", flag.ContinueOnError)
   292  	l.VisitAll(func(flag *flag.Flag) {
   293  		if flag.Name != hidden {
   294  			f.AddFlag(flag)
   295  		}
   296  	})
   297  	return f
   298  }
   299  
   300  func filter(cmds []*cobra.Command, names ...string) []*cobra.Command {
   301  	out := []*cobra.Command{}
   302  	for _, c := range cmds {
   303  		if c.Hidden {
   304  			continue
   305  		}
   306  		skip := false
   307  		for _, name := range names {
   308  			if name == c.Name() {
   309  				skip = true
   310  				break
   311  			}
   312  		}
   313  		if skip {
   314  			continue
   315  		}
   316  		out = append(out, c)
   317  	}
   318  	return out
   319  }
   320  

View as plain text