...

Source file src/k8s.io/kubectl/pkg/cmd/set/set_serviceaccount.go

Documentation: k8s.io/kubectl/pkg/cmd/set

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package set
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  
    23  	"github.com/spf13/cobra"
    24  	v1 "k8s.io/api/core/v1"
    25  	"k8s.io/klog/v2"
    26  
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/types"
    29  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    30  	"k8s.io/cli-runtime/pkg/genericclioptions"
    31  	"k8s.io/cli-runtime/pkg/genericiooptions"
    32  	"k8s.io/cli-runtime/pkg/printers"
    33  	"k8s.io/cli-runtime/pkg/resource"
    34  	"k8s.io/client-go/tools/clientcmd"
    35  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    36  	"k8s.io/kubectl/pkg/polymorphichelpers"
    37  	"k8s.io/kubectl/pkg/scheme"
    38  	"k8s.io/kubectl/pkg/util/i18n"
    39  	"k8s.io/kubectl/pkg/util/templates"
    40  )
    41  
    42  var (
    43  	serviceaccountResources = i18n.T(`replicationcontroller (rc), deployment (deploy), daemonset (ds), job, replicaset (rs), statefulset`)
    44  
    45  	serviceaccountLong = templates.LongDesc(i18n.T(`
    46  	Update the service account of pod template resources.
    47  
    48  	Possible resources (case insensitive) can be:
    49  
    50  	`) + serviceaccountResources)
    51  
    52  	serviceaccountExample = templates.Examples(i18n.T(`
    53  	# Set deployment nginx-deployment's service account to serviceaccount1
    54  	kubectl set serviceaccount deployment nginx-deployment serviceaccount1
    55  
    56  	# Print the result (in YAML format) of updated nginx deployment with the service account from local file, without hitting the API server
    57  	kubectl set sa -f nginx-deployment.yaml serviceaccount1 --local --dry-run=client -o yaml
    58  	`))
    59  )
    60  
    61  // SetServiceAccountOptions encapsulates the data required to perform the operation.
    62  type SetServiceAccountOptions struct {
    63  	PrintFlags  *genericclioptions.PrintFlags
    64  	RecordFlags *genericclioptions.RecordFlags
    65  
    66  	fileNameOptions        resource.FilenameOptions
    67  	dryRunStrategy         cmdutil.DryRunStrategy
    68  	shortOutput            bool
    69  	all                    bool
    70  	output                 string
    71  	local                  bool
    72  	updatePodSpecForObject polymorphichelpers.UpdatePodSpecForObjectFunc
    73  	infos                  []*resource.Info
    74  	serviceAccountName     string
    75  	fieldManager           string
    76  
    77  	PrintObj printers.ResourcePrinterFunc
    78  	Recorder genericclioptions.Recorder
    79  
    80  	genericiooptions.IOStreams
    81  }
    82  
    83  // NewSetServiceAccountOptions returns an initialized SetServiceAccountOptions instance
    84  func NewSetServiceAccountOptions(streams genericiooptions.IOStreams) *SetServiceAccountOptions {
    85  	return &SetServiceAccountOptions{
    86  		PrintFlags:  genericclioptions.NewPrintFlags("serviceaccount updated").WithTypeSetter(scheme.Scheme),
    87  		RecordFlags: genericclioptions.NewRecordFlags(),
    88  
    89  		Recorder: genericclioptions.NoopRecorder{},
    90  
    91  		IOStreams: streams,
    92  	}
    93  }
    94  
    95  // NewCmdServiceAccount returns the "set serviceaccount" command.
    96  func NewCmdServiceAccount(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
    97  	o := NewSetServiceAccountOptions(streams)
    98  
    99  	cmd := &cobra.Command{
   100  		Use:                   "serviceaccount (-f FILENAME | TYPE NAME) SERVICE_ACCOUNT",
   101  		DisableFlagsInUseLine: true,
   102  		Aliases:               []string{"sa"},
   103  		Short:                 i18n.T("Update the service account of a resource"),
   104  		Long:                  serviceaccountLong,
   105  		Example:               serviceaccountExample,
   106  		Run: func(cmd *cobra.Command, args []string) {
   107  			cmdutil.CheckErr(o.Complete(f, cmd, args))
   108  			cmdutil.CheckErr(o.Run())
   109  		},
   110  	}
   111  
   112  	o.PrintFlags.AddFlags(cmd)
   113  	o.RecordFlags.AddFlags(cmd)
   114  
   115  	usage := "identifying the resource to get from a server."
   116  	cmdutil.AddFilenameOptionFlags(cmd, &o.fileNameOptions, usage)
   117  	cmd.Flags().BoolVar(&o.all, "all", o.all, "Select all resources, in the namespace of the specified resource types")
   118  	cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, set serviceaccount will NOT contact api-server but run locally.")
   119  	cmdutil.AddDryRunFlag(cmd)
   120  	cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-set")
   121  	return cmd
   122  }
   123  
   124  // Complete configures serviceAccountConfig from command line args.
   125  func (o *SetServiceAccountOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
   126  	var err error
   127  
   128  	o.RecordFlags.Complete(cmd)
   129  	o.Recorder, err = o.RecordFlags.ToRecorder()
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	o.shortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
   135  	o.dryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
   136  	if err != nil {
   137  		return err
   138  	}
   139  	if o.local && o.dryRunStrategy == cmdutil.DryRunServer {
   140  		return fmt.Errorf("cannot specify --local and --dry-run=server - did you mean --dry-run=client?")
   141  	}
   142  	o.output = cmdutil.GetFlagString(cmd, "output")
   143  	o.updatePodSpecForObject = polymorphichelpers.UpdatePodSpecForObjectFn
   144  
   145  	cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.dryRunStrategy)
   146  	printer, err := o.PrintFlags.ToPrinter()
   147  	if err != nil {
   148  		return err
   149  	}
   150  	o.PrintObj = printer.PrintObj
   151  
   152  	cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
   153  	if err != nil && !(o.local && clientcmd.IsEmptyConfig(err)) {
   154  		return err
   155  	}
   156  	if len(args) == 0 {
   157  		return errors.New("serviceaccount is required")
   158  	}
   159  	o.serviceAccountName = args[len(args)-1]
   160  	resources := args[:len(args)-1]
   161  	builder := f.NewBuilder().
   162  		WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
   163  		LocalParam(o.local).
   164  		ContinueOnError().
   165  		NamespaceParam(cmdNamespace).DefaultNamespace().
   166  		FilenameParam(enforceNamespace, &o.fileNameOptions).
   167  		Flatten()
   168  	if !o.local {
   169  		builder.ResourceTypeOrNameArgs(o.all, resources...).
   170  			Latest()
   171  	}
   172  	o.infos, err = builder.Do().Infos()
   173  	return err
   174  }
   175  
   176  // Run creates and applies the patch either locally or calling apiserver.
   177  func (o *SetServiceAccountOptions) Run() error {
   178  	patchErrs := []error{}
   179  	patchFn := func(obj runtime.Object) ([]byte, error) {
   180  		_, err := o.updatePodSpecForObject(obj, func(podSpec *v1.PodSpec) error {
   181  			podSpec.ServiceAccountName = o.serviceAccountName
   182  			return nil
   183  		})
   184  		if err != nil {
   185  			return nil, err
   186  		}
   187  		// record this change (for rollout history)
   188  		if err := o.Recorder.Record(obj); err != nil {
   189  			klog.V(4).Infof("error recording current command: %v", err)
   190  		}
   191  
   192  		return runtime.Encode(scheme.DefaultJSONEncoder(), obj)
   193  	}
   194  
   195  	patches := CalculatePatches(o.infos, scheme.DefaultJSONEncoder(), patchFn)
   196  	for _, patch := range patches {
   197  		info := patch.Info
   198  		name := info.ObjectName()
   199  		if patch.Err != nil {
   200  			patchErrs = append(patchErrs, fmt.Errorf("error: %s %v\n", name, patch.Err))
   201  			continue
   202  		}
   203  		if o.local || o.dryRunStrategy == cmdutil.DryRunClient {
   204  			if err := o.PrintObj(info.Object, o.Out); err != nil {
   205  				patchErrs = append(patchErrs, err)
   206  			}
   207  			continue
   208  		}
   209  		actual, err := resource.
   210  			NewHelper(info.Client, info.Mapping).
   211  			DryRun(o.dryRunStrategy == cmdutil.DryRunServer).
   212  			WithFieldManager(o.fieldManager).
   213  			Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
   214  		if err != nil {
   215  			patchErrs = append(patchErrs, fmt.Errorf("failed to patch ServiceAccountName %v", err))
   216  			continue
   217  		}
   218  
   219  		if err := o.PrintObj(actual, o.Out); err != nil {
   220  			patchErrs = append(patchErrs, err)
   221  		}
   222  	}
   223  	return utilerrors.NewAggregate(patchErrs)
   224  }
   225  

View as plain text