2014-12-17 13:03:03 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2014-12-17 13:03:03 +00:00
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 config
import (
2016-04-06 23:31:01 +00:00
"encoding/base64"
2014-12-17 13:03:03 +00:00
"errors"
"fmt"
"io"
"reflect"
"strings"
"github.com/spf13/cobra"
2016-04-06 23:31:01 +00:00
2017-01-20 18:06:17 +00:00
"k8s.io/client-go/tools/clientcmd"
2019-02-15 15:28:13 +00:00
cliflag "k8s.io/component-base/cli/flag"
2016-10-29 09:46:05 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2017-07-07 04:04:11 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
2018-10-10 18:29:30 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/templates"
2014-12-17 13:03:03 +00:00
)
type setOptions struct {
2016-04-14 18:47:19 +00:00
configAccess clientcmd . ConfigAccess
2014-12-17 13:03:03 +00:00
propertyName string
propertyValue string
2019-02-15 15:28:13 +00:00
setRawBytes cliflag . Tristate
2014-12-17 13:03:03 +00:00
}
2019-01-04 20:50:20 +00:00
var (
setLong = templates . LongDesc ( `
2016-05-20 17:49:56 +00:00
Sets an individual value in a kubeconfig file
2016-10-07 22:24:42 +00:00
2016-08-02 16:51:51 +00:00
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 .
2016-10-07 22:24:42 +00:00
2019-01-04 20:50:20 +00:00
PROPERTY_VALUE is the new value you wish to set . Binary fields such as ' certificate - authority - data ' expect a base64 encoded string unless the -- set - raw - bytes flag is used .
Specifying a attribute name that already exists will merge new fields on top of existing values . ` )
setExample = templates . Examples ( `
# Set server field on the my - cluster cluster to https : //1.2.3.4
kubectl config set clusters . my - cluster . server https : //1.2.3.4
# Set certificate - authority - data field on the my - cluster cluster .
kubectl config set clusters . my - cluster . certificate - authority - data $ ( echo "cert_data_here" | base64 - i - )
# Set cluster field in the my - context context to my - cluster .
kubectl config set contexts . my - context . cluster my - cluster
# Set client - key - data field in the cluster - admin user using -- set - raw - bytes option .
kubectl config set users . cluster - admin . client - key - data cert_data_here -- set - raw - bytes = true ` )
)
2015-03-11 17:22:08 +00:00
2018-10-30 10:35:24 +00:00
// NewCmdConfigSet returns a Command instance for 'config set' sub command
2016-04-14 18:47:19 +00:00
func NewCmdConfigSet ( out io . Writer , configAccess clientcmd . ConfigAccess ) * cobra . Command {
2015-04-10 12:54:22 +00:00
options := & setOptions { configAccess : configAccess }
2014-12-17 13:03:03 +00:00
cmd := & cobra . Command {
2018-10-05 19:59:38 +00:00
Use : "set PROPERTY_NAME PROPERTY_VALUE" ,
2017-10-11 06:26:02 +00:00
DisableFlagsInUseLine : true ,
2018-10-05 19:59:38 +00:00
Short : i18n . T ( "Sets an individual value in a kubeconfig file" ) ,
2018-10-30 10:35:24 +00:00
Long : setLong ,
2019-01-04 20:50:20 +00:00
Example : setExample ,
2014-12-17 13:03:03 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2017-03-13 16:02:00 +00:00
cmdutil . CheckErr ( options . complete ( cmd ) )
2016-10-29 09:46:05 +00:00
cmdutil . CheckErr ( options . run ( ) )
fmt . Fprintf ( out , "Property %q set.\n" , options . propertyName )
2014-12-17 13:03:03 +00:00
} ,
}
2016-04-06 23:31:01 +00:00
f := cmd . Flags ( ) . VarPF ( & options . setRawBytes , "set-raw-bytes" , "" , "When writing a []byte PROPERTY_VALUE, write the given string directly without base64 decoding." )
f . NoOptDefVal = "true"
2014-12-17 13:03:03 +00:00
return cmd
}
func ( o setOptions ) run ( ) error {
err := o . validate ( )
if err != nil {
return err
}
2015-04-10 12:54:22 +00:00
config , err := o . configAccess . GetStartingConfig ( )
2014-12-17 13:03:03 +00:00
if err != nil {
return err
}
2015-02-10 15:00:24 +00:00
steps , err := newNavigationSteps ( o . propertyName )
if err != nil {
return err
}
2016-04-06 23:31:01 +00:00
setRawBytes := false
if o . setRawBytes . Provided ( ) {
setRawBytes = o . setRawBytes . Value ( )
}
err = modifyConfig ( reflect . ValueOf ( config ) , steps , o . propertyValue , false , setRawBytes )
2014-12-17 13:03:03 +00:00
if err != nil {
return err
}
2016-04-14 18:47:19 +00:00
if err := clientcmd . ModifyConfig ( o . configAccess , * config , false ) ; err != nil {
2014-12-17 13:03:03 +00:00
return err
}
return nil
}
2017-03-13 16:02:00 +00:00
func ( o * setOptions ) complete ( cmd * cobra . Command ) error {
2014-12-17 13:03:03 +00:00
endingArgs := cmd . Flags ( ) . Args ( )
if len ( endingArgs ) != 2 {
2017-09-14 09:14:42 +00:00
return helpErrorf ( cmd , "Unexpected args: %v" , endingArgs )
2014-12-17 13:03:03 +00:00
}
o . propertyValue = endingArgs [ 1 ]
o . propertyName = endingArgs [ 0 ]
2017-03-13 16:02:00 +00:00
return nil
2014-12-17 13:03:03 +00:00
}
func ( o setOptions ) validate ( ) error {
if len ( o . propertyValue ) == 0 {
2015-09-23 08:08:42 +00:00
return errors . New ( "you cannot use set to unset a property" )
2014-12-17 13:03:03 +00:00
}
if len ( o . propertyName ) == 0 {
2015-09-23 08:08:42 +00:00
return errors . New ( "you must specify a property" )
2014-12-17 13:03:03 +00:00
}
2015-04-10 12:54:22 +00:00
return nil
2014-12-17 13:03:03 +00:00
}
2016-04-06 23:31:01 +00:00
func modifyConfig ( curr reflect . Value , steps * navigationSteps , propertyValue string , unset bool , setRawBytes bool ) error {
2015-02-10 15:00:24 +00:00
currStep := steps . pop ( )
2014-12-17 13:03:03 +00:00
actualCurrValue := curr
if curr . Kind ( ) == reflect . Ptr {
actualCurrValue = curr . Elem ( )
}
switch actualCurrValue . Kind ( ) {
case reflect . Map :
2015-02-10 15:00:24 +00:00
if ! steps . moreStepsRemaining ( ) && ! unset {
2015-09-23 08:08:42 +00:00
return fmt . Errorf ( "can't set a map to a value: %v" , actualCurrValue )
2014-12-17 13:03:03 +00:00
}
2015-02-10 15:00:24 +00:00
mapKey := reflect . ValueOf ( currStep . stepValue )
2014-12-17 13:03:03 +00:00
mapValueType := curr . Type ( ) . Elem ( ) . Elem ( )
2015-02-10 15:00:24 +00:00
if ! steps . moreStepsRemaining ( ) && unset {
2014-12-17 13:03:03 +00:00
actualCurrValue . SetMapIndex ( mapKey , reflect . Value { } )
return nil
}
currMapValue := actualCurrValue . MapIndex ( mapKey )
needToSetNewMapValue := currMapValue . Kind ( ) == reflect . Invalid
if needToSetNewMapValue {
2017-09-24 14:23:13 +00:00
if unset {
return fmt . Errorf ( "current map key `%v` is invalid" , mapKey . Interface ( ) )
}
2015-06-29 18:39:48 +00:00
currMapValue = reflect . New ( mapValueType . Elem ( ) ) . Elem ( ) . Addr ( )
2014-12-17 13:03:03 +00:00
actualCurrValue . SetMapIndex ( mapKey , currMapValue )
}
2016-04-06 23:31:01 +00:00
err := modifyConfig ( currMapValue , steps , propertyValue , unset , setRawBytes )
2014-12-17 13:03:03 +00:00
if err != nil {
return err
}
return nil
case reflect . String :
if steps . moreStepsRemaining ( ) {
2015-09-23 08:08:42 +00:00
return fmt . Errorf ( "can't have more steps after a string. %v" , steps )
2014-12-17 13:03:03 +00:00
}
actualCurrValue . SetString ( propertyValue )
return nil
2016-04-06 23:31:01 +00:00
case reflect . Slice :
if steps . moreStepsRemaining ( ) {
return fmt . Errorf ( "can't have more steps after bytes. %v" , steps )
}
innerKind := actualCurrValue . Type ( ) . Elem ( ) . Kind ( )
if innerKind != reflect . Uint8 {
return fmt . Errorf ( "unrecognized slice type. %v" , innerKind )
}
if unset {
actualCurrValue . Set ( reflect . Zero ( actualCurrValue . Type ( ) ) )
return nil
}
if setRawBytes {
actualCurrValue . SetBytes ( [ ] byte ( propertyValue ) )
} else {
val , err := base64 . StdEncoding . DecodeString ( propertyValue )
if err != nil {
return fmt . Errorf ( "error decoding input value: %v" , err )
}
actualCurrValue . SetBytes ( val )
}
return nil
2014-12-17 13:03:03 +00:00
case reflect . Bool :
if steps . moreStepsRemaining ( ) {
2015-09-23 08:08:42 +00:00
return fmt . Errorf ( "can't have more steps after a bool. %v" , steps )
2014-12-17 13:03:03 +00:00
}
boolValue , err := toBool ( propertyValue )
if err != nil {
return err
}
actualCurrValue . SetBool ( boolValue )
return nil
case reflect . Struct :
for fieldIndex := 0 ; fieldIndex < actualCurrValue . NumField ( ) ; fieldIndex ++ {
currFieldValue := actualCurrValue . Field ( fieldIndex )
currFieldType := actualCurrValue . Type ( ) . Field ( fieldIndex )
currYamlTag := currFieldType . Tag . Get ( "json" )
currFieldTypeYamlName := strings . Split ( currYamlTag , "," ) [ 0 ]
2015-02-10 15:00:24 +00:00
if currFieldTypeYamlName == currStep . stepValue {
2014-12-17 13:03:03 +00:00
thisMapHasNoValue := ( currFieldValue . Kind ( ) == reflect . Map && currFieldValue . IsNil ( ) )
if thisMapHasNoValue {
newValue := reflect . MakeMap ( currFieldValue . Type ( ) )
currFieldValue . Set ( newValue )
2015-02-10 15:00:24 +00:00
if ! steps . moreStepsRemaining ( ) && unset {
2014-12-17 13:03:03 +00:00
return nil
}
}
2015-02-10 15:00:24 +00:00
if ! steps . moreStepsRemaining ( ) && unset {
2014-12-17 13:03:03 +00:00
// 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
newValue := reflect . New ( currFieldValue . Type ( ) ) . Elem ( )
currFieldValue . Set ( newValue )
return nil
}
2016-04-06 23:31:01 +00:00
return modifyConfig ( currFieldValue . Addr ( ) , steps , propertyValue , unset , setRawBytes )
2014-12-17 13:03:03 +00:00
}
}
2015-09-23 08:08:42 +00:00
return fmt . Errorf ( "unable to locate path %#v under %v" , currStep , actualCurrValue )
2014-12-17 13:03:03 +00:00
}
2015-09-23 08:08:42 +00:00
panic ( fmt . Errorf ( "unrecognized type: %v" , actualCurrValue ) )
2014-12-17 13:03:03 +00:00
}