From 697934c96b8a8facf98bac4e9068b3f838d6325a Mon Sep 17 00:00:00 2001 From: Omer Aplatony Date: Thu, 21 Aug 2025 14:01:58 +0000 Subject: [PATCH] Add feature flag to admission controller Signed-off-by: Omer Aplatony --- .../resource/vpa/handler.go | 14 +++++ .../resource/vpa/handler_test.go | 55 +++++++++++++++++++ .../pkg/features/features.go | 2 +- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go index 6b804db573..3acb9515a7 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go @@ -137,6 +137,11 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error { return fmt.Errorf("containerPolicies.ContainerName is required") } + // check that perVPA is on if being used + if err := validatePerVPAFeatureFlag(&policy); err != nil { + return err + } + // Validate OOMBumpUpRatio if policy.OOMBumpUpRatio != nil { ratio := float64(policy.OOMBumpUpRatio.MilliValue()) / 1000.0 @@ -217,3 +222,12 @@ func validateMemoryResolution(val apires.Quantity) error { } return nil } + +func validatePerVPAFeatureFlag(policy *vpa_types.ContainerResourcePolicy) error { + featureFlagOn := features.Enabled(features.PerVPAConfig) + perVPA := policy.OOMBumpUpRatio != nil || policy.OOMMinBumpUp != nil + if !featureFlagOn && perVPA { + return fmt.Errorf("OOMBumpUpRatio and OOMMinBumpUp are not supported when feature flag %s is disabled", features.PerVPAConfig) + } + return nil +} diff --git a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go index 9074f37dff..78044ff61b 100644 --- a/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go +++ b/vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go @@ -51,6 +51,7 @@ func TestValidateVPA(t *testing.T) { isCreate bool expectError error inPlaceOrRecreateFeatureGateDisabled bool + PerVPAConfigDisabled bool }{ { name: "empty update", @@ -319,10 +320,64 @@ func TestValidateVPA(t *testing.T) { }, }, }, + { + name: "per-vpa config active and used", + vpa: vpa_types.VerticalPodAutoscaler{ + Spec: vpa_types.VerticalPodAutoscalerSpec{ + UpdatePolicy: &vpa_types.PodUpdatePolicy{ + UpdateMode: &validUpdateMode, + }, + ResourcePolicy: &vpa_types.PodResourcePolicy{ + ContainerPolicies: []vpa_types.ContainerResourcePolicy{ + { + ContainerName: "loot box", + Mode: &validScalingMode, + MinAllowed: apiv1.ResourceList{ + cpu: resource.MustParse("10"), + }, + MaxAllowed: apiv1.ResourceList{ + cpu: resource.MustParse("100"), + }, + OOMBumpUpRatio: resource.NewQuantity(2, resource.DecimalSI), + }, + }, + }, + }, + }, + PerVPAConfigDisabled: false, + }, + { + name: "per-vpa config disabled and used", + vpa: vpa_types.VerticalPodAutoscaler{ + Spec: vpa_types.VerticalPodAutoscalerSpec{ + UpdatePolicy: &vpa_types.PodUpdatePolicy{ + UpdateMode: &validUpdateMode, + }, + ResourcePolicy: &vpa_types.PodResourcePolicy{ + ContainerPolicies: []vpa_types.ContainerResourcePolicy{ + { + ContainerName: "loot box", + Mode: &validScalingMode, + MinAllowed: apiv1.ResourceList{ + cpu: resource.MustParse("10"), + }, + MaxAllowed: apiv1.ResourceList{ + cpu: resource.MustParse("100"), + }, + OOMMinBumpUp: resource.NewQuantity(2, resource.DecimalSI), + }, + }, + }, + }, + }, + PerVPAConfigDisabled: true, + expectError: fmt.Errorf("OOMBumpUpRatio and OOMMinBumpUp are not supported when feature flag PerVPAConfig is disabled"), + }, } for _, tc := range tests { t.Run(fmt.Sprintf("test case: %s", tc.name), func(t *testing.T) { featuregatetesting.SetFeatureGateDuringTest(t, features.MutableFeatureGate, features.InPlaceOrRecreate, !tc.inPlaceOrRecreateFeatureGateDisabled) + featuregatetesting.SetFeatureGateDuringTest(t, features.MutableFeatureGate, features.PerVPAConfig, !tc.PerVPAConfigDisabled) err := ValidateVPA(&tc.vpa, tc.isCreate) if tc.expectError == nil { assert.NoError(t, err) diff --git a/vertical-pod-autoscaler/pkg/features/features.go b/vertical-pod-autoscaler/pkg/features/features.go index 2c6ddc6a1a..5f92b6013c 100644 --- a/vertical-pod-autoscaler/pkg/features/features.go +++ b/vertical-pod-autoscaler/pkg/features/features.go @@ -50,7 +50,7 @@ const ( InPlaceOrRecreate featuregate.Feature = "InPlaceOrRecreate" // alpha: v1.5.0 - // components: recommender + // components: recommender, updater // PerVPAConfig enables the ability to specify component-specific configuration // parameters at the individual VPA object level. This allows for different