2019-01-12 04:58:27 +00:00
/ *
Copyright 2015 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package autoscale
import (
2020-03-26 21:07:15 +00:00
"context"
2019-01-12 04:58:27 +00:00
"fmt"
"github.com/spf13/cobra"
2020-08-10 17:43:49 +00:00
"k8s.io/klog/v2"
2019-01-12 04:58:27 +00:00
autoscalingv1 "k8s.io/api/autoscaling/v1"
"k8s.io/apimachinery/pkg/api/meta"
2020-03-26 21:07:15 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2019-01-12 04:58:27 +00:00
"k8s.io/cli-runtime/pkg/genericclioptions"
2019-04-07 17:07:55 +00:00
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/cli-runtime/pkg/resource"
2019-01-12 04:58:27 +00:00
autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1"
2019-04-07 17:07:55 +00:00
"k8s.io/client-go/scale"
2019-09-27 21:51:53 +00:00
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/templates"
2019-01-12 04:58:27 +00:00
)
var (
autoscaleLong = templates . LongDesc ( i18n . T ( `
Creates an autoscaler that automatically chooses and sets the number of pods that run in a kubernetes cluster .
Looks up a Deployment , ReplicaSet , StatefulSet , or ReplicationController by name and creates an autoscaler that uses the given resource as a reference .
An autoscaler can automatically increase or decrease number of pods deployed within the system as needed . ` ) )
autoscaleExample = templates . Examples ( i18n . T ( `
# 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 :
kubectl autoscale deployment foo -- min = 2 -- max = 10
# Auto scale a replication controller "foo" , with the number of pods between 1 and 5 , target CPU utilization at 80 % :
kubectl autoscale rc foo -- max = 5 -- cpu - percent = 80 ` ) )
)
2020-12-01 01:06:26 +00:00
// AutoscaleOptions declares the arguments accepted by the Autoscale command
2019-01-12 04:58:27 +00:00
type AutoscaleOptions struct {
FilenameOptions * resource . FilenameOptions
RecordFlags * genericclioptions . RecordFlags
Recorder genericclioptions . Recorder
PrintFlags * genericclioptions . PrintFlags
ToPrinter func ( string ) ( printers . ResourcePrinter , error )
Name string
Min int32
Max int32
2019-04-07 17:07:55 +00:00
CPUPercent int32
2019-01-12 04:58:27 +00:00
createAnnotation bool
args [ ] string
enforceNamespace bool
namespace string
2020-03-26 21:07:15 +00:00
dryRunStrategy cmdutil . DryRunStrategy
dryRunVerifier * resource . DryRunVerifier
2019-01-12 04:58:27 +00:00
builder * resource . Builder
2020-08-10 17:43:49 +00:00
fieldManager string
2019-01-12 04:58:27 +00:00
2019-04-07 17:07:55 +00:00
HPAClient autoscalingv1client . HorizontalPodAutoscalersGetter
scaleKindResolver scale . ScaleKindResolver
2019-01-12 04:58:27 +00:00
genericclioptions . IOStreams
}
2019-08-30 18:33:25 +00:00
// NewAutoscaleOptions creates the options for autoscale
2019-01-12 04:58:27 +00:00
func NewAutoscaleOptions ( ioStreams genericclioptions . IOStreams ) * AutoscaleOptions {
return & AutoscaleOptions {
PrintFlags : genericclioptions . NewPrintFlags ( "autoscaled" ) . WithTypeSetter ( scheme . Scheme ) ,
FilenameOptions : & resource . FilenameOptions { } ,
RecordFlags : genericclioptions . NewRecordFlags ( ) ,
Recorder : genericclioptions . NoopRecorder { } ,
IOStreams : ioStreams ,
}
}
2019-08-30 18:33:25 +00:00
// NewCmdAutoscale returns the autoscale Cobra command
2019-01-12 04:58:27 +00:00
func NewCmdAutoscale ( f cmdutil . Factory , ioStreams genericclioptions . IOStreams ) * cobra . Command {
o := NewAutoscaleOptions ( ioStreams )
2021-03-18 22:40:29 +00:00
validArgs := [ ] string { "deployment" , "replicaset" , "replicationcontroller" , "statefulset" }
2019-01-12 04:58:27 +00:00
cmd := & cobra . Command {
Use : "autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU]" ,
DisableFlagsInUseLine : true ,
2021-03-18 22:40:29 +00:00
Short : i18n . T ( "Auto-scale a Deployment, ReplicaSet, StatefulSet, or ReplicationController" ) ,
2019-01-12 04:58:27 +00:00
Long : autoscaleLong ,
Example : autoscaleExample ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
cmdutil . CheckErr ( o . Complete ( f , cmd , args ) )
cmdutil . CheckErr ( o . Validate ( ) )
cmdutil . CheckErr ( o . Run ( ) )
} ,
ValidArgs : validArgs ,
}
// bind flag structs
o . RecordFlags . AddFlags ( cmd )
o . PrintFlags . AddFlags ( cmd )
2020-12-01 01:06:26 +00:00
cmd . Flags ( ) . String ( "generator" , "horizontalpodautoscaler/v1" , i18n . T ( "The name of the API generator to use. Currently there is only 1 generator." ) )
cmd . Flags ( ) . MarkDeprecated ( "generator" , "has no effect and will be removed in the future." )
2019-01-12 04:58:27 +00:00
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." )
cmd . Flags ( ) . Int32Var ( & o . Max , "max" , - 1 , "The upper limit for the number of pods that can be set by the autoscaler. Required." )
cmd . MarkFlagRequired ( "max" )
2019-04-07 17:07:55 +00:00
cmd . Flags ( ) . Int32Var ( & o . CPUPercent , "cpu-percent" , - 1 , fmt . Sprintf ( "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." ) )
2019-01-12 04:58:27 +00:00
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." ) )
cmdutil . AddDryRunFlag ( cmd )
cmdutil . AddFilenameOptionFlags ( cmd , o . FilenameOptions , "identifying the resource to autoscale." )
cmdutil . AddApplyAnnotationFlags ( cmd )
2020-08-10 17:43:49 +00:00
cmdutil . AddFieldManagerFlagVar ( cmd , & o . fieldManager , "kubectl-autoscale" )
2019-01-12 04:58:27 +00:00
return cmd
}
2019-08-30 18:33:25 +00:00
// Complete verifies command line arguments and loads data from the command environment
2019-01-12 04:58:27 +00:00
func ( o * AutoscaleOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command , args [ ] string ) error {
var err error
2020-03-26 21:07:15 +00:00
o . dryRunStrategy , err = cmdutil . GetDryRunStrategy ( cmd )
if err != nil {
return err
}
dynamicClient , err := f . DynamicClient ( )
if err != nil {
return err
}
2019-04-07 17:07:55 +00:00
discoveryClient , err := f . ToDiscoveryClient ( )
if err != nil {
return err
}
2021-03-18 22:40:29 +00:00
o . dryRunVerifier = resource . NewDryRunVerifier ( dynamicClient , f . OpenAPIGetter ( ) )
2020-03-26 21:07:15 +00:00
o . createAnnotation = cmdutil . GetFlagBool ( cmd , cmdutil . ApplyAnnotationsFlag )
o . builder = f . NewBuilder ( )
2019-04-07 17:07:55 +00:00
o . scaleKindResolver = scale . NewDiscoveryScaleKindResolver ( discoveryClient )
2019-01-12 04:58:27 +00:00
o . args = args
o . RecordFlags . Complete ( cmd )
o . Recorder , err = o . RecordFlags . ToRecorder ( )
if err != nil {
return err
}
kubeClient , err := f . KubernetesClientSet ( )
if err != nil {
return err
}
o . HPAClient = kubeClient . AutoscalingV1 ( )
o . namespace , o . enforceNamespace , err = f . ToRawKubeConfigLoader ( ) . Namespace ( )
if err != nil {
return err
}
o . ToPrinter = func ( operation string ) ( printers . ResourcePrinter , error ) {
o . PrintFlags . NamePrintFlags . Operation = operation
2020-03-26 21:07:15 +00:00
cmdutil . PrintFlagsWithDryRunStrategy ( o . PrintFlags , o . dryRunStrategy )
2019-01-12 04:58:27 +00:00
return o . PrintFlags . ToPrinter ( )
}
return nil
}
2019-08-30 18:33:25 +00:00
// Validate checks that the provided attach options are specified.
2019-01-12 04:58:27 +00:00
func ( o * AutoscaleOptions ) Validate ( ) error {
if o . Max < 1 {
return fmt . Errorf ( "--max=MAXPODS is required and must be at least 1, max: %d" , o . Max )
}
if o . Max < o . Min {
return fmt . Errorf ( "--max=MAXPODS must be larger or equal to --min=MINPODS, max: %d, min: %d" , o . Max , o . Min )
}
return nil
}
2019-08-30 18:33:25 +00:00
// Run performs the execution
2019-01-12 04:58:27 +00:00
func ( o * AutoscaleOptions ) Run ( ) error {
r := o . builder .
2019-04-07 17:07:55 +00:00
Unstructured ( ) .
2019-01-12 04:58:27 +00:00
ContinueOnError ( ) .
NamespaceParam ( o . namespace ) . DefaultNamespace ( ) .
FilenameParam ( o . enforceNamespace , o . FilenameOptions ) .
ResourceTypeOrNameArgs ( false , o . args ... ) .
Flatten ( ) .
Do ( )
if err := r . Err ( ) ; err != nil {
return err
}
count := 0
err := r . Visit ( func ( info * resource . Info , err error ) error {
if err != nil {
return err
}
mapping := info . ResourceMapping ( )
2019-04-07 17:07:55 +00:00
gvr := mapping . GroupVersionKind . GroupVersion ( ) . WithResource ( mapping . Resource . Resource )
if _ , err := o . scaleKindResolver . ScaleForResource ( gvr ) ; err != nil {
return fmt . Errorf ( "cannot autoscale a %v: %v" , mapping . GroupVersionKind . Kind , err )
2019-01-12 04:58:27 +00:00
}
2020-12-01 01:06:26 +00:00
hpa := o . createHorizontalPodAutoscaler ( info . Name , mapping )
2019-01-12 04:58:27 +00:00
if err := o . Recorder . Record ( hpa ) ; err != nil {
klog . V ( 4 ) . Infof ( "error recording current command: %v" , err )
}
2020-03-26 21:07:15 +00:00
if o . dryRunStrategy == cmdutil . DryRunClient {
2019-01-12 04:58:27 +00:00
count ++
printer , err := o . ToPrinter ( "created" )
if err != nil {
return err
}
return printer . PrintObj ( hpa , o . Out )
}
2019-09-27 21:51:53 +00:00
if err := util . CreateOrUpdateAnnotation ( o . createAnnotation , hpa , scheme . DefaultJSONEncoder ( ) ) ; err != nil {
2019-01-12 04:58:27 +00:00
return err
}
2020-03-26 21:07:15 +00:00
createOptions := metav1 . CreateOptions { }
2020-08-10 17:43:49 +00:00
if o . fieldManager != "" {
createOptions . FieldManager = o . fieldManager
}
2020-03-26 21:07:15 +00:00
if o . dryRunStrategy == cmdutil . DryRunServer {
if err := o . dryRunVerifier . HasSupport ( hpa . GroupVersionKind ( ) ) ; err != nil {
return err
}
createOptions . DryRun = [ ] string { metav1 . DryRunAll }
}
actualHPA , err := o . HPAClient . HorizontalPodAutoscalers ( o . namespace ) . Create ( context . TODO ( ) , hpa , createOptions )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
count ++
printer , err := o . ToPrinter ( "autoscaled" )
if err != nil {
return err
}
return printer . PrintObj ( actualHPA , o . Out )
} )
if err != nil {
return err
}
if count == 0 {
return fmt . Errorf ( "no objects passed to autoscale" )
}
return nil
}
2020-12-01 01:06:26 +00:00
func ( o * AutoscaleOptions ) createHorizontalPodAutoscaler ( refName string , mapping * meta . RESTMapping ) * autoscalingv1 . HorizontalPodAutoscaler {
name := o . Name
if len ( name ) == 0 {
name = refName
}
scaler := autoscalingv1 . HorizontalPodAutoscaler {
ObjectMeta : metav1 . ObjectMeta {
Name : name ,
} ,
Spec : autoscalingv1 . HorizontalPodAutoscalerSpec {
ScaleTargetRef : autoscalingv1 . CrossVersionObjectReference {
APIVersion : mapping . GroupVersionKind . GroupVersion ( ) . String ( ) ,
Kind : mapping . GroupVersionKind . Kind ,
Name : refName ,
} ,
MaxReplicas : o . Max ,
} ,
}
if o . Min > 0 {
v := int32 ( o . Min )
scaler . Spec . MinReplicas = & v
}
if o . CPUPercent >= 0 {
c := int32 ( o . CPUPercent )
scaler . Spec . TargetCPUUtilizationPercentage = & c
}
return & scaler
}