set_resources.go 10 KB

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