...

Source file src/k8s.io/kubectl/pkg/generate/generate.go

Documentation: k8s.io/kubectl/pkg/generate

     1  /*
     2  Copyright 2018 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 generate
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strconv"
    23  	"strings"
    24  
    25  	"github.com/spf13/cobra"
    26  	"github.com/spf13/pflag"
    27  
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    30  )
    31  
    32  // GeneratorFunc returns the generators for the provided command
    33  type GeneratorFunc func(cmdName string) map[string]Generator
    34  
    35  // GeneratorParam is a parameter for a generator
    36  // TODO: facilitate structured json generator input schemes
    37  type GeneratorParam struct {
    38  	Name     string
    39  	Required bool
    40  }
    41  
    42  // Generator is an interface for things that can generate API objects from input
    43  // parameters. One example is the "expose" generator that is capable of exposing
    44  // new replication controllers and services, among other things.
    45  type Generator interface {
    46  	// Generate creates an API object given a set of parameters
    47  	Generate(params map[string]interface{}) (runtime.Object, error)
    48  	// ParamNames returns the list of parameters that this generator uses
    49  	ParamNames() []GeneratorParam
    50  }
    51  
    52  // StructuredGenerator is an interface for things that can generate API objects not using parameter injection
    53  type StructuredGenerator interface {
    54  	// StructuredGenerator creates an API object using pre-configured parameters
    55  	StructuredGenerate() (runtime.Object, error)
    56  }
    57  
    58  func IsZero(i interface{}) bool {
    59  	if i == nil {
    60  		return true
    61  	}
    62  	return reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface())
    63  }
    64  
    65  // ValidateParams ensures that all required params are present in the params map
    66  func ValidateParams(paramSpec []GeneratorParam, params map[string]interface{}) error {
    67  	allErrs := []error{}
    68  	for ix := range paramSpec {
    69  		if paramSpec[ix].Required {
    70  			value, found := params[paramSpec[ix].Name]
    71  			if !found || IsZero(value) {
    72  				allErrs = append(allErrs, fmt.Errorf("Parameter: %s is required", paramSpec[ix].Name))
    73  			}
    74  		}
    75  	}
    76  	return utilerrors.NewAggregate(allErrs)
    77  }
    78  
    79  // AnnotateFlags annotates all flags that are used by generators.
    80  func AnnotateFlags(cmd *cobra.Command, generators map[string]Generator) {
    81  	// Iterate over all generators and mark any flags used by them.
    82  	for name, generator := range generators {
    83  		generatorParams := map[string]struct{}{}
    84  		for _, param := range generator.ParamNames() {
    85  			generatorParams[param.Name] = struct{}{}
    86  		}
    87  
    88  		cmd.Flags().VisitAll(func(flag *pflag.Flag) {
    89  			if _, found := generatorParams[flag.Name]; !found {
    90  				// This flag is not used by the current generator
    91  				// so skip it.
    92  				return
    93  			}
    94  			if flag.Annotations == nil {
    95  				flag.Annotations = map[string][]string{}
    96  			}
    97  			if annotations := flag.Annotations["generator"]; annotations == nil {
    98  				flag.Annotations["generator"] = []string{}
    99  			}
   100  			flag.Annotations["generator"] = append(flag.Annotations["generator"], name)
   101  		})
   102  	}
   103  }
   104  
   105  // EnsureFlagsValid ensures that no invalid flags are being used against a
   106  func EnsureFlagsValid(cmd *cobra.Command, generators map[string]Generator, generatorInUse string) error {
   107  	AnnotateFlags(cmd, generators)
   108  
   109  	allErrs := []error{}
   110  	cmd.Flags().VisitAll(func(flag *pflag.Flag) {
   111  		// If the flag hasn't changed, don't validate it.
   112  		if !flag.Changed {
   113  			return
   114  		}
   115  		// Look into the flag annotations for the generators that can use it.
   116  		if annotations := flag.Annotations["generator"]; len(annotations) > 0 {
   117  			annotationMap := map[string]struct{}{}
   118  			for _, ann := range annotations {
   119  				annotationMap[ann] = struct{}{}
   120  			}
   121  			// If the current generator is not annotated, then this flag shouldn't
   122  			// be used with it.
   123  			if _, found := annotationMap[generatorInUse]; !found {
   124  				allErrs = append(allErrs, fmt.Errorf("cannot use --%s with --generator=%s", flag.Name, generatorInUse))
   125  			}
   126  		}
   127  	})
   128  	return utilerrors.NewAggregate(allErrs)
   129  }
   130  
   131  // MakeParams is a utility that creates generator parameters from a command line
   132  func MakeParams(cmd *cobra.Command, params []GeneratorParam) map[string]interface{} {
   133  	result := map[string]interface{}{}
   134  	for ix := range params {
   135  		f := cmd.Flags().Lookup(params[ix].Name)
   136  		if f != nil {
   137  			result[params[ix].Name] = f.Value.String()
   138  		}
   139  	}
   140  	return result
   141  }
   142  
   143  func MakeProtocols(protocols map[string]string) string {
   144  	out := []string{}
   145  	for key, value := range protocols {
   146  		out = append(out, fmt.Sprintf("%s/%s", key, value))
   147  	}
   148  	return strings.Join(out, ",")
   149  }
   150  
   151  func ParseProtocols(protocols interface{}) (map[string]string, error) {
   152  	protocolsString, isString := protocols.(string)
   153  	if !isString {
   154  		return nil, fmt.Errorf("expected string, found %v", protocols)
   155  	}
   156  	if len(protocolsString) == 0 {
   157  		return nil, fmt.Errorf("no protocols passed")
   158  	}
   159  	portProtocolMap := map[string]string{}
   160  	protocolsSlice := strings.Split(protocolsString, ",")
   161  	for ix := range protocolsSlice {
   162  		portProtocol := strings.Split(protocolsSlice[ix], "/")
   163  		if len(portProtocol) != 2 {
   164  			return nil, fmt.Errorf("unexpected port protocol mapping: %s", protocolsSlice[ix])
   165  		}
   166  		if len(portProtocol[0]) == 0 {
   167  			return nil, fmt.Errorf("unexpected empty port")
   168  		}
   169  		if len(portProtocol[1]) == 0 {
   170  			return nil, fmt.Errorf("unexpected empty protocol")
   171  		}
   172  		portProtocolMap[portProtocol[0]] = portProtocol[1]
   173  	}
   174  	return portProtocolMap, nil
   175  }
   176  
   177  // ParseLabels turns a string representation of a label set into a map[string]string
   178  func ParseLabels(labelSpec interface{}) (map[string]string, error) {
   179  	labelString, isString := labelSpec.(string)
   180  	if !isString {
   181  		return nil, fmt.Errorf("expected string, found %v", labelSpec)
   182  	}
   183  	if len(labelString) == 0 {
   184  		return nil, fmt.Errorf("no label spec passed")
   185  	}
   186  	labels := map[string]string{}
   187  	labelSpecs := strings.Split(labelString, ",")
   188  	for ix := range labelSpecs {
   189  		labelSpec := strings.Split(labelSpecs[ix], "=")
   190  		if len(labelSpec) != 2 {
   191  			return nil, fmt.Errorf("unexpected label spec: %s", labelSpecs[ix])
   192  		}
   193  		if len(labelSpec[0]) == 0 {
   194  			return nil, fmt.Errorf("unexpected empty label key")
   195  		}
   196  		labels[labelSpec[0]] = labelSpec[1]
   197  	}
   198  	return labels, nil
   199  }
   200  
   201  func GetBool(params map[string]string, key string, defValue bool) (bool, error) {
   202  	if val, found := params[key]; !found {
   203  		return defValue, nil
   204  	} else {
   205  		return strconv.ParseBool(val)
   206  	}
   207  }
   208  

View as plain text