...

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

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

     1  /*
     2  Copyright 2016 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  	"fmt"
    21  
    22  	"github.com/spf13/cobra"
    23  	"k8s.io/klog/v2"
    24  
    25  	v1 "k8s.io/api/core/v1"
    26  	"k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/apimachinery/pkg/types"
    28  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    29  	"k8s.io/cli-runtime/pkg/genericclioptions"
    30  	"k8s.io/cli-runtime/pkg/genericiooptions"
    31  	"k8s.io/cli-runtime/pkg/printers"
    32  	"k8s.io/cli-runtime/pkg/resource"
    33  	"k8s.io/client-go/tools/clientcmd"
    34  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    35  	generateversioned "k8s.io/kubectl/pkg/generate/versioned"
    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  	resourcesLong = templates.LongDesc(i18n.T(`
    44  		Specify compute resource requirements (CPU, memory) for any resource that defines a pod template.  If a pod is successfully scheduled, it is guaranteed the amount of resource requested, but may burst up to its specified limits.
    45  
    46  		For each compute resource, if a limit is specified and a request is omitted, the request will default to the limit.
    47  
    48  		Possible resources include (case insensitive): %s.`))
    49  
    50  	resourcesExample = templates.Examples(`
    51  		# Set a deployments nginx container cpu limits to "200m" and memory to "512Mi"
    52  		kubectl set resources deployment nginx -c=nginx --limits=cpu=200m,memory=512Mi
    53  
    54  		# Set the resource request and limits for all containers in nginx
    55  		kubectl set resources deployment nginx --limits=cpu=200m,memory=512Mi --requests=cpu=100m,memory=256Mi
    56  
    57  		# Remove the resource requests for resources on containers in nginx
    58  		kubectl set resources deployment nginx --limits=cpu=0,memory=0 --requests=cpu=0,memory=0
    59  
    60  		# Print the result (in yaml format) of updating nginx container limits from a local, without hitting the server
    61  		kubectl set resources -f path/to/file.yaml --limits=cpu=200m,memory=512Mi --local -o yaml`)
    62  )
    63  
    64  // SetResourcesOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
    65  // referencing the cmd.Flags
    66  type SetResourcesOptions struct {
    67  	resource.FilenameOptions
    68  
    69  	PrintFlags  *genericclioptions.PrintFlags
    70  	RecordFlags *genericclioptions.RecordFlags
    71  
    72  	Infos             []*resource.Info
    73  	Selector          string
    74  	ContainerSelector string
    75  	Output            string
    76  	All               bool
    77  	Local             bool
    78  	fieldManager      string
    79  
    80  	DryRunStrategy cmdutil.DryRunStrategy
    81  
    82  	PrintObj printers.ResourcePrinterFunc
    83  	Recorder genericclioptions.Recorder
    84  
    85  	Limits               string
    86  	Requests             string
    87  	ResourceRequirements v1.ResourceRequirements
    88  
    89  	UpdatePodSpecForObject polymorphichelpers.UpdatePodSpecForObjectFunc
    90  	Resources              []string
    91  
    92  	genericiooptions.IOStreams
    93  }
    94  
    95  // NewResourcesOptions returns a ResourcesOptions indicating all containers in the selected
    96  // pod templates are selected by default.
    97  func NewResourcesOptions(streams genericiooptions.IOStreams) *SetResourcesOptions {
    98  	return &SetResourcesOptions{
    99  		PrintFlags:  genericclioptions.NewPrintFlags("resource requirements updated").WithTypeSetter(scheme.Scheme),
   100  		RecordFlags: genericclioptions.NewRecordFlags(),
   101  
   102  		Recorder: genericclioptions.NoopRecorder{},
   103  
   104  		ContainerSelector: "*",
   105  
   106  		IOStreams: streams,
   107  	}
   108  }
   109  
   110  // NewCmdResources returns initialized Command instance for the 'set resources' sub command
   111  func NewCmdResources(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
   112  	o := NewResourcesOptions(streams)
   113  
   114  	cmd := &cobra.Command{
   115  		Use:                   "resources (-f FILENAME | TYPE NAME)  ([--limits=LIMITS & --requests=REQUESTS]",
   116  		DisableFlagsInUseLine: true,
   117  		Short:                 i18n.T("Update resource requests/limits on objects with pod templates"),
   118  		Long:                  fmt.Sprintf(resourcesLong, cmdutil.SuggestAPIResources("kubectl")),
   119  		Example:               resourcesExample,
   120  		Run: func(cmd *cobra.Command, args []string) {
   121  			cmdutil.CheckErr(o.Complete(f, cmd, args))
   122  			cmdutil.CheckErr(o.Validate())
   123  			cmdutil.CheckErr(o.Run())
   124  		},
   125  	}
   126  
   127  	o.PrintFlags.AddFlags(cmd)
   128  	o.RecordFlags.AddFlags(cmd)
   129  
   130  	//usage := "Filename, directory, or URL to a file identifying the resource to get from the server"
   131  	//kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
   132  	usage := "identifying the resource to get from a server."
   133  	cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
   134  	cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources, in the namespace of the specified resource types")
   135  	cmdutil.AddLabelSelectorFlagVar(cmd, &o.Selector)
   136  	cmd.Flags().StringVarP(&o.ContainerSelector, "containers", "c", o.ContainerSelector, "The names of containers in the selected pod templates to change, all containers are selected by default - may use wildcards")
   137  	cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, set resources will NOT contact api-server but run locally.")
   138  	cmdutil.AddDryRunFlag(cmd)
   139  	cmd.Flags().StringVar(&o.Limits, "limits", o.Limits, "The resource requirement requests for this container.  For example, 'cpu=100m,memory=256Mi'.  Note that server side components may assign requests depending on the server configuration, such as limit ranges.")
   140  	cmd.Flags().StringVar(&o.Requests, "requests", o.Requests, "The resource requirement requests for this container.  For example, 'cpu=100m,memory=256Mi'.  Note that server side components may assign requests depending on the server configuration, such as limit ranges.")
   141  	cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-set")
   142  	return cmd
   143  }
   144  
   145  // Complete completes all required options
   146  func (o *SetResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
   147  	var err error
   148  
   149  	o.RecordFlags.Complete(cmd)
   150  	o.Recorder, err = o.RecordFlags.ToRecorder()
   151  	if err != nil {
   152  		return err
   153  	}
   154  
   155  	o.UpdatePodSpecForObject = polymorphichelpers.UpdatePodSpecForObjectFn
   156  	o.Output = cmdutil.GetFlagString(cmd, "output")
   157  	o.DryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.DryRunStrategy)
   163  	printer, err := o.PrintFlags.ToPrinter()
   164  	if err != nil {
   165  		return err
   166  	}
   167  	o.PrintObj = printer.PrintObj
   168  
   169  	cmdNamespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
   170  	if err != nil && !(o.Local && clientcmd.IsEmptyConfig(err)) {
   171  		return err
   172  	}
   173  
   174  	builder := f.NewBuilder().
   175  		WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
   176  		LocalParam(o.Local).
   177  		ContinueOnError().
   178  		NamespaceParam(cmdNamespace).DefaultNamespace().
   179  		FilenameParam(enforceNamespace, &o.FilenameOptions).
   180  		Flatten()
   181  
   182  	if !o.Local {
   183  		builder.LabelSelectorParam(o.Selector).
   184  			ResourceTypeOrNameArgs(o.All, args...).
   185  			Latest()
   186  	} else {
   187  		// if a --local flag was provided, and a resource was specified in the form
   188  		// <resource>/<name>, fail immediately as --local cannot query the api server
   189  		// for the specified resource.
   190  		// TODO: this should be in the builder - if someone specifies tuples, fail when
   191  		// local is true
   192  		if len(args) > 0 {
   193  			return resource.LocalResourceError
   194  		}
   195  	}
   196  
   197  	o.Infos, err = builder.Do().Infos()
   198  	return err
   199  }
   200  
   201  // Validate makes sure that provided values in ResourcesOptions are valid
   202  func (o *SetResourcesOptions) Validate() error {
   203  	var err error
   204  	if o.Local && o.DryRunStrategy == cmdutil.DryRunServer {
   205  		return fmt.Errorf("cannot specify --local and --dry-run=server - did you mean --dry-run=client?")
   206  	}
   207  	if o.All && len(o.Selector) > 0 {
   208  		return fmt.Errorf("cannot set --all and --selector at the same time")
   209  	}
   210  	if len(o.Limits) == 0 && len(o.Requests) == 0 {
   211  		return fmt.Errorf("you must specify an update to requests or limits (in the form of --requests/--limits)")
   212  	}
   213  
   214  	o.ResourceRequirements, err = generateversioned.HandleResourceRequirementsV1(map[string]string{"limits": o.Limits, "requests": o.Requests})
   215  	if err != nil {
   216  		return err
   217  	}
   218  
   219  	return nil
   220  }
   221  
   222  // Run performs the execution of 'set resources' sub command
   223  func (o *SetResourcesOptions) Run() error {
   224  	allErrs := []error{}
   225  	patches := CalculatePatches(o.Infos, scheme.DefaultJSONEncoder(), func(obj runtime.Object) ([]byte, error) {
   226  		transformed := false
   227  		_, err := o.UpdatePodSpecForObject(obj, func(spec *v1.PodSpec) error {
   228  			initContainers, _ := selectContainers(spec.InitContainers, o.ContainerSelector)
   229  			containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
   230  			containers = append(containers, initContainers...)
   231  			if len(containers) != 0 {
   232  				for i := range containers {
   233  					if len(o.Limits) != 0 && len(containers[i].Resources.Limits) == 0 {
   234  						containers[i].Resources.Limits = make(v1.ResourceList)
   235  					}
   236  					for key, value := range o.ResourceRequirements.Limits {
   237  						containers[i].Resources.Limits[key] = value
   238  					}
   239  
   240  					if len(o.Requests) != 0 && len(containers[i].Resources.Requests) == 0 {
   241  						containers[i].Resources.Requests = make(v1.ResourceList)
   242  					}
   243  					for key, value := range o.ResourceRequirements.Requests {
   244  						containers[i].Resources.Requests[key] = value
   245  					}
   246  					transformed = true
   247  				}
   248  			} else {
   249  				allErrs = append(allErrs, fmt.Errorf("error: unable to find container named %s", o.ContainerSelector))
   250  			}
   251  			return nil
   252  		})
   253  		if err != nil {
   254  			return nil, err
   255  		}
   256  		if !transformed {
   257  			return nil, nil
   258  		}
   259  		// record this change (for rollout history)
   260  		if err := o.Recorder.Record(obj); err != nil {
   261  			klog.V(4).Infof("error recording current command: %v", err)
   262  		}
   263  
   264  		return runtime.Encode(scheme.DefaultJSONEncoder(), obj)
   265  	})
   266  
   267  	for _, patch := range patches {
   268  		info := patch.Info
   269  		name := info.ObjectName()
   270  		if patch.Err != nil {
   271  			allErrs = append(allErrs, fmt.Errorf("error: %s %v\n", name, patch.Err))
   272  			continue
   273  		}
   274  
   275  		//no changes
   276  		if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
   277  			continue
   278  		}
   279  
   280  		if o.Local || o.DryRunStrategy == cmdutil.DryRunClient {
   281  			if err := o.PrintObj(info.Object, o.Out); err != nil {
   282  				allErrs = append(allErrs, err)
   283  			}
   284  			continue
   285  		}
   286  
   287  		actual, err := resource.
   288  			NewHelper(info.Client, info.Mapping).
   289  			DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
   290  			WithFieldManager(o.fieldManager).
   291  			Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch, nil)
   292  		if err != nil {
   293  			allErrs = append(allErrs, fmt.Errorf("failed to patch resources update to pod template %v", err))
   294  			continue
   295  		}
   296  
   297  		if err := o.PrintObj(actual, o.Out); err != nil {
   298  			allErrs = append(allErrs, err)
   299  		}
   300  	}
   301  	return utilerrors.NewAggregate(allErrs)
   302  }
   303  

View as plain text