diff --git a/internal/webhook/podcpuboost_webhook.go b/internal/webhook/podcpuboost_webhook.go index 3aabfc2..8e20ab9 100644 --- a/internal/webhook/podcpuboost_webhook.go +++ b/internal/webhook/podcpuboost_webhook.go @@ -79,6 +79,10 @@ func (h *podCPUBoostHandler) boostContainerResources(b boost.StartupCPUBoost, po "CPURequests", container.Resources.Requests.Cpu().String(), "CPULimits", container.Resources.Limits.Cpu().String(), ) + if resizeRequiresRestart(container, corev1.ResourceCPU) { + log.Info("skipping container due to restart policy") + continue + } updateBoostAnnotation(annotation, container.Name, container.Resources) resources := policy.NewResources(&container) log = log.WithValues( @@ -108,3 +112,13 @@ func updateBoostAnnotation(annot *bpod.BoostPodAnnotation, containerName string, annot.InitCPULimits[containerName] = cpuLimits.String() } } + +func resizeRequiresRestart(c corev1.Container, r corev1.ResourceName) bool { + for _, p := range c.ResizePolicy { + if p.ResourceName != r { + continue + } + return p.RestartPolicy == corev1.RestartContainer + } + return false +} diff --git a/internal/webhook/podcpuboost_webhook_test.go b/internal/webhook/podcpuboost_webhook_test.go index 808b00b..ca1b6e5 100644 --- a/internal/webhook/podcpuboost_webhook_test.go +++ b/internal/webhook/podcpuboost_webhook_test.go @@ -189,6 +189,22 @@ var _ = Describe("Pod CPU Boost Webhook", func() { Expect(response.Patches).To(HaveLen(3)) }) }) + When("container has restart container resize policy", func() { + BeforeEach(func() { + pod.Spec.Containers[0].ResizePolicy = []corev1.ContainerResizePolicy{ + { + ResourceName: corev1.ResourceCPU, + RestartPolicy: corev1.RestartContainer, + }, + } + }) + It("allows the admission", func() { + Expect(response.Allowed).To(BeTrue()) + }) + It("returns admission with zero patches", func() { + Expect(response.Patches).To(HaveLen(0)) + }) + }) }) When("there is a policy for two containers", func() { var ( diff --git a/internal/webhook/podcpuboost_webhook_test.go.old b/internal/webhook/podcpuboost_webhook_test.go.old deleted file mode 100644 index a0c754a..0000000 --- a/internal/webhook/podcpuboost_webhook_test.go.old +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package webhook - -import ( - "encoding/json" - "testing" - - "github.com/google/kube-startup-cpu-boost/internal/boost" - bpod "github.com/google/kube-startup-cpu-boost/internal/boost/pod" - . "github.com/onsi/ginkgo/v2" - - corev1 "k8s.io/api/core/v1" - apiResource "k8s.io/apimachinery/pkg/api/resource" - "sigs.k8s.io/controller-runtime/pkg/log/zap" -) - -func TestBoostContainersCPU(t *testing.T) { - reqQuantityStr := "1" - reqQuantity, _ := apiResource.ParseQuantity(reqQuantityStr) - limitQuantityStr := "2" - limitQuantity, _ := apiResource.ParseQuantity(limitQuantityStr) - pod := &corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "container-one", - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: reqQuantity, - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: limitQuantity, - }, - }, - }, - { - Name: "container-two", - Resources: corev1.ResourceRequirements{ - Requests: corev1.ResourceList{ - corev1.ResourceCPU: reqQuantity, - }, - Limits: corev1.ResourceList{ - corev1.ResourceCPU: limitQuantity, - }, - }, - }, - }, - }, - } - var boostPerc int64 = 20 - expReqQuantityStr := "1200m" - expLimitQuantityStr := "2400m" - log := zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)) - - handler := &podCPUBoostHandler{} - result, boosted := handler.boostContainersCPU(pod, boostPerc, log) - if !boosted { - t.Fatalf("boosted = %v; want %v", boosted, true) - } - if len(result) != len(pod.Spec.Containers) { - t.Errorf("len(result) = %v; want %v", len(result), len(pod.Spec.Containers)) - } - for i := range result { - cpuReq := result[i].Resources.Requests[corev1.ResourceCPU] - cpuLimit := result[i].Resources.Limits[corev1.ResourceCPU] - if cpuReq.String() != expReqQuantityStr { - t.Errorf("container %d: cpu requests = %v; want %v", i, cpuReq.String(), expReqQuantityStr) - } - if cpuLimit.String() != expLimitQuantityStr { - t.Errorf("container %d: cpu limits = %v; want %v", i, cpuLimit.String(), expLimitQuantityStr) - } - } - annotStr, ok := pod.Annotations[boost.StartupCPUBoostPodAnnotationKey] - if !ok { - t.Fatalf("POD is missing startup CPU boost annotation") - } - annot := &bpod.BoostPodAnnotation{} - if err := json.Unmarshal([]byte(annotStr), annot); err != nil { - t.Fatalf("can't unmarshal boost annotation due to %s", err) - } - if len(annot.InitCPURequests) != len(pod.Spec.Containers) { - t.Fatalf("CPU boost annotation: len(initCPURequests) = %v; want %v", len(annot.InitCPURequests), len(pod.Spec.Containers)) - } - if len(annot.InitCPULimits) != len(pod.Spec.Containers) { - t.Fatalf("CPU boost annotation: len(initCPULimits) = %v; want %v", len(annot.InitCPULimits), len(pod.Spec.Containers)) - } - for _, container := range pod.Spec.Containers { - initReq := annot.InitCPURequests[container.Name] - initLimit := annot.InitCPULimits[container.Name] - if initReq != reqQuantityStr { - t.Errorf("CPU boost annotation: InitCPURequests[%v] = %v; want %v", container.Name, initReq, reqQuantityStr) - } - if initLimit != limitQuantityStr { - t.Errorf("CPU boost annotation: InitCPULimits[%v] = %v; want %v", container.Name, initLimit, limitQuantityStr) - } - } -} - -func TestIncreaseQuantityForResource(t *testing.T) { - quantityStr := "250m" - boostPerc := 120 - expectedQuantityStr := "550m" - quantity, _ := apiResource.ParseQuantity(quantityStr) - log := zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)) - requests := corev1.ResourceList{ - corev1.ResourceCPU: quantity, - } - increased, init, new := increaseQuantityForResource(requests, corev1.ResourceCPU, int64(boostPerc), log) - result := requests[corev1.ResourceCPU] - if !increased { - t.Errorf("increased = %v; want %v", increased, true) - } - if init.String() != quantityStr { - t.Errorf("initial quantity = %v; want %v", init.String(), quantityStr) - } - if new.String() != expectedQuantityStr { - t.Errorf("new quantity = %v; want %v", new.String(), expectedQuantityStr) - } - if result.String() != expectedQuantityStr { - t.Errorf("quantity = %v; want %v", result, expectedQuantityStr) - } -} - -func TestIncreaseQuantity(t *testing.T) { - type input struct { - quantityStr string - boostPerc int64 - } - inputs := []input{ - {"100m", 20}, - {"1.3", 50}, - {"800m", 100}, - {"4", 80}, - {"101m", 325}, - {"1", 20}, - } - expected := []string{ - "120m", - "1950m", - "1600m", - "7200m", - "430m", - "1200m", - } - - for i := range inputs { - quantity, err := apiResource.ParseQuantity(inputs[i].quantityStr) - if err != nil { - t.Fatalf("could not parse quantity due to %s", err) - } - result := increaseQuantity(quantity, inputs[i].boostPerc) - if result.String() != expected[i] { - t.Errorf("input %d, result = %v; want %v", i, result, expected[i]) - } - } -}