...

Source file src/k8s.io/kubectl/pkg/cmd/config/set.go

Documentation: k8s.io/kubectl/pkg/cmd/config

     1  /*
     2  Copyright 2014 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 config
    18  
    19  import (
    20  	"encoding/base64"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"reflect"
    25  	"strings"
    26  
    27  	"github.com/spf13/cobra"
    28  
    29  	"k8s.io/client-go/tools/clientcmd"
    30  	cliflag "k8s.io/component-base/cli/flag"
    31  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    32  	"k8s.io/kubectl/pkg/util/i18n"
    33  	"k8s.io/kubectl/pkg/util/templates"
    34  )
    35  
    36  type setOptions struct {
    37  	configAccess  clientcmd.ConfigAccess
    38  	propertyName  string
    39  	propertyValue string
    40  	setRawBytes   cliflag.Tristate
    41  }
    42  
    43  var (
    44  	setLong = templates.LongDesc(i18n.T(`
    45  	Set an individual value in a kubeconfig file.
    46  
    47  	PROPERTY_NAME is a dot delimited name where each token represents either an attribute name or a map key.  Map keys may not contain dots.
    48  
    49  	PROPERTY_VALUE is the new value you want to set. Binary fields such as 'certificate-authority-data' expect a base64 encoded string unless the --set-raw-bytes flag is used.
    50  
    51  	Specifying an attribute name that already exists will merge new fields on top of existing values.`))
    52  
    53  	setExample = templates.Examples(`
    54  	# Set the server field on the my-cluster cluster to https://1.2.3.4
    55  	kubectl config set clusters.my-cluster.server https://1.2.3.4
    56  
    57  	# Set the certificate-authority-data field on the my-cluster cluster
    58  	kubectl config set clusters.my-cluster.certificate-authority-data $(echo "cert_data_here" | base64 -i -)
    59  
    60  	# Set the cluster field in the my-context context to my-cluster
    61  	kubectl config set contexts.my-context.cluster my-cluster
    62  
    63  	# Set the client-key-data field in the cluster-admin user using --set-raw-bytes option
    64  	kubectl config set users.cluster-admin.client-key-data cert_data_here --set-raw-bytes=true`)
    65  )
    66  
    67  // NewCmdConfigSet returns a Command instance for 'config set' sub command
    68  func NewCmdConfigSet(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
    69  	options := &setOptions{configAccess: configAccess}
    70  
    71  	cmd := &cobra.Command{
    72  		Use:                   "set PROPERTY_NAME PROPERTY_VALUE",
    73  		DisableFlagsInUseLine: true,
    74  		Short:                 i18n.T("Set an individual value in a kubeconfig file"),
    75  		Long:                  setLong,
    76  		Example:               setExample,
    77  		Run: func(cmd *cobra.Command, args []string) {
    78  			cmdutil.CheckErr(options.complete(cmd))
    79  			cmdutil.CheckErr(options.run())
    80  			fmt.Fprintf(out, "Property %q set.\n", options.propertyName)
    81  		},
    82  	}
    83  
    84  	f := cmd.Flags().VarPF(&options.setRawBytes, "set-raw-bytes", "", "When writing a []byte PROPERTY_VALUE, write the given string directly without base64 decoding.")
    85  	f.NoOptDefVal = "true"
    86  	return cmd
    87  }
    88  
    89  func (o setOptions) run() error {
    90  	err := o.validate()
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	config, err := o.configAccess.GetStartingConfig()
    96  	if err != nil {
    97  		return err
    98  	}
    99  	steps, err := newNavigationSteps(o.propertyName)
   100  	if err != nil {
   101  		return err
   102  	}
   103  
   104  	setRawBytes := false
   105  	if o.setRawBytes.Provided() {
   106  		setRawBytes = o.setRawBytes.Value()
   107  	}
   108  
   109  	err = modifyConfig(reflect.ValueOf(config), steps, o.propertyValue, false, setRawBytes)
   110  	if err != nil {
   111  		return err
   112  	}
   113  
   114  	if err := clientcmd.ModifyConfig(o.configAccess, *config, false); err != nil {
   115  		return err
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  func (o *setOptions) complete(cmd *cobra.Command) error {
   122  	endingArgs := cmd.Flags().Args()
   123  	if len(endingArgs) != 2 {
   124  		return helpErrorf(cmd, "Unexpected args: %v", endingArgs)
   125  	}
   126  
   127  	o.propertyValue = endingArgs[1]
   128  	o.propertyName = endingArgs[0]
   129  	return nil
   130  }
   131  
   132  func (o setOptions) validate() error {
   133  	if len(o.propertyValue) == 0 {
   134  		return errors.New("you cannot use set to unset a property")
   135  	}
   136  
   137  	if len(o.propertyName) == 0 {
   138  		return errors.New("you must specify a property")
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue string, unset bool, setRawBytes bool) error {
   145  	currStep := steps.pop()
   146  
   147  	actualCurrValue := curr
   148  	if curr.Kind() == reflect.Pointer {
   149  		actualCurrValue = curr.Elem()
   150  	}
   151  
   152  	switch actualCurrValue.Kind() {
   153  	case reflect.Map:
   154  		if !steps.moreStepsRemaining() && !unset {
   155  			return fmt.Errorf("can't set a map to a value: %v", actualCurrValue)
   156  		}
   157  
   158  		mapKey := reflect.ValueOf(currStep.stepValue)
   159  		mapValueType := curr.Type().Elem().Elem()
   160  
   161  		if !steps.moreStepsRemaining() && unset {
   162  			actualCurrValue.SetMapIndex(mapKey, reflect.Value{})
   163  			return nil
   164  		}
   165  
   166  		currMapValue := actualCurrValue.MapIndex(mapKey)
   167  
   168  		needToSetNewMapValue := currMapValue.Kind() == reflect.Invalid
   169  		if needToSetNewMapValue {
   170  			if unset {
   171  				return fmt.Errorf("current map key `%v` is invalid", mapKey.Interface())
   172  			}
   173  			currMapValue = reflect.New(mapValueType.Elem()).Elem().Addr()
   174  			actualCurrValue.SetMapIndex(mapKey, currMapValue)
   175  		}
   176  
   177  		err := modifyConfig(currMapValue, steps, propertyValue, unset, setRawBytes)
   178  		if err != nil {
   179  			return err
   180  		}
   181  
   182  		return nil
   183  
   184  	case reflect.String:
   185  		if steps.moreStepsRemaining() {
   186  			return fmt.Errorf("can't have more steps after a string. %v", steps)
   187  		}
   188  		actualCurrValue.SetString(propertyValue)
   189  		return nil
   190  
   191  	case reflect.Slice:
   192  		if steps.moreStepsRemaining() {
   193  			return fmt.Errorf("can't have more steps after bytes. %v", steps)
   194  		}
   195  		innerKind := actualCurrValue.Type().Elem().Kind()
   196  		if innerKind != reflect.Uint8 {
   197  			return fmt.Errorf("unrecognized slice type. %v", innerKind)
   198  		}
   199  
   200  		if unset {
   201  			actualCurrValue.Set(reflect.Zero(actualCurrValue.Type()))
   202  			return nil
   203  		}
   204  
   205  		if setRawBytes {
   206  			actualCurrValue.SetBytes([]byte(propertyValue))
   207  		} else {
   208  			val, err := base64.StdEncoding.DecodeString(propertyValue)
   209  			if err != nil {
   210  				return fmt.Errorf("error decoding input value: %v", err)
   211  			}
   212  			actualCurrValue.SetBytes(val)
   213  		}
   214  		return nil
   215  
   216  	case reflect.Bool:
   217  		if steps.moreStepsRemaining() {
   218  			return fmt.Errorf("can't have more steps after a bool. %v", steps)
   219  		}
   220  		boolValue, err := toBool(propertyValue)
   221  		if err != nil {
   222  			return err
   223  		}
   224  		actualCurrValue.SetBool(boolValue)
   225  		return nil
   226  
   227  	case reflect.Struct:
   228  		for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ {
   229  			currFieldValue := actualCurrValue.Field(fieldIndex)
   230  			currFieldType := actualCurrValue.Type().Field(fieldIndex)
   231  			currYamlTag := currFieldType.Tag.Get("json")
   232  			currFieldTypeYamlName := strings.Split(currYamlTag, ",")[0]
   233  
   234  			if currFieldTypeYamlName == currStep.stepValue {
   235  				thisMapHasNoValue := (currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil())
   236  
   237  				if thisMapHasNoValue {
   238  					newValue := reflect.MakeMap(currFieldValue.Type())
   239  					currFieldValue.Set(newValue)
   240  
   241  					if !steps.moreStepsRemaining() && unset {
   242  						return nil
   243  					}
   244  				}
   245  
   246  				if !steps.moreStepsRemaining() && unset {
   247  					// if we're supposed to unset the value or if the value is a map that doesn't exist, create a new value and overwrite
   248  					newValue := reflect.New(currFieldValue.Type()).Elem()
   249  					currFieldValue.Set(newValue)
   250  					return nil
   251  				}
   252  
   253  				return modifyConfig(currFieldValue.Addr(), steps, propertyValue, unset, setRawBytes)
   254  			}
   255  		}
   256  
   257  		return fmt.Errorf("unable to locate path %#v under %v", currStep, actualCurrValue)
   258  
   259  	}
   260  
   261  	panic(fmt.Errorf("unrecognized type: %v", actualCurrValue))
   262  }
   263  

View as plain text