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
2016-04-14 18:47:19 +00:00
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
2016-10-07 22:24:42 +00:00
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
2016-04-06 23:31:01 +00:00
"k8s.io/kubernetes/pkg/util/flag"
2014-12-17 13:03:03 +00:00
)
const (
cannotHaveStepsAfterError = "Cannot have steps after %v. %v are remaining"
additionStepRequiredUnlessUnsettingError = "Must have additional steps after %v unless you are unsetting it"
)
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
2016-04-06 23:31:01 +00:00
setRawBytes flag . Tristate
2014-12-17 13:03:03 +00:00
}
2016-10-07 22:24:42 +00:00
var set_long = 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
2016-05-20 17:49:56 +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 . ` )
2015-03-11 17:22:08 +00:00
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 {
2015-03-11 17:22:08 +00:00
Use : "set PROPERTY_NAME PROPERTY_VALUE" ,
2015-04-08 14:32:32 +00:00
Short : "Sets an individual value in a kubeconfig file" ,
2015-03-11 17:22:08 +00:00
Long : set_long ,
2014-12-17 13:03:03 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
if ! options . complete ( cmd ) {
return
}
err := options . run ( )
if err != nil {
2015-09-23 08:08:42 +00:00
fmt . Fprintf ( out , "%v\n" , err )
} else {
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
}
func ( o * setOptions ) complete ( cmd * cobra . Command ) bool {
endingArgs := cmd . Flags ( ) . Args ( )
if len ( endingArgs ) != 2 {
cmd . Help ( )
return false
}
o . propertyValue = endingArgs [ 1 ]
o . propertyName = endingArgs [ 0 ]
return true
}
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 {
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
}