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.
pull/6/head
petervo 2016-04-06 16:31:01 -07:00
parent 2e9bcb8311
commit 4f9d3ace5d
7 changed files with 149 additions and 9 deletions

View File

@ -3086,6 +3086,7 @@ _kubectl_config_set()
flags_with_completion=() flags_with_completion=()
flags_completion=() flags_completion=()
flags+=("--set-raw-bytes=")
flags+=("--alsologtostderr") flags+=("--alsologtostderr")
flags+=("--api-version=") flags+=("--api-version=")
flags+=("--as=") flags+=("--as=")

View File

@ -15,7 +15,13 @@ kubectl config set \- Sets an individual value in a kubeconfig file
.PP .PP
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\_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 .SH OPTIONS INHERITED FROM PARENT COMMANDS

View File

@ -41,12 +41,18 @@ Sets an individual value in a kubeconfig file
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_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 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 ### 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 * [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 --> <!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
[![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_config_set.md?pixel)]() [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_config_set.md?pixel)]()

View File

@ -270,6 +270,26 @@ runTests() {
# Passing no arguments to create is an error # Passing no arguments to create is an error
! kubectl create ! 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 # # kubectl local proxy #
####################### #######################

View File

@ -435,6 +435,76 @@ func TestCertLeavesToken(t *testing.T) {
test.run(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) { func TestCAClearsInsecure(t *testing.T) {
fakeCAFile, _ := ioutil.TempFile("", "ca-file") fakeCAFile, _ := ioutil.TempFile("", "ca-file")

View File

@ -17,6 +17,7 @@ limitations under the License.
package config package config
import ( import (
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -24,6 +25,8 @@ import (
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/util/flag"
) )
const ( const (
@ -35,11 +38,12 @@ type setOptions struct {
configAccess ConfigAccess configAccess ConfigAccess
propertyName string propertyName string
propertyValue string propertyValue string
setRawBytes flag.Tristate
} }
const set_long = `Sets an individual value in a kubeconfig file 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_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 { func NewCmdConfigSet(out io.Writer, configAccess ConfigAccess) *cobra.Command {
options := &setOptions{configAccess: configAccess} 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 return cmd
} }
@ -79,7 +85,13 @@ func (o setOptions) run() error {
if err != nil { if err != nil {
return err 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 { if err != nil {
return err return err
} }
@ -115,7 +127,7 @@ func (o setOptions) validate() error {
return nil 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() currStep := steps.pop()
actualCurrValue := curr actualCurrValue := curr
@ -145,7 +157,7 @@ func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue stri
actualCurrValue.SetMapIndex(mapKey, currMapValue) actualCurrValue.SetMapIndex(mapKey, currMapValue)
} }
err := modifyConfig(currMapValue, steps, propertyValue, unset) err := modifyConfig(currMapValue, steps, propertyValue, unset, setRawBytes)
if err != nil { if err != nil {
return err return err
} }
@ -159,6 +171,31 @@ func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue stri
actualCurrValue.SetString(propertyValue) actualCurrValue.SetString(propertyValue)
return nil 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: case reflect.Bool:
if steps.moreStepsRemaining() { if steps.moreStepsRemaining() {
return fmt.Errorf("can't have more steps after a bool. %v", steps) 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 nil
} }
return modifyConfig(currFieldValue.Addr(), steps, propertyValue, unset) return modifyConfig(currFieldValue.Addr(), steps, propertyValue, unset, setRawBytes)
} }
} }

View File

@ -72,7 +72,7 @@ func (o unsetOptions) run() error {
if err != nil { if err != nil {
return err return err
} }
err = modifyConfig(reflect.ValueOf(config), steps, "", true) err = modifyConfig(reflect.ValueOf(config), steps, "", true, true)
if err != nil { if err != nil {
return err return err
} }