sigs.k8s.io/kueue@v0.6.2/pkg/workload/resources.go (about) 1 /* 2 Copyright 2023 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 workload 18 19 import ( 20 "context" 21 "fmt" 22 23 corev1 "k8s.io/api/core/v1" 24 nodev1 "k8s.io/api/node/v1" 25 "k8s.io/apimachinery/pkg/types" 26 ctrl "sigs.k8s.io/controller-runtime" 27 "sigs.k8s.io/controller-runtime/pkg/client" 28 29 kueue "sigs.k8s.io/kueue/apis/kueue/v1beta1" 30 "sigs.k8s.io/kueue/pkg/controller/core/indexer" 31 "sigs.k8s.io/kueue/pkg/util/limitrange" 32 "sigs.k8s.io/kueue/pkg/util/resource" 33 ) 34 35 // We do not verify Pod's RuntimeClass legality here as this will be performed in admission controller. 36 // As a result, the pod's Overhead is not always correct. E.g. if we set a non-existent runtime class name to 37 // `pod.Spec.RuntimeClassName` and we also set the `pod.Spec.Overhead`, in real world, the pod creation will be 38 // rejected due to the mismatch with RuntimeClass. However, in the future we assume that they are correct. 39 func handlePodOverhead(ctx context.Context, cl client.Client, wl *kueue.Workload) []error { 40 var errs []error 41 for i := range wl.Spec.PodSets { 42 podSpec := &wl.Spec.PodSets[i].Template.Spec 43 if podSpec.RuntimeClassName != nil && len(podSpec.Overhead) == 0 { 44 var runtimeClass nodev1.RuntimeClass 45 if err := cl.Get(ctx, types.NamespacedName{Name: *podSpec.RuntimeClassName}, &runtimeClass); err != nil { 46 errs = append(errs, fmt.Errorf("in podSet %s: %w", wl.Spec.PodSets[i].Name, err)) 47 continue 48 } 49 if runtimeClass.Overhead != nil { 50 podSpec.Overhead = runtimeClass.Overhead.PodFixed 51 } 52 } 53 } 54 return errs 55 } 56 57 func handlePodLimitRange(ctx context.Context, cl client.Client, wl *kueue.Workload) error { 58 // get the list of limit ranges 59 var list corev1.LimitRangeList 60 if err := cl.List(ctx, &list, &client.ListOptions{Namespace: wl.Namespace}, client.MatchingFields{indexer.LimitRangeHasContainerType: "true"}); err != nil { 61 return err 62 } 63 64 if len(list.Items) == 0 { 65 return nil 66 } 67 summary := limitrange.Summarize(list.Items...) 68 containerLimits, found := summary[corev1.LimitTypeContainer] 69 if !found { 70 return nil 71 } 72 73 for pi := range wl.Spec.PodSets { 74 pod := &wl.Spec.PodSets[pi].Template.Spec 75 for ci := range pod.InitContainers { 76 res := &pod.InitContainers[ci].Resources 77 res.Limits = resource.MergeResourceListKeepFirst(res.Limits, containerLimits.Default) 78 res.Requests = resource.MergeResourceListKeepFirst(res.Requests, containerLimits.DefaultRequest) 79 } 80 for ci := range pod.Containers { 81 res := &pod.Containers[ci].Resources 82 res.Limits = resource.MergeResourceListKeepFirst(res.Limits, containerLimits.Default) 83 res.Requests = resource.MergeResourceListKeepFirst(res.Requests, containerLimits.DefaultRequest) 84 } 85 } 86 return nil 87 } 88 89 func handleLimitsToRequests(wl *kueue.Workload) { 90 for pi := range wl.Spec.PodSets { 91 pod := &wl.Spec.PodSets[pi].Template.Spec 92 for ci := range pod.InitContainers { 93 res := &pod.InitContainers[ci].Resources 94 res.Requests = resource.MergeResourceListKeepFirst(res.Requests, res.Limits) 95 } 96 for ci := range pod.Containers { 97 res := &pod.Containers[ci].Resources 98 res.Requests = resource.MergeResourceListKeepFirst(res.Requests, res.Limits) 99 } 100 } 101 } 102 103 // AdjustResources adjusts the resource requests of a workload based on: 104 // - PodOverhead 105 // - LimitRanges 106 // - Limits 107 func AdjustResources(ctx context.Context, cl client.Client, wl *kueue.Workload) { 108 log := ctrl.LoggerFrom(ctx) 109 for _, err := range handlePodOverhead(ctx, cl, wl) { 110 log.Error(err, "Failures adjusting requests for pod overhead") 111 } 112 if err := handlePodLimitRange(ctx, cl, wl); err != nil { 113 log.Error(err, "Failed adjusting requests for LimitRanges") 114 } 115 handleLimitsToRequests(wl) 116 }