set_serviceaccount.go 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /*
  2. Copyright 2017 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 set
  14. import (
  15. "errors"
  16. "fmt"
  17. "github.com/spf13/cobra"
  18. "k8s.io/klog"
  19. "k8s.io/api/core/v1"
  20. "k8s.io/apimachinery/pkg/runtime"
  21. "k8s.io/apimachinery/pkg/types"
  22. utilerrors "k8s.io/apimachinery/pkg/util/errors"
  23. "k8s.io/cli-runtime/pkg/genericclioptions"
  24. "k8s.io/cli-runtime/pkg/printers"
  25. "k8s.io/cli-runtime/pkg/resource"
  26. cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  27. "k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
  28. "k8s.io/kubernetes/pkg/kubectl/scheme"
  29. "k8s.io/kubernetes/pkg/kubectl/util/i18n"
  30. "k8s.io/kubernetes/pkg/kubectl/util/templates"
  31. )
  32. var (
  33. serviceaccountResources = `
  34. replicationcontroller (rc), deployment (deploy), daemonset (ds), job, replicaset (rs), statefulset`
  35. serviceaccountLong = templates.LongDesc(i18n.T(`
  36. Update ServiceAccount of pod template resources.
  37. Possible resources (case insensitive) can be:
  38. ` + serviceaccountResources))
  39. serviceaccountExample = templates.Examples(i18n.T(`
  40. # Set Deployment nginx-deployment's ServiceAccount to serviceaccount1
  41. kubectl set serviceaccount deployment nginx-deployment serviceaccount1
  42. # Print the result (in yaml format) of updated nginx deployment with serviceaccount from local file, without hitting apiserver
  43. kubectl set sa -f nginx-deployment.yaml serviceaccount1 --local --dry-run -o yaml
  44. `))
  45. )
  46. // SetServiceAccountOptions encapsulates the data required to perform the operation.
  47. type SetServiceAccountOptions struct {
  48. PrintFlags *genericclioptions.PrintFlags
  49. RecordFlags *genericclioptions.RecordFlags
  50. fileNameOptions resource.FilenameOptions
  51. dryRun bool
  52. shortOutput bool
  53. all bool
  54. output string
  55. local bool
  56. updatePodSpecForObject polymorphichelpers.UpdatePodSpecForObjectFunc
  57. infos []*resource.Info
  58. serviceAccountName string
  59. PrintObj printers.ResourcePrinterFunc
  60. Recorder genericclioptions.Recorder
  61. genericclioptions.IOStreams
  62. }
  63. // NewSetServiceAccountOptions returns an initialized SetServiceAccountOptions instance
  64. func NewSetServiceAccountOptions(streams genericclioptions.IOStreams) *SetServiceAccountOptions {
  65. return &SetServiceAccountOptions{
  66. PrintFlags: genericclioptions.NewPrintFlags("serviceaccount updated").WithTypeSetter(scheme.Scheme),
  67. RecordFlags: genericclioptions.NewRecordFlags(),
  68. Recorder: genericclioptions.NoopRecorder{},
  69. IOStreams: streams,
  70. }
  71. }
  72. // NewCmdServiceAccount returns the "set serviceaccount" command.
  73. func NewCmdServiceAccount(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
  74. o := NewSetServiceAccountOptions(streams)
  75. cmd := &cobra.Command{
  76. Use: "serviceaccount (-f FILENAME | TYPE NAME) SERVICE_ACCOUNT",
  77. DisableFlagsInUseLine: true,
  78. Aliases: []string{"sa"},
  79. Short: i18n.T("Update ServiceAccount of a resource"),
  80. Long: serviceaccountLong,
  81. Example: serviceaccountExample,
  82. Run: func(cmd *cobra.Command, args []string) {
  83. cmdutil.CheckErr(o.Complete(f, cmd, args))
  84. cmdutil.CheckErr(o.Run())
  85. },
  86. }
  87. o.PrintFlags.AddFlags(cmd)
  88. o.RecordFlags.AddFlags(cmd)
  89. usage := "identifying the resource to get from a server."
  90. cmdutil.AddFilenameOptionFlags(cmd, &o.fileNameOptions, usage)
  91. cmd.Flags().BoolVar(&o.all, "all", o.all, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
  92. cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, set serviceaccount will NOT contact api-server but run locally.")
  93. cmdutil.AddDryRunFlag(cmd)
  94. cmdutil.AddIncludeUninitializedFlag(cmd)
  95. return cmd
  96. }
  97. // Complete configures serviceAccountConfig from command line args.
  98. func (o *SetServiceAccountOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
  99. var err error
  100. o.RecordFlags.Complete(cmd)
  101. o.Recorder, err = o.RecordFlags.ToRecorder()
  102. if err != nil {
  103. return err
  104. }
  105. o.shortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
  106. o.dryRun = cmdutil.GetDryRunFlag(cmd)
  107. o.output = cmdutil.GetFlagString(cmd, "output")
  108. o.updatePodSpecForObject = polymorphichelpers.UpdatePodSpecForObjectFn
  109. if o.dryRun {
  110. o.PrintFlags.Complete("%s (dry run)")
  111. }
  112. printer, err := o.PrintFlags.ToPrinter()
  113. if err != nil {
  114. return err
  115. }
  116. o.PrintObj = printer.PrintObj
  117. cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
  118. if err != nil {
  119. return err
  120. }
  121. if len(args) == 0 {
  122. return errors.New("serviceaccount is required")
  123. }
  124. o.serviceAccountName = args[len(args)-1]
  125. resources := args[:len(args)-1]
  126. builder := f.NewBuilder().
  127. WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
  128. LocalParam(o.local).
  129. ContinueOnError().
  130. NamespaceParam(cmdNamespace).DefaultNamespace().
  131. FilenameParam(enforceNamespace, &o.fileNameOptions).
  132. Flatten()
  133. if !o.local {
  134. builder.ResourceTypeOrNameArgs(o.all, resources...).
  135. Latest()
  136. }
  137. o.infos, err = builder.Do().Infos()
  138. if err != nil {
  139. return err
  140. }
  141. return nil
  142. }
  143. // Run creates and applies the patch either locally or calling apiserver.
  144. func (o *SetServiceAccountOptions) Run() error {
  145. patchErrs := []error{}
  146. patchFn := func(obj runtime.Object) ([]byte, error) {
  147. _, err := o.updatePodSpecForObject(obj, func(podSpec *v1.PodSpec) error {
  148. podSpec.ServiceAccountName = o.serviceAccountName
  149. return nil
  150. })
  151. if err != nil {
  152. return nil, err
  153. }
  154. // record this change (for rollout history)
  155. if err := o.Recorder.Record(obj); err != nil {
  156. klog.V(4).Infof("error recording current command: %v", err)
  157. }
  158. return runtime.Encode(scheme.DefaultJSONEncoder(), obj)
  159. }
  160. patches := CalculatePatches(o.infos, scheme.DefaultJSONEncoder(), patchFn)
  161. for _, patch := range patches {
  162. info := patch.Info
  163. name := info.ObjectName()
  164. if patch.Err != nil {
  165. patchErrs = append(patchErrs, fmt.Errorf("error: %s %v\n", name, patch.Err))
  166. continue
  167. }
  168. if o.local || o.dryRun {
  169. if err := o.PrintObj(info.Object, o.Out); err != nil {
  170. patchErrs = append(patchErrs, err)
  171. }
  172. continue
  173. }
  174. actual, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
  175. if err != nil {
  176. patchErrs = append(patchErrs, fmt.Errorf("failed to patch ServiceAccountName %v", err))
  177. continue
  178. }
  179. if err := o.PrintObj(actual, o.Out); err != nil {
  180. patchErrs = append(patchErrs, err)
  181. }
  182. }
  183. return utilerrors.NewAggregate(patchErrs)
  184. }