diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 3125cd294ec..77b23f6af05 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -3086,6 +3086,7 @@ _kubectl_config_set() flags_with_completion=() flags_completion=() + flags+=("--set-raw-bytes=") flags+=("--alsologtostderr") flags+=("--api-version=") flags+=("--as=") diff --git a/docs/man/man1/kubectl-config-set.1 b/docs/man/man1/kubectl-config-set.1 index 4b88a433f4b..42d0e138dcc 100644 --- a/docs/man/man1/kubectl-config-set.1 +++ b/docs/man/man1/kubectl-config-set.1 @@ -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 diff --git a/docs/user-guide/kubectl/kubectl_config_set.md b/docs/user-guide/kubectl/kubectl_config_set.md index 9e9a99345ac..743acc2e728 100644 --- a/docs/user-guide/kubectl/kubectl_config_set.md +++ b/docs/user-guide/kubectl/kubectl_config_set.md @@ -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 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_config_set.md?pixel)]() diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 3e93450367e..be7e56e8e98 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -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 # ####################### diff --git a/pkg/kubectl/cmd/config/config_test.go b/pkg/kubectl/cmd/config/config_test.go index 73237ba34ea..3eca3d53c68 100644 --- a/pkg/kubectl/cmd/config/config_test.go +++ b/pkg/kubectl/cmd/config/config_test.go @@ -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") diff --git a/pkg/kubectl/cmd/config/set.go b/pkg/kubectl/cmd/config/set.go index c175a23c2f1..bc7fcae7e10 100644 --- a/pkg/kubectl/cmd/config/set.go +++ b/pkg/kubectl/cmd/config/set.go @@ -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) } } diff --git a/pkg/kubectl/cmd/config/unset.go b/pkg/kubectl/cmd/config/unset.go index 555309ccbe5..9e77c447544 100644 --- a/pkg/kubectl/cmd/config/unset.go +++ b/pkg/kubectl/cmd/config/unset.go @@ -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 }