set.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. Copyright 2014 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package config
  14. import (
  15. "encoding/base64"
  16. "errors"
  17. "fmt"
  18. "io"
  19. "reflect"
  20. "strings"
  21. "github.com/spf13/cobra"
  22. "k8s.io/client-go/tools/clientcmd"
  23. cliflag "k8s.io/component-base/cli/flag"
  24. cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  25. "k8s.io/kubernetes/pkg/kubectl/util/i18n"
  26. "k8s.io/kubernetes/pkg/kubectl/util/templates"
  27. )
  28. type setOptions struct {
  29. configAccess clientcmd.ConfigAccess
  30. propertyName string
  31. propertyValue string
  32. setRawBytes cliflag.Tristate
  33. }
  34. var (
  35. setLong = templates.LongDesc(`
  36. Sets an individual value in a kubeconfig file
  37. 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.
  38. 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.
  39. Specifying a attribute name that already exists will merge new fields on top of existing values.`)
  40. setExample = templates.Examples(`
  41. # Set server field on the my-cluster cluster to https://1.2.3.4
  42. kubectl config set clusters.my-cluster.server https://1.2.3.4
  43. # Set certificate-authority-data field on the my-cluster cluster.
  44. kubectl config set clusters.my-cluster.certificate-authority-data $(echo "cert_data_here" | base64 -i -)
  45. # Set cluster field in the my-context context to my-cluster.
  46. kubectl config set contexts.my-context.cluster my-cluster
  47. # Set client-key-data field in the cluster-admin user using --set-raw-bytes option.
  48. kubectl config set users.cluster-admin.client-key-data cert_data_here --set-raw-bytes=true`)
  49. )
  50. // NewCmdConfigSet returns a Command instance for 'config set' sub command
  51. func NewCmdConfigSet(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
  52. options := &setOptions{configAccess: configAccess}
  53. cmd := &cobra.Command{
  54. Use: "set PROPERTY_NAME PROPERTY_VALUE",
  55. DisableFlagsInUseLine: true,
  56. Short: i18n.T("Sets an individual value in a kubeconfig file"),
  57. Long: setLong,
  58. Example: setExample,
  59. Run: func(cmd *cobra.Command, args []string) {
  60. cmdutil.CheckErr(options.complete(cmd))
  61. cmdutil.CheckErr(options.run())
  62. fmt.Fprintf(out, "Property %q set.\n", options.propertyName)
  63. },
  64. }
  65. f := cmd.Flags().VarPF(&options.setRawBytes, "set-raw-bytes", "", "When writing a []byte PROPERTY_VALUE, write the given string directly without base64 decoding.")
  66. f.NoOptDefVal = "true"
  67. return cmd
  68. }
  69. func (o setOptions) run() error {
  70. err := o.validate()
  71. if err != nil {
  72. return err
  73. }
  74. config, err := o.configAccess.GetStartingConfig()
  75. if err != nil {
  76. return err
  77. }
  78. steps, err := newNavigationSteps(o.propertyName)
  79. if err != nil {
  80. return err
  81. }
  82. setRawBytes := false
  83. if o.setRawBytes.Provided() {
  84. setRawBytes = o.setRawBytes.Value()
  85. }
  86. err = modifyConfig(reflect.ValueOf(config), steps, o.propertyValue, false, setRawBytes)
  87. if err != nil {
  88. return err
  89. }
  90. if err := clientcmd.ModifyConfig(o.configAccess, *config, false); err != nil {
  91. return err
  92. }
  93. return nil
  94. }
  95. func (o *setOptions) complete(cmd *cobra.Command) error {
  96. endingArgs := cmd.Flags().Args()
  97. if len(endingArgs) != 2 {
  98. return helpErrorf(cmd, "Unexpected args: %v", endingArgs)
  99. }
  100. o.propertyValue = endingArgs[1]
  101. o.propertyName = endingArgs[0]
  102. return nil
  103. }
  104. func (o setOptions) validate() error {
  105. if len(o.propertyValue) == 0 {
  106. return errors.New("you cannot use set to unset a property")
  107. }
  108. if len(o.propertyName) == 0 {
  109. return errors.New("you must specify a property")
  110. }
  111. return nil
  112. }
  113. func modifyConfig(curr reflect.Value, steps *navigationSteps, propertyValue string, unset bool, setRawBytes bool) error {
  114. currStep := steps.pop()
  115. actualCurrValue := curr
  116. if curr.Kind() == reflect.Ptr {
  117. actualCurrValue = curr.Elem()
  118. }
  119. switch actualCurrValue.Kind() {
  120. case reflect.Map:
  121. if !steps.moreStepsRemaining() && !unset {
  122. return fmt.Errorf("can't set a map to a value: %v", actualCurrValue)
  123. }
  124. mapKey := reflect.ValueOf(currStep.stepValue)
  125. mapValueType := curr.Type().Elem().Elem()
  126. if !steps.moreStepsRemaining() && unset {
  127. actualCurrValue.SetMapIndex(mapKey, reflect.Value{})
  128. return nil
  129. }
  130. currMapValue := actualCurrValue.MapIndex(mapKey)
  131. needToSetNewMapValue := currMapValue.Kind() == reflect.Invalid
  132. if needToSetNewMapValue {
  133. if unset {
  134. return fmt.Errorf("current map key `%v` is invalid", mapKey.Interface())
  135. }
  136. currMapValue = reflect.New(mapValueType.Elem()).Elem().Addr()
  137. actualCurrValue.SetMapIndex(mapKey, currMapValue)
  138. }
  139. err := modifyConfig(currMapValue, steps, propertyValue, unset, setRawBytes)
  140. if err != nil {
  141. return err
  142. }
  143. return nil
  144. case reflect.String:
  145. if steps.moreStepsRemaining() {
  146. return fmt.Errorf("can't have more steps after a string. %v", steps)
  147. }
  148. actualCurrValue.SetString(propertyValue)
  149. return nil
  150. case reflect.Slice:
  151. if steps.moreStepsRemaining() {
  152. return fmt.Errorf("can't have more steps after bytes. %v", steps)
  153. }
  154. innerKind := actualCurrValue.Type().Elem().Kind()
  155. if innerKind != reflect.Uint8 {
  156. return fmt.Errorf("unrecognized slice type. %v", innerKind)
  157. }
  158. if unset {
  159. actualCurrValue.Set(reflect.Zero(actualCurrValue.Type()))
  160. return nil
  161. }
  162. if setRawBytes {
  163. actualCurrValue.SetBytes([]byte(propertyValue))
  164. } else {
  165. val, err := base64.StdEncoding.DecodeString(propertyValue)
  166. if err != nil {
  167. return fmt.Errorf("error decoding input value: %v", err)
  168. }
  169. actualCurrValue.SetBytes(val)
  170. }
  171. return nil
  172. case reflect.Bool:
  173. if steps.moreStepsRemaining() {
  174. return fmt.Errorf("can't have more steps after a bool. %v", steps)
  175. }
  176. boolValue, err := toBool(propertyValue)
  177. if err != nil {
  178. return err
  179. }
  180. actualCurrValue.SetBool(boolValue)
  181. return nil
  182. case reflect.Struct:
  183. for fieldIndex := 0; fieldIndex < actualCurrValue.NumField(); fieldIndex++ {
  184. currFieldValue := actualCurrValue.Field(fieldIndex)
  185. currFieldType := actualCurrValue.Type().Field(fieldIndex)
  186. currYamlTag := currFieldType.Tag.Get("json")
  187. currFieldTypeYamlName := strings.Split(currYamlTag, ",")[0]
  188. if currFieldTypeYamlName == currStep.stepValue {
  189. thisMapHasNoValue := (currFieldValue.Kind() == reflect.Map && currFieldValue.IsNil())
  190. if thisMapHasNoValue {
  191. newValue := reflect.MakeMap(currFieldValue.Type())
  192. currFieldValue.Set(newValue)
  193. if !steps.moreStepsRemaining() && unset {
  194. return nil
  195. }
  196. }
  197. if !steps.moreStepsRemaining() && unset {
  198. // 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
  199. newValue := reflect.New(currFieldValue.Type()).Elem()
  200. currFieldValue.Set(newValue)
  201. return nil
  202. }
  203. return modifyConfig(currFieldValue.Addr(), steps, propertyValue, unset, setRawBytes)
  204. }
  205. }
  206. return fmt.Errorf("unable to locate path %#v under %v", currStep, actualCurrValue)
  207. }
  208. panic(fmt.Errorf("unrecognized type: %v", actualCurrValue))
  209. }