@@ -37,11 +37,13 @@ import (
37
37
"k8s.io/apiserver/pkg/registry/rest"
38
38
apistorage "k8s.io/apiserver/pkg/storage"
39
39
"k8s.io/apiserver/pkg/storage/names"
40
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
40
41
"k8s.io/kubernetes/pkg/api/legacyscheme"
41
42
"k8s.io/kubernetes/pkg/api/pod"
42
43
api "k8s.io/kubernetes/pkg/apis/core"
43
44
"k8s.io/kubernetes/pkg/apis/core/helper"
44
45
corevalidation "k8s.io/kubernetes/pkg/apis/core/validation"
46
+ "k8s.io/kubernetes/pkg/features"
45
47
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
46
48
)
47
49
@@ -123,7 +125,27 @@ func (rcStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object)
123
125
func (rcStrategy ) Validate (ctx context.Context , obj runtime.Object ) field.ErrorList {
124
126
controller := obj .(* api.ReplicationController )
125
127
opts := pod .GetValidationOptionsFromPodTemplate (controller .Spec .Template , nil )
126
- return corevalidation .ValidateReplicationController (controller , opts )
128
+
129
+ // Run imperative validation
130
+ allErrs := corevalidation .ValidateReplicationController (controller , opts )
131
+
132
+ // If DeclarativeValidation feature gate is enabled, also run declarative validation
133
+ if utilfeature .DefaultFeatureGate .Enabled (features .DeclarativeValidation ) {
134
+ // Determine if takeover is enabled
135
+ takeover := utilfeature .DefaultFeatureGate .Enabled (features .DeclarativeValidationTakeover )
136
+
137
+ // Run declarative validation with panic recovery
138
+ declarativeErrs := rest .ValidateDeclarativelyWithRecovery (ctx , nil , legacyscheme .Scheme , controller , takeover )
139
+
140
+ // Compare imperative and declarative errors and log + emit metric if there's a mismatch
141
+ rest .CompareDeclarativeErrorsAndEmitMismatches (ctx , allErrs , declarativeErrs , takeover )
142
+
143
+ // Only apply declarative errors if takeover is enabled
144
+ if takeover {
145
+ allErrs = append (allErrs .RemoveCoveredByDeclarative (), declarativeErrs ... )
146
+ }
147
+ }
148
+ return allErrs
127
149
}
128
150
129
151
// WarningsOnCreate returns warnings for the creation of the given object.
@@ -153,6 +175,8 @@ func (rcStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) f
153
175
newRc := obj .(* api.ReplicationController )
154
176
155
177
opts := pod .GetValidationOptionsFromPodTemplate (newRc .Spec .Template , oldRc .Spec .Template )
178
+ // FIXME: Calling both validator functions here results in redundant calls to ValidateReplicationControllerSpec.
179
+ // This should be fixed to avoid the redundant calls, but carefully.
156
180
validationErrorList := corevalidation .ValidateReplicationController (newRc , opts )
157
181
updateErrorList := corevalidation .ValidateReplicationControllerUpdate (newRc , oldRc , opts )
158
182
errs := append (validationErrorList , updateErrorList ... )
@@ -174,6 +198,23 @@ func (rcStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) f
174
198
}
175
199
}
176
200
201
+ // If DeclarativeValidation feature gate is enabled, also run declarative validation
202
+ if utilfeature .DefaultFeatureGate .Enabled (features .DeclarativeValidation ) {
203
+ // Determine if takeover is enabled
204
+ takeover := utilfeature .DefaultFeatureGate .Enabled (features .DeclarativeValidationTakeover )
205
+
206
+ // Run declarative update validation with panic recovery
207
+ declarativeErrs := rest .ValidateUpdateDeclarativelyWithRecovery (ctx , nil , legacyscheme .Scheme , newRc , oldRc , takeover )
208
+
209
+ // Compare imperative and declarative errors and emit metric if there's a mismatch
210
+ rest .CompareDeclarativeErrorsAndEmitMismatches (ctx , errs , declarativeErrs , takeover )
211
+
212
+ // Only apply declarative errors if takeover is enabled
213
+ if takeover {
214
+ errs = append (errs .RemoveCoveredByDeclarative (), declarativeErrs ... )
215
+ }
216
+ }
217
+
177
218
return errs
178
219
}
179
220
0 commit comments