@@ -23,8 +23,13 @@ import (
23
23
24
24
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
25
25
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
26
+ apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
26
27
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27
28
"k8s.io/apimachinery/pkg/util/validation/field"
29
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
30
+ "k8s.io/component-base/featuregate"
31
+ featuregatetesting "k8s.io/component-base/featuregate/testing"
32
+ "k8s.io/utils/ptr"
28
33
)
29
34
30
35
func jsonPtr (x interface {}) * apiextensions.JSON {
@@ -34,8 +39,9 @@ func jsonPtr(x interface{}) *apiextensions.JSON {
34
39
35
40
func TestDefaultValidationWithCostBudget (t * testing.T ) {
36
41
tests := []struct {
37
- name string
38
- input apiextensions.CustomResourceValidation
42
+ name string
43
+ input apiextensions.CustomResourceValidation
44
+ features []featuregate.Feature
39
45
}{
40
46
{
41
47
name : "default cel validation" ,
@@ -98,6 +104,10 @@ func TestDefaultValidationWithCostBudget(t *testing.T) {
98
104
for _ , tt := range tests {
99
105
ctx := context .TODO ()
100
106
t .Run (tt .name , func (t * testing.T ) {
107
+ for _ , f := range tt .features {
108
+ defer featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , f , true )()
109
+ }
110
+
101
111
schema := tt .input .OpenAPIV3Schema
102
112
ss , err := structuralschema .NewStructural (schema )
103
113
if err != nil {
@@ -148,3 +158,103 @@ func TestDefaultValidationWithCostBudget(t *testing.T) {
148
158
})
149
159
}
150
160
}
161
+
162
+ func TestDefaultValidationWithOptionalOldSelf (t * testing.T ) {
163
+ tests := []struct {
164
+ name string
165
+ input apiextensions.CustomResourceValidation
166
+ errors []string
167
+ }{
168
+ {
169
+ name : "invalid default" ,
170
+ input : apiextensions.CustomResourceValidation {
171
+ OpenAPIV3Schema : & apiextensions.JSONSchemaProps {
172
+ Type : "object" ,
173
+ Properties : map [string ]apiextensions.JSONSchemaProps {
174
+ "defaultFailsRatcheting" : {
175
+ Type : "string" ,
176
+ Default : jsonPtr ("default" ),
177
+ XValidations : apiextensions.ValidationRules {
178
+ {
179
+ Rule : "oldSelf.hasValue()" ,
180
+ OptionalOldSelf : ptr .To (true ),
181
+ Message : "foobarErrorMessage" ,
182
+ },
183
+ },
184
+ },
185
+ },
186
+ },
187
+ },
188
+ errors : []string {"foobarErrorMessage" },
189
+ },
190
+ {
191
+ name : "valid default" ,
192
+ input : apiextensions.CustomResourceValidation {
193
+ OpenAPIV3Schema : & apiextensions.JSONSchemaProps {
194
+ Type : "object" ,
195
+ Properties : map [string ]apiextensions.JSONSchemaProps {
196
+ "defaultFailsRatcheting" : {
197
+ Type : "string" ,
198
+ Default : jsonPtr ("default" ),
199
+ XValidations : apiextensions.ValidationRules {
200
+ {
201
+ Rule : "oldSelf.orValue(self) == self" ,
202
+ OptionalOldSelf : ptr .To (true ),
203
+ Message : "foobarErrorMessage" ,
204
+ },
205
+ },
206
+ },
207
+ },
208
+ },
209
+ },
210
+ errors : []string {},
211
+ },
212
+ }
213
+
214
+ for _ , tt := range tests {
215
+ ctx := context .TODO ()
216
+ t .Run (tt .name , func (t * testing.T ) {
217
+ defer featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , apiextensionsfeatures .CRDValidationRatcheting , true )()
218
+ schema := tt .input .OpenAPIV3Schema
219
+ ss , err := structuralschema .NewStructural (schema )
220
+ if err != nil {
221
+ t .Errorf ("unexpected error: %v" , err )
222
+ }
223
+
224
+ f := NewRootObjectFunc ().WithTypeMeta (metav1.TypeMeta {APIVersion : "validation/v1" , Kind : "Validation" })
225
+
226
+ // cost budget is large enough to pass all validation rules
227
+ allErrs , err , _ := validate (ctx , field .NewPath ("test" ), ss , ss , f , false , false , 10 )
228
+ if err != nil {
229
+ t .Errorf ("unexpected error: %v" , err )
230
+ }
231
+
232
+ for _ , err := range allErrs {
233
+ found := false
234
+ for _ , expected := range tt .errors {
235
+ if strings .Contains (err .Error (), expected ) {
236
+ found = true
237
+ break
238
+ }
239
+ }
240
+ if ! found {
241
+ t .Errorf ("unexpected error: %v" , err )
242
+ }
243
+ }
244
+
245
+ for _ , expected := range tt .errors {
246
+ found := false
247
+ for _ , err := range allErrs {
248
+ if strings .Contains (err .Error (), expected ) {
249
+ found = true
250
+ break
251
+ }
252
+ }
253
+ if ! found {
254
+ t .Errorf ("expected error: %v" , expected )
255
+ }
256
+ }
257
+
258
+ })
259
+ }
260
+ }
0 commit comments