...

Source file src/k8s.io/kubectl/pkg/cmd/util/sanity/cmd_sanity.go

Documentation: k8s.io/kubectl/pkg/cmd/util/sanity

     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 sanity
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"regexp"
    23  	"strings"
    24  
    25  	"github.com/spf13/cobra"
    26  	"github.com/spf13/pflag"
    27  
    28  	"k8s.io/kubectl/pkg/util/templates"
    29  )
    30  
    31  // CmdCheck is the commom type of functions to check cobra commands
    32  type CmdCheck func(cmd *cobra.Command) []error
    33  
    34  // GlobalCheck is the common type of functions to check global flags
    35  type GlobalCheck func() []error
    36  
    37  var (
    38  	// AllCmdChecks is the list of CmdCheck type functions
    39  	AllCmdChecks = []CmdCheck{
    40  		CheckLongDesc,
    41  		CheckExamples,
    42  		CheckFlags,
    43  	}
    44  
    45  	// AllGlobalChecks is the list of GlobalCheck type functions
    46  	AllGlobalChecks = []GlobalCheck{
    47  		CheckGlobalVarFlags,
    48  	}
    49  )
    50  
    51  // RunGlobalChecks runs all the GlobalCheck functions passed and checks for error
    52  func RunGlobalChecks(globalChecks []GlobalCheck) []error {
    53  	fmt.Fprint(os.Stdout, "---+ RUNNING GLOBAL CHECKS\n")
    54  	errors := []error{}
    55  	for _, check := range globalChecks {
    56  		errors = append(errors, check()...)
    57  	}
    58  	return errors
    59  }
    60  
    61  // RunCmdChecks runs all the CmdCheck functions passed, skipping skippable commands and looks for error
    62  func RunCmdChecks(cmd *cobra.Command, cmdChecks []CmdCheck, skipCmd []string) []error {
    63  	cmdPath := cmd.CommandPath()
    64  
    65  	for _, skipCmdPath := range skipCmd {
    66  		if cmdPath == skipCmdPath {
    67  			fmt.Fprintf(os.Stdout, "---+ skipping command %s\n", cmdPath)
    68  			return []error{}
    69  		}
    70  	}
    71  
    72  	errors := []error{}
    73  
    74  	if cmd.HasSubCommands() {
    75  		for _, subCmd := range cmd.Commands() {
    76  			errors = append(errors, RunCmdChecks(subCmd, cmdChecks, skipCmd)...)
    77  		}
    78  	}
    79  
    80  	fmt.Fprintf(os.Stdout, "---+ RUNNING COMMAND CHECKS on %q\n", cmdPath)
    81  
    82  	for _, check := range cmdChecks {
    83  		if err := check(cmd); len(err) > 0 {
    84  			errors = append(errors, err...)
    85  		}
    86  	}
    87  
    88  	return errors
    89  }
    90  
    91  // CheckLongDesc checks if the long description is valid
    92  func CheckLongDesc(cmd *cobra.Command) []error {
    93  	fmt.Fprint(os.Stdout, "   ↳ checking long description\n")
    94  	cmdPath := cmd.CommandPath()
    95  	long := cmd.Long
    96  	if len(long) > 0 {
    97  		if strings.Trim(long, " \t\n") != long {
    98  			return []error{fmt.Errorf(`command %q: long description is not normalized, make sure you are calling templates.LongDesc (from pkg/cmd/templates) before assigning cmd.Long`, cmdPath)}
    99  		}
   100  	}
   101  	return nil
   102  }
   103  
   104  // CheckExamples checks if the command examples are valid
   105  func CheckExamples(cmd *cobra.Command) []error {
   106  	fmt.Fprint(os.Stdout, "   ↳ checking examples\n")
   107  	cmdPath := cmd.CommandPath()
   108  	examples := cmd.Example
   109  	errors := []error{}
   110  	if len(examples) > 0 {
   111  		for _, line := range strings.Split(examples, "\n") {
   112  			if !strings.HasPrefix(line, templates.Indentation) {
   113  				errors = append(errors, fmt.Errorf(`command %q: examples are not normalized, make sure you are calling templates.Examples (from pkg/cmd/templates) before assigning cmd.Example`, cmdPath))
   114  			}
   115  			if trimmed := strings.TrimSpace(line); strings.HasPrefix(trimmed, "//") {
   116  				errors = append(errors, fmt.Errorf(`command %q: we use # to start comments in examples instead of //`, cmdPath))
   117  			}
   118  		}
   119  	}
   120  	return errors
   121  }
   122  
   123  // CheckFlags checks if the command-line flags are valid
   124  func CheckFlags(cmd *cobra.Command) []error {
   125  	allFlagsSlice := []*pflag.Flag{}
   126  
   127  	cmd.Flags().VisitAll(func(f *pflag.Flag) {
   128  		allFlagsSlice = append(allFlagsSlice, f)
   129  	})
   130  	cmd.PersistentFlags().VisitAll(func(f *pflag.Flag) {
   131  		allFlagsSlice = append(allFlagsSlice, f)
   132  	})
   133  
   134  	fmt.Fprintf(os.Stdout, "   ↳ checking %d flags\n", len(allFlagsSlice))
   135  
   136  	errors := []error{}
   137  
   138  	// check flags long names
   139  	regex, err := regexp.Compile(`^[a-z]+[a-z\-]*$`)
   140  	if err != nil {
   141  		errors = append(errors, fmt.Errorf("command %q: unable to compile regex to check flags", cmd.CommandPath()))
   142  		return errors
   143  	}
   144  	for _, flag := range allFlagsSlice {
   145  		name := flag.Name
   146  		if !regex.MatchString(name) {
   147  			errors = append(errors, fmt.Errorf("command %q: flag name %q is invalid, long form of flag names can only contain lowercase characters or dash (must match %v)", cmd.CommandPath(), name, regex))
   148  		}
   149  	}
   150  
   151  	return errors
   152  }
   153  
   154  // CheckGlobalVarFlags checks if the global flags are valid
   155  func CheckGlobalVarFlags() []error {
   156  	fmt.Fprint(os.Stdout, "   ↳ checking flags from global vars\n")
   157  	errors := []error{}
   158  	pflag.CommandLine.VisitAll(func(f *pflag.Flag) {
   159  		errors = append(errors, fmt.Errorf("flag %q is invalid, please don't register any flag under the global variable \"CommandLine\"", f.Name))
   160  	})
   161  	return errors
   162  }
   163  

View as plain text