Skip to content

Commit a0cf017

Browse files
committed
VPA: Allow admission-controller to validate in-place spec
Only allow VPA objects with InPlaceOrRecreate update mode to be created if InPlaceOrRecreate feature gate is enabled. If a VPA object already exists with this mode on, and the feature gate is disabled, this prevents further objects to be created with InPlaceOrRecreate, but this does not prevent the existing InPlaceOrRecreate VPA objects with from being modified. Signed-off-by: Max Cao <[email protected]>
1 parent 4486391 commit a0cf017

File tree

2 files changed

+57
-8
lines changed

2 files changed

+57
-8
lines changed

vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,17 @@ import (
3030

3131
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/admission-controller/resource"
3232
vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
33+
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features"
3334
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission"
3435
)
3536

3637
var (
3738
possibleUpdateModes = map[vpa_types.UpdateMode]interface{}{
38-
vpa_types.UpdateModeOff: struct{}{},
39-
vpa_types.UpdateModeInitial: struct{}{},
40-
vpa_types.UpdateModeRecreate: struct{}{},
41-
vpa_types.UpdateModeAuto: struct{}{},
39+
vpa_types.UpdateModeOff: struct{}{},
40+
vpa_types.UpdateModeInitial: struct{}{},
41+
vpa_types.UpdateModeRecreate: struct{}{},
42+
vpa_types.UpdateModeAuto: struct{}{},
43+
vpa_types.UpdateModeInPlaceOrRecreate: struct{}{},
4244
}
4345

4446
possibleScalingModes = map[vpa_types.ContainerScalingMode]interface{}{
@@ -121,6 +123,9 @@ func ValidateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error {
121123
if _, found := possibleUpdateModes[*mode]; !found {
122124
return fmt.Errorf("unexpected UpdateMode value %s", *mode)
123125
}
126+
if (*mode == vpa_types.UpdateModeInPlaceOrRecreate) && !features.Enabled(features.InPlaceOrRecreate) && isCreate {
127+
return fmt.Errorf("in order to use UpdateMode %s, you must enable feature gate %s in the admission-controller args", vpa_types.UpdateModeInPlaceOrRecreate, features.InPlaceOrRecreate)
128+
}
124129

125130
if minReplicas := vpa.Spec.UpdatePolicy.MinReplicas; minReplicas != nil && *minReplicas <= 0 {
126131
return fmt.Errorf("MinReplicas has to be positive, got %v", *minReplicas)

vertical-pod-autoscaler/pkg/admission-controller/resource/vpa/handler_test.go

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ import (
2424
apiv1 "k8s.io/api/core/v1"
2525
"k8s.io/apimachinery/pkg/api/resource"
2626

27+
featuregatetesting "k8s.io/component-base/featuregate/testing"
28+
2729
vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
30+
"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/features"
2831
)
2932

3033
const (
@@ -42,11 +45,13 @@ func TestValidateVPA(t *testing.T) {
4245
validScalingMode := vpa_types.ContainerScalingModeAuto
4346
scalingModeOff := vpa_types.ContainerScalingModeOff
4447
controlledValuesRequestsAndLimits := vpa_types.ContainerControlledValuesRequestsAndLimits
48+
inPlaceOrRecreateUpdateMode := vpa_types.UpdateModeInPlaceOrRecreate
4549
tests := []struct {
46-
name string
47-
vpa vpa_types.VerticalPodAutoscaler
48-
isCreate bool
49-
expectError error
50+
name string
51+
vpa vpa_types.VerticalPodAutoscaler
52+
isCreate bool
53+
expectError error
54+
inPlaceOrRecreateFeatureGateDisabled bool
5055
}{
5156
{
5257
name: "empty update",
@@ -78,6 +83,42 @@ func TestValidateVPA(t *testing.T) {
7883
},
7984
expectError: fmt.Errorf("unexpected UpdateMode value bad"),
8085
},
86+
{
87+
name: "creating VPA with InPlaceOrRecreate update mode not allowed by disabled feature gate",
88+
vpa: vpa_types.VerticalPodAutoscaler{
89+
Spec: vpa_types.VerticalPodAutoscalerSpec{
90+
UpdatePolicy: &vpa_types.PodUpdatePolicy{
91+
UpdateMode: &inPlaceOrRecreateUpdateMode,
92+
},
93+
},
94+
},
95+
isCreate: true,
96+
inPlaceOrRecreateFeatureGateDisabled: true,
97+
expectError: fmt.Errorf("in order to use UpdateMode %s, you must enable feature gate %s in the admission-controller args", vpa_types.UpdateModeInPlaceOrRecreate, features.InPlaceOrRecreate),
98+
},
99+
{
100+
name: "updating VPA with InPlaceOrRecreate update mode allowed by disabled feature gate",
101+
vpa: vpa_types.VerticalPodAutoscaler{
102+
Spec: vpa_types.VerticalPodAutoscalerSpec{
103+
UpdatePolicy: &vpa_types.PodUpdatePolicy{
104+
UpdateMode: &inPlaceOrRecreateUpdateMode,
105+
},
106+
},
107+
},
108+
isCreate: false,
109+
inPlaceOrRecreateFeatureGateDisabled: true,
110+
expectError: nil,
111+
},
112+
{
113+
name: "InPlaceOrRecreate update mode enabled by feature gate",
114+
vpa: vpa_types.VerticalPodAutoscaler{
115+
Spec: vpa_types.VerticalPodAutoscalerSpec{
116+
UpdatePolicy: &vpa_types.PodUpdatePolicy{
117+
UpdateMode: &inPlaceOrRecreateUpdateMode,
118+
},
119+
},
120+
},
121+
},
81122
{
82123
name: "zero minReplicas",
83124
vpa: vpa_types.VerticalPodAutoscaler{
@@ -282,6 +323,9 @@ func TestValidateVPA(t *testing.T) {
282323
}
283324
for _, tc := range tests {
284325
t.Run(fmt.Sprintf("test case: %s", tc.name), func(t *testing.T) {
326+
if !tc.inPlaceOrRecreateFeatureGateDisabled {
327+
featuregatetesting.SetFeatureGateDuringTest(t, features.MutableFeatureGate, features.InPlaceOrRecreate, true)
328+
}
285329
err := ValidateVPA(&tc.vpa, tc.isCreate)
286330
if tc.expectError == nil {
287331
assert.NoError(t, err)

0 commit comments

Comments
 (0)