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  }