autoscale.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. Copyright 2015 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 autoscale
  14. import (
  15. "fmt"
  16. "github.com/spf13/cobra"
  17. "k8s.io/klog"
  18. autoscalingv1 "k8s.io/api/autoscaling/v1"
  19. "k8s.io/apimachinery/pkg/api/meta"
  20. "k8s.io/cli-runtime/pkg/genericclioptions"
  21. "k8s.io/cli-runtime/pkg/printers"
  22. "k8s.io/cli-runtime/pkg/resource"
  23. autoscalingv1client "k8s.io/client-go/kubernetes/typed/autoscaling/v1"
  24. "k8s.io/client-go/scale"
  25. "k8s.io/kubernetes/pkg/kubectl"
  26. cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  27. "k8s.io/kubernetes/pkg/kubectl/generate"
  28. generateversioned "k8s.io/kubernetes/pkg/kubectl/generate/versioned"
  29. "k8s.io/kubernetes/pkg/kubectl/scheme"
  30. "k8s.io/kubernetes/pkg/kubectl/util/i18n"
  31. "k8s.io/kubernetes/pkg/kubectl/util/templates"
  32. )
  33. var (
  34. autoscaleLong = templates.LongDesc(i18n.T(`
  35. Creates an autoscaler that automatically chooses and sets the number of pods that run in a kubernetes cluster.
  36. Looks up a Deployment, ReplicaSet, StatefulSet, or ReplicationController by name and creates an autoscaler that uses the given resource as a reference.
  37. An autoscaler can automatically increase or decrease number of pods deployed within the system as needed.`))
  38. autoscaleExample = templates.Examples(i18n.T(`
  39. # 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:
  40. kubectl autoscale deployment foo --min=2 --max=10
  41. # Auto scale a replication controller "foo", with the number of pods between 1 and 5, target CPU utilization at 80%:
  42. kubectl autoscale rc foo --max=5 --cpu-percent=80`))
  43. )
  44. // AutoscaleOptions declare the arguments accepted by the Autoscale command
  45. type AutoscaleOptions struct {
  46. FilenameOptions *resource.FilenameOptions
  47. RecordFlags *genericclioptions.RecordFlags
  48. Recorder genericclioptions.Recorder
  49. PrintFlags *genericclioptions.PrintFlags
  50. ToPrinter func(string) (printers.ResourcePrinter, error)
  51. Name string
  52. Generator string
  53. Min int32
  54. Max int32
  55. CPUPercent int32
  56. createAnnotation bool
  57. args []string
  58. enforceNamespace bool
  59. namespace string
  60. dryRun bool
  61. builder *resource.Builder
  62. generatorFunc func(string, *meta.RESTMapping) (generate.StructuredGenerator, error)
  63. HPAClient autoscalingv1client.HorizontalPodAutoscalersGetter
  64. scaleKindResolver scale.ScaleKindResolver
  65. genericclioptions.IOStreams
  66. }
  67. // NewAutoscaleOptions creates the options for autoscale
  68. func NewAutoscaleOptions(ioStreams genericclioptions.IOStreams) *AutoscaleOptions {
  69. return &AutoscaleOptions{
  70. PrintFlags: genericclioptions.NewPrintFlags("autoscaled").WithTypeSetter(scheme.Scheme),
  71. FilenameOptions: &resource.FilenameOptions{},
  72. RecordFlags: genericclioptions.NewRecordFlags(),
  73. Recorder: genericclioptions.NoopRecorder{},
  74. IOStreams: ioStreams,
  75. }
  76. }
  77. // NewCmdAutoscale returns the autoscale Cobra command
  78. func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
  79. o := NewAutoscaleOptions(ioStreams)
  80. validArgs := []string{"deployment", "replicaset", "replicationcontroller"}
  81. cmd := &cobra.Command{
  82. Use: "autoscale (-f FILENAME | TYPE NAME | TYPE/NAME) [--min=MINPODS] --max=MAXPODS [--cpu-percent=CPU]",
  83. DisableFlagsInUseLine: true,
  84. Short: i18n.T("Auto-scale a Deployment, ReplicaSet, or ReplicationController"),
  85. Long: autoscaleLong,
  86. Example: autoscaleExample,
  87. Run: func(cmd *cobra.Command, args []string) {
  88. cmdutil.CheckErr(o.Complete(f, cmd, args))
  89. cmdutil.CheckErr(o.Validate())
  90. cmdutil.CheckErr(o.Run())
  91. },
  92. ValidArgs: validArgs,
  93. }
  94. // bind flag structs
  95. o.RecordFlags.AddFlags(cmd)
  96. o.PrintFlags.AddFlags(cmd)
  97. cmd.Flags().StringVar(&o.Generator, "generator", generateversioned.HorizontalPodAutoscalerV1GeneratorName, i18n.T("The name of the API generator to use. Currently there is only 1 generator."))
  98. 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.")
  99. cmd.Flags().Int32Var(&o.Max, "max", -1, "The upper limit for the number of pods that can be set by the autoscaler. Required.")
  100. cmd.MarkFlagRequired("max")
  101. 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."))
  102. 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."))
  103. cmdutil.AddDryRunFlag(cmd)
  104. cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, "identifying the resource to autoscale.")
  105. cmdutil.AddApplyAnnotationFlags(cmd)
  106. return cmd
  107. }
  108. // Complete verifies command line arguments and loads data from the command environment
  109. func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
  110. var err error
  111. o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run")
  112. o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
  113. o.builder = f.NewBuilder()
  114. discoveryClient, err := f.ToDiscoveryClient()
  115. if err != nil {
  116. return err
  117. }
  118. o.scaleKindResolver = scale.NewDiscoveryScaleKindResolver(discoveryClient)
  119. o.args = args
  120. o.RecordFlags.Complete(cmd)
  121. o.Recorder, err = o.RecordFlags.ToRecorder()
  122. if err != nil {
  123. return err
  124. }
  125. kubeClient, err := f.KubernetesClientSet()
  126. if err != nil {
  127. return err
  128. }
  129. o.HPAClient = kubeClient.AutoscalingV1()
  130. // get the generator
  131. o.generatorFunc = func(name string, mapping *meta.RESTMapping) (generate.StructuredGenerator, error) {
  132. switch o.Generator {
  133. case generateversioned.HorizontalPodAutoscalerV1GeneratorName:
  134. return &generateversioned.HorizontalPodAutoscalerGeneratorV1{
  135. Name: name,
  136. MinReplicas: o.Min,
  137. MaxReplicas: o.Max,
  138. CPUPercent: o.CPUPercent,
  139. ScaleRefName: name,
  140. ScaleRefKind: mapping.GroupVersionKind.Kind,
  141. ScaleRefAPIVersion: mapping.GroupVersionKind.GroupVersion().String(),
  142. }, nil
  143. default:
  144. return nil, cmdutil.UsageErrorf(cmd, "Generator %s not supported. ", o.Generator)
  145. }
  146. }
  147. o.namespace, o.enforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
  148. if err != nil {
  149. return err
  150. }
  151. o.ToPrinter = func(operation string) (printers.ResourcePrinter, error) {
  152. o.PrintFlags.NamePrintFlags.Operation = operation
  153. if o.dryRun {
  154. o.PrintFlags.Complete("%s (dry run)")
  155. }
  156. return o.PrintFlags.ToPrinter()
  157. }
  158. return nil
  159. }
  160. // Validate checks that the provided attach options are specified.
  161. func (o *AutoscaleOptions) Validate() error {
  162. if o.Max < 1 {
  163. return fmt.Errorf("--max=MAXPODS is required and must be at least 1, max: %d", o.Max)
  164. }
  165. if o.Max < o.Min {
  166. return fmt.Errorf("--max=MAXPODS must be larger or equal to --min=MINPODS, max: %d, min: %d", o.Max, o.Min)
  167. }
  168. return nil
  169. }
  170. // Run performs the execution
  171. func (o *AutoscaleOptions) Run() error {
  172. r := o.builder.
  173. Unstructured().
  174. ContinueOnError().
  175. NamespaceParam(o.namespace).DefaultNamespace().
  176. FilenameParam(o.enforceNamespace, o.FilenameOptions).
  177. ResourceTypeOrNameArgs(false, o.args...).
  178. Flatten().
  179. Do()
  180. if err := r.Err(); err != nil {
  181. return err
  182. }
  183. count := 0
  184. err := r.Visit(func(info *resource.Info, err error) error {
  185. if err != nil {
  186. return err
  187. }
  188. mapping := info.ResourceMapping()
  189. gvr := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource.Resource)
  190. if _, err := o.scaleKindResolver.ScaleForResource(gvr); err != nil {
  191. return fmt.Errorf("cannot autoscale a %v: %v", mapping.GroupVersionKind.Kind, err)
  192. }
  193. generator, err := o.generatorFunc(info.Name, mapping)
  194. if err != nil {
  195. return err
  196. }
  197. // Generate new object
  198. object, err := generator.StructuredGenerate()
  199. if err != nil {
  200. return err
  201. }
  202. hpa, ok := object.(*autoscalingv1.HorizontalPodAutoscaler)
  203. if !ok {
  204. return fmt.Errorf("generator made %T, not autoscalingv1.HorizontalPodAutoscaler", object)
  205. }
  206. if err := o.Recorder.Record(hpa); err != nil {
  207. klog.V(4).Infof("error recording current command: %v", err)
  208. }
  209. if o.dryRun {
  210. count++
  211. printer, err := o.ToPrinter("created")
  212. if err != nil {
  213. return err
  214. }
  215. return printer.PrintObj(hpa, o.Out)
  216. }
  217. if err := kubectl.CreateOrUpdateAnnotation(o.createAnnotation, hpa, scheme.DefaultJSONEncoder()); err != nil {
  218. return err
  219. }
  220. actualHPA, err := o.HPAClient.HorizontalPodAutoscalers(o.namespace).Create(hpa)
  221. if err != nil {
  222. return err
  223. }
  224. count++
  225. printer, err := o.ToPrinter("autoscaled")
  226. if err != nil {
  227. return err
  228. }
  229. return printer.PrintObj(actualHPA, o.Out)
  230. })
  231. if err != nil {
  232. return err
  233. }
  234. if count == 0 {
  235. return fmt.Errorf("no objects passed to autoscale")
  236. }
  237. return nil
  238. }