...

Source file src/k8s.io/kubectl/pkg/cmd/autoscale/autoscale.go

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

     1  /*
     2  Copyright 2015 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 autoscale
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/spf13/cobra"
    24  	"k8s.io/klog/v2"
    25  
    26  	autoscalingv1 "k8s.io/api/autoscaling/v1"
    27  	"k8s.io/apimachinery/pkg/api/meta"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    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  	autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1"
    34  	"k8s.io/client-go/scale"
    35  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    36  	"k8s.io/kubectl/pkg/scheme"
    37  	"k8s.io/kubectl/pkg/util"
    38  	"k8s.io/kubectl/pkg/util/completion"
    39  	"k8s.io/kubectl/pkg/util/i18n"
    40  	"k8s.io/kubectl/pkg/util/templates"
    41  )
    42  
    43  var (
    44  	autoscaleLong = templates.LongDesc(i18n.T(`
    45  		Creates an autoscaler that automatically chooses and sets the number of pods that run in a Kubernetes cluster.
    46  
    47  		Looks up a deployment, replica set, stateful set, or replication controller by name and creates an autoscaler that uses the given resource as a reference.
    48  		An autoscaler can automatically increase or decrease number of pods deployed within the system as needed.`))
    49  
    50  	autoscaleExample = templates.Examples(i18n.T(`
    51  		# Auto scale a deployment "foo", with the number of pods between 2 and 10, no target CPU utilization specified so a default autoscaling policy will be used
    52  		kubectl autoscale deployment foo --min=2 --max=10
    53  
    54  		# Auto scale a replication controller "foo", with the number of pods between 1 and 5, target CPU utilization at 80%
    55  		kubectl autoscale rc foo --max=5 --cpu-percent=80`))
    56  )
    57  
    58  // AutoscaleOptions declares the arguments accepted by the Autoscale command
    59  type AutoscaleOptions struct {
    60  	FilenameOptions *resource.FilenameOptions
    61  
    62  	RecordFlags *genericclioptions.RecordFlags
    63  	Recorder    genericclioptions.Recorder
    64  
    65  	PrintFlags *genericclioptions.PrintFlags
    66  	ToPrinter  func(string) (printers.ResourcePrinter, error)
    67  
    68  	Name       string
    69  	Min        int32
    70  	Max        int32
    71  	CPUPercent int32
    72  
    73  	createAnnotation bool
    74  	args             []string
    75  	enforceNamespace bool
    76  	namespace        string
    77  	dryRunStrategy   cmdutil.DryRunStrategy
    78  	builder          *resource.Builder
    79  	fieldManager     string
    80  
    81  	HPAClient         autoscalingv1client.HorizontalPodAutoscalersGetter
    82  	scaleKindResolver scale.ScaleKindResolver
    83  
    84  	genericiooptions.IOStreams
    85  }
    86  
    87  // NewAutoscaleOptions creates the options for autoscale
    88  func NewAutoscaleOptions(ioStreams genericiooptions.IOStreams) *AutoscaleOptions {
    89  	return &AutoscaleOptions{
    90  		PrintFlags:      genericclioptions.NewPrintFlags("autoscaled").WithTypeSetter(scheme.Scheme),
    91  		FilenameOptions: &resource.FilenameOptions{},
    92  		RecordFlags:     genericclioptions.NewRecordFlags(),
    93  		Recorder:        genericclioptions.NoopRecorder{},
    94  
    95  		IOStreams: ioStreams,
    96  	}
    97  }
    98  
    99  // NewCmdAutoscale returns the autoscale Cobra command
   100  func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
   101  	o := NewAutoscaleOptions(ioStreams)
   102  
   103  	validArgs := []string{"deployment", "replicaset", "replicationcontroller", "statefulset"}
   104  
   105  	cmd := &cobra.Command{
   106  		Use:                   "autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU]",
   107  		DisableFlagsInUseLine: true,
   108  		Short:                 i18n.T("Auto-scale a deployment, replica set, stateful set, or replication controller"),
   109  		Long:                  autoscaleLong,
   110  		Example:               autoscaleExample,
   111  		ValidArgsFunction:     completion.SpecifiedResourceTypeAndNameCompletionFunc(f, validArgs),
   112  		Run: func(cmd *cobra.Command, args []string) {
   113  			cmdutil.CheckErr(o.Complete(f, cmd, args))
   114  			cmdutil.CheckErr(o.Validate())
   115  			cmdutil.CheckErr(o.Run())
   116  		},
   117  	}
   118  
   119  	// bind flag structs
   120  	o.RecordFlags.AddFlags(cmd)
   121  	o.PrintFlags.AddFlags(cmd)
   122  	cmd.Flags().Int32Var(&o.Min, "min", -1, "The lower limit for the number of pods that can be set by the autoscaler. If it's not specified or negative, the server will apply a default value.")
   123  	cmd.Flags().Int32Var(&o.Max, "max", -1, "The upper limit for the number of pods that can be set by the autoscaler. Required.")
   124  	cmd.MarkFlagRequired("max")
   125  	cmd.Flags().Int32Var(&o.CPUPercent, "cpu-percent", -1, "The target average CPU utilization (represented as a percent of requested CPU) over all the pods. If it's not specified or negative, a default autoscaling policy will be used.")
   126  	cmd.Flags().StringVar(&o.Name, "name", "", i18n.T("The name for the newly created object. If not specified, the name of the input resource will be used."))
   127  	cmdutil.AddDryRunFlag(cmd)
   128  	cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, "identifying the resource to autoscale.")
   129  	cmdutil.AddApplyAnnotationFlags(cmd)
   130  	cmdutil.AddFieldManagerFlagVar(cmd, &o.fieldManager, "kubectl-autoscale")
   131  	return cmd
   132  }
   133  
   134  // Complete verifies command line arguments and loads data from the command environment
   135  func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
   136  	var err error
   137  	o.dryRunStrategy, err = cmdutil.GetDryRunStrategy(cmd)
   138  	if err != nil {
   139  		return err
   140  	}
   141  	discoveryClient, err := f.ToDiscoveryClient()
   142  	if err != nil {
   143  		return err
   144  	}
   145  	o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
   146  	o.builder = f.NewBuilder()
   147  	o.scaleKindResolver = scale.NewDiscoveryScaleKindResolver(discoveryClient)
   148  	o.args = args
   149  	o.RecordFlags.Complete(cmd)
   150  
   151  	o.Recorder, err = o.RecordFlags.ToRecorder()
   152  	if err != nil {
   153  		return err
   154  	}
   155  
   156  	kubeClient, err := f.KubernetesClientSet()
   157  	if err != nil {
   158  		return err
   159  	}
   160  	o.HPAClient = kubeClient.AutoscalingV1()
   161  
   162  	o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
   168  		o.PrintFlags.NamePrintFlags.Operation = operation
   169  		cmdutil.PrintFlagsWithDryRunStrategy(o.PrintFlags, o.dryRunStrategy)
   170  
   171  		return o.PrintFlags.ToPrinter()
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  // Validate checks that the provided attach options are specified.
   178  func (o *AutoscaleOptions) Validate() error {
   179  	if o.Max < 1 {
   180  		return fmt.Errorf("--max=MAXPODS is required and must be at least 1, max: %d", o.Max)
   181  	}
   182  	if o.Max < o.Min {
   183  		return fmt.Errorf("--max=MAXPODS must be larger or equal to --min=MINPODS, max: %d, min: %d", o.Max, o.Min)
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  // Run performs the execution
   190  func (o *AutoscaleOptions) Run() error {
   191  	r := o.builder.
   192  		Unstructured().
   193  		ContinueOnError().
   194  		NamespaceParam(o.namespace).DefaultNamespace().
   195  		FilenameParam(o.enforceNamespace, o.FilenameOptions).
   196  		ResourceTypeOrNameArgs(false, o.args...).
   197  		Flatten().
   198  		Do()
   199  	if err := r.Err(); err != nil {
   200  		return err
   201  	}
   202  
   203  	count := 0
   204  	err := r.Visit(func(info *resource.Info, err error) error {
   205  		if err != nil {
   206  			return err
   207  		}
   208  
   209  		mapping := info.ResourceMapping()
   210  		gvr := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource.Resource)
   211  		if _, err := o.scaleKindResolver.ScaleForResource(gvr); err != nil {
   212  			return fmt.Errorf("cannot autoscale a %v: %v", mapping.GroupVersionKind.Kind, err)
   213  		}
   214  
   215  		hpa := o.createHorizontalPodAutoscaler(info.Name, mapping)
   216  
   217  		if err := o.Recorder.Record(hpa); err != nil {
   218  			klog.V(4).Infof("error recording current command: %v", err)
   219  		}
   220  
   221  		if o.dryRunStrategy == cmdutil.DryRunClient {
   222  			count++
   223  
   224  			printer, err := o.ToPrinter("created")
   225  			if err != nil {
   226  				return err
   227  			}
   228  			return printer.PrintObj(hpa, o.Out)
   229  		}
   230  
   231  		if err := util.CreateOrUpdateAnnotation(o.createAnnotation, hpa, scheme.DefaultJSONEncoder()); err != nil {
   232  			return err
   233  		}
   234  
   235  		createOptions := metav1.CreateOptions{}
   236  		if o.fieldManager != "" {
   237  			createOptions.FieldManager = o.fieldManager
   238  		}
   239  		if o.dryRunStrategy == cmdutil.DryRunServer {
   240  			createOptions.DryRun = []string{metav1.DryRunAll}
   241  		}
   242  		actualHPA, err := o.HPAClient.HorizontalPodAutoscalers(o.namespace).Create(context.TODO(), hpa, createOptions)
   243  		if err != nil {
   244  			return err
   245  		}
   246  
   247  		count++
   248  		printer, err := o.ToPrinter("autoscaled")
   249  		if err != nil {
   250  			return err
   251  		}
   252  		return printer.PrintObj(actualHPA, o.Out)
   253  	})
   254  	if err != nil {
   255  		return err
   256  	}
   257  	if count == 0 {
   258  		return fmt.Errorf("no objects passed to autoscale")
   259  	}
   260  	return nil
   261  }
   262  
   263  func (o *AutoscaleOptions) createHorizontalPodAutoscaler(refName string, mapping *meta.RESTMapping) *autoscalingv1.HorizontalPodAutoscaler {
   264  	name := o.Name
   265  	if len(name) == 0 {
   266  		name = refName
   267  	}
   268  
   269  	scaler := autoscalingv1.HorizontalPodAutoscaler{
   270  		ObjectMeta: metav1.ObjectMeta{
   271  			Name: name,
   272  		},
   273  		Spec: autoscalingv1.HorizontalPodAutoscalerSpec{
   274  			ScaleTargetRef: autoscalingv1.CrossVersionObjectReference{
   275  				APIVersion: mapping.GroupVersionKind.GroupVersion().String(),
   276  				Kind:       mapping.GroupVersionKind.Kind,
   277  				Name:       refName,
   278  			},
   279  			MaxReplicas: o.Max,
   280  		},
   281  	}
   282  
   283  	if o.Min > 0 {
   284  		v := int32(o.Min)
   285  		scaler.Spec.MinReplicas = &v
   286  	}
   287  	if o.CPUPercent >= 0 {
   288  		c := int32(o.CPUPercent)
   289  		scaler.Spec.TargetCPUUtilizationPercentage = &c
   290  	}
   291  
   292  	return &scaler
   293  }
   294  

View as plain text