kubectl: Allow []byte config fields to be set by the cli
Allows []byte config fields such as 'certificate-authority-data' to be set using `kubectl config set` commands.
This commit is contained in:
		| @@ -3086,6 +3086,7 @@ _kubectl_config_set() | ||||
|     flags_with_completion=() | ||||
|     flags_completion=() | ||||
|  | ||||
|     flags+=("--set-raw-bytes=") | ||||
|     flags+=("--alsologtostderr") | ||||
|     flags+=("--api-version=") | ||||
|     flags+=("--as=") | ||||
|   | ||||
| @@ -15,7 +15,13 @@ kubectl config set \- Sets an individual value in a kubeconfig file | ||||
| .PP | ||||
| Sets an individual value in a kubeconfig file | ||||
| PROPERTY\_NAME is a dot delimited name where each token represents either a attribute name or a map key.  Map keys may not contain dots. | ||||
| PROPERTY\_VALUE is the new value you wish to set. | ||||
| 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. | ||||
|  | ||||
|  | ||||
| .SH OPTIONS | ||||
| .PP | ||||
| \fB\-\-set\-raw\-bytes\fP=false | ||||
|     When writing a []byte PROPERTY\_VALUE, write the given string directly without base64 decoding. | ||||
|  | ||||
|  | ||||
| .SH OPTIONS INHERITED FROM PARENT COMMANDS | ||||
|   | ||||
| @@ -41,12 +41,18 @@ Sets an individual value in a kubeconfig file | ||||
|  | ||||
| Sets an individual value in a kubeconfig file | ||||
| PROPERTY_NAME is a dot delimited name where each token represents either a attribute name or a map key.  Map keys may not contain dots. | ||||
| PROPERTY_VALUE is the new value you wish to set. | ||||
| 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. | ||||
|  | ||||
| ``` | ||||
| kubectl config set PROPERTY_NAME PROPERTY_VALUE | ||||
| ``` | ||||
|  | ||||
| ### Options | ||||
|  | ||||
| ``` | ||||
|       --set-raw-bytes[=false]: When writing a []byte PROPERTY_VALUE, write the given string directly without base64 decoding. | ||||
| ``` | ||||
|  | ||||
| ### Options inherited from parent commands | ||||
|  | ||||
| ``` | ||||
| @@ -79,7 +85,7 @@ kubectl config set PROPERTY_NAME PROPERTY_VALUE | ||||
|  | ||||
| * [kubectl config](kubectl_config.md)	 - config modifies kubeconfig files | ||||
|  | ||||
| ###### Auto generated by spf13/cobra on 5-Apr-2016 | ||||
| ###### Auto generated by spf13/cobra on 14-Apr-2016 | ||||
|  | ||||
| <!-- BEGIN MUNGE: GENERATED_ANALYTICS --> | ||||
| []() | ||||
|   | ||||
| @@ -270,6 +270,26 @@ runTests() { | ||||
|   # Passing no arguments to create is an error | ||||
|   ! kubectl create | ||||
|  | ||||
|   ####################### | ||||
|   # kubectl config set # | ||||
|   ####################### | ||||
|  | ||||
|   kube::log::status "Testing kubectl(${version}:config set)" | ||||
|  | ||||
|   kubectl config set-cluster test-cluster --server="https://does-not-work" | ||||
|  | ||||
|   # Get the api cert and add a comment to avoid flag parsing problems | ||||
|   cert_data=$(echo "#Comment" && cat "${TMPDIR:-/tmp/}apiserver.crt") | ||||
|  | ||||
|   kubectl config set clusters.test-cluster.certificate-authority-data "$cert_data" --set-raw-bytes | ||||
|   r_writen=$(kubectl config view --raw -o jsonpath='{.clusters[?(@.name == "test-cluster")].cluster.certificate-authority-data}') | ||||
|  | ||||
|   encoded=$(echo -n "$cert_data" | base64 --wrap=0) | ||||
|   kubectl config set clusters.test-cluster.certificate-authority-data "$encoded" | ||||
|   e_writen=$(kubectl config view --raw -o jsonpath='{.clusters[?(@.name == "test-cluster")].cluster.certificate-authority-data}') | ||||
|  | ||||
|   test "$e_writen" == "$r_writen" | ||||
|  | ||||
|   ####################### | ||||
|   # kubectl local proxy # | ||||
|   ####################### | ||||
|   | ||||
| @@ -435,6 +435,76 @@ func TestCertLeavesToken(t *testing.T) { | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestSetBytesBad(t *testing.T) { | ||||
| 	startingConfig := newRedFederalCowHammerConfig() | ||||
| 	startingConfig.Clusters["another-cluster"] = clientcmdapi.NewCluster() | ||||
|  | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set", "clusters.another-cluster.certificate-authority-data", "cadata"}, | ||||
| 		startingConfig: startingConfig, | ||||
| 		expectedConfig: startingConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestSetBytes(t *testing.T) { | ||||
| 	clusterInfoWithCAData := clientcmdapi.NewCluster() | ||||
| 	clusterInfoWithCAData.CertificateAuthorityData = []byte("cadata") | ||||
|  | ||||
| 	startingConfig := newRedFederalCowHammerConfig() | ||||
| 	startingConfig.Clusters["another-cluster"] = clientcmdapi.NewCluster() | ||||
|  | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	expectedConfig.Clusters["another-cluster"] = clusterInfoWithCAData | ||||
|  | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set", "clusters.another-cluster.certificate-authority-data", "cadata", "--set-raw-bytes"}, | ||||
| 		startingConfig: startingConfig, | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestSetBase64Bytes(t *testing.T) { | ||||
| 	clusterInfoWithCAData := clientcmdapi.NewCluster() | ||||
| 	clusterInfoWithCAData.CertificateAuthorityData = []byte("cadata") | ||||
|  | ||||
| 	startingConfig := newRedFederalCowHammerConfig() | ||||
| 	startingConfig.Clusters["another-cluster"] = clientcmdapi.NewCluster() | ||||
|  | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	expectedConfig.Clusters["another-cluster"] = clusterInfoWithCAData | ||||
|  | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"set", "clusters.another-cluster.certificate-authority-data", "Y2FkYXRh"}, | ||||
| 		startingConfig: startingConfig, | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestUnsetBytes(t *testing.T) { | ||||
| 	clusterInfoWithCAData := clientcmdapi.NewCluster() | ||||
| 	clusterInfoWithCAData.CertificateAuthorityData = []byte("cadata") | ||||
|  | ||||
| 	startingConfig := newRedFederalCowHammerConfig() | ||||
| 	startingConfig.Clusters["another-cluster"] = clusterInfoWithCAData | ||||
|  | ||||
| 	expectedConfig := newRedFederalCowHammerConfig() | ||||
| 	expectedConfig.Clusters["another-cluster"] = clientcmdapi.NewCluster() | ||||
|  | ||||
| 	test := configCommandTest{ | ||||
| 		args:           []string{"unset", "clusters.another-cluster.certificate-authority-data"}, | ||||
| 		startingConfig: startingConfig, | ||||
| 		expectedConfig: expectedConfig, | ||||
| 	} | ||||
|  | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestCAClearsInsecure(t *testing.T) { | ||||
| 	fakeCAFile, _ := ioutil.TempFile("", "ca-file") | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ limitations under the License. | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| @@ -24,6 +25,8 @@ import ( | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/spf13/cobra" | ||||
|  | ||||
| 	"k8s.io/kubernetes/pkg/util/flag" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| @@ -35,11 +38,12 @@ type setOptions struct { | ||||
| 	configAccess  ConfigAccess | ||||
| 	propertyName  string | ||||
| 	propertyValue string | ||||
| 	setRawBytes   flag.Tristate | ||||
| } | ||||
|  | ||||
| const set_long = `Sets an individual value in a kubeconfig file | ||||
| PROPERTY_NAME is a dot delimited name where each token represents either a attribute name or a map key.  Map keys may not contain dots. | ||||
| PROPERTY_VALUE is the new value you wish to set.` | ||||
| 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.` | ||||
|  | ||||
| func NewCmdConfigSet(out io.Writer, configAccess ConfigAccess) *cobra.Command { | ||||
| 	options := &setOptions{configAccess: configAccess} | ||||
| @@ -62,6 +66,8 @@ func NewCmdConfigSet(out io.Writer, configAccess ConfigAccess) *cobra.Command { | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	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" | ||||
| 	return cmd | ||||
| } | ||||
|  | ||||
| @@ -79,7 +85,13 @@ func (o setOptions) run() error { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = modifyConfig(reflect.ValueOf(config), steps, o.propertyValue, false) | ||||
|  | ||||
| 	setRawBytes := false | ||||
| 	if o.setRawBytes.Provided() { | ||||
| 		setRawBytes = o.setRawBytes.Value() | ||||
| 	} | ||||
|  | ||||
| 	err = modifyConfig(reflect.ValueOf(config), steps, o.propertyValue, false, setRawBytes) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -115,7 +127,7 @@ func (o setOptions) validate() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue string, unset bool) error { | ||||
| func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue string, unset bool, setRawBytes bool) error { | ||||
| 	currStep := steps.pop() | ||||
|  | ||||
| 	actualCurrValue := curr | ||||
| @@ -145,7 +157,7 @@ func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue stri | ||||
| 			actualCurrValue.SetMapIndex(mapKey, currMapValue) | ||||
| 		} | ||||
|  | ||||
| 		err := modifyConfig(currMapValue, steps, propertyValue, unset) | ||||
| 		err := modifyConfig(currMapValue, steps, propertyValue, unset, setRawBytes) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -159,6 +171,31 @@ func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue stri | ||||
| 		actualCurrValue.SetString(propertyValue) | ||||
| 		return nil | ||||
|  | ||||
| 	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 | ||||
|  | ||||
| 	case reflect.Bool: | ||||
| 		if steps.moreStepsRemaining() { | ||||
| 			return fmt.Errorf("can't have more steps after a bool. %v", steps) | ||||
| @@ -196,7 +233,7 @@ func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue stri | ||||
| 					return nil | ||||
| 				} | ||||
|  | ||||
| 				return modifyConfig(currFieldValue.Addr(), steps, propertyValue, unset) | ||||
| 				return modifyConfig(currFieldValue.Addr(), steps, propertyValue, unset, setRawBytes) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -72,7 +72,7 @@ func (o unsetOptions) run() error { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = modifyConfig(reflect.ValueOf(config), steps, "", true) | ||||
| 	err = modifyConfig(reflect.ValueOf(config), steps, "", true, true) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 petervo
					petervo