// Copyright 2020-2026 Project Capsule Authors // SPDX-License-Identifier: Apache-2.0 package workloads import ( "fmt" "strings" inf "gopkg.in/inf.v0" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" ) // PodLevelResourceSupported reports whether Kubernetes permits the resource // name in Pod-level resource requirements. func PodLevelResourceSupported(name corev1.ResourceName) bool { return name == corev1.ResourceCPU || name == corev1.ResourceMemory || strings.HasPrefix(string(name), corev1.ResourceHugePagesPrefix) } // RatioSupportedResource reports whether Capsule can safely calculate a // limit-to-request ratio for the resource. CPU is rounded down to milliCPU; // byte-based resources are rounded down to whole bytes. func RatioSupportedResource(name corev1.ResourceName) bool { return name == corev1.ResourceCPU || name == corev1.ResourceMemory || name == corev1.ResourceEphemeralStorage } // LimitForRatio calculates request * ratio without floating-point arithmetic. // The result is rounded down so it never exceeds the configured maximum ratio. func LimitForRatio( name corev1.ResourceName, request resource.Quantity, ratio resource.Quantity, ) (resource.Quantity, error) { if !RatioSupportedResource(name) { return resource.Quantity{}, fmt.Errorf("ratio is not supported for resource %q", name) } if request.Sign() <= 0 { return resource.Quantity{}, fmt.Errorf("request for resource %q must be greater than zero", name) } if ratio.Cmp(resource.MustParse("1")) < 0 { return resource.Quantity{}, fmt.Errorf("ratio for resource %q must be greater than or equal to 1", name) } product := new(inf.Dec).Mul(request.AsDec(), ratio.AsDec()) scale := inf.Scale(0) if name == corev1.ResourceCPU { scale = inf.Scale(3) } rounded := new(inf.Dec).Round(product, scale, inf.RoundDown) return *resource.NewDecimalQuantity(*rounded, request.Format), nil }