@@ -23,8 +23,13 @@ import (
2323
2424 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
2525 structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
26+ apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
2627 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728 "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"
2833)
2934
3035func jsonPtr (x interface {}) * apiextensions.JSON {
@@ -34,8 +39,9 @@ func jsonPtr(x interface{}) *apiextensions.JSON {
3439
3540func TestDefaultValidationWithCostBudget (t * testing.T ) {
3641 tests := []struct {
37- name string
38- input apiextensions.CustomResourceValidation
42+ name string
43+ input apiextensions.CustomResourceValidation
44+ features []featuregate.Feature
3945 }{
4046 {
4147 name : "default cel validation" ,
@@ -98,6 +104,10 @@ func TestDefaultValidationWithCostBudget(t *testing.T) {
98104 for _ , tt := range tests {
99105 ctx := context .TODO ()
100106 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+
101111 schema := tt .input .OpenAPIV3Schema
102112 ss , err := structuralschema .NewStructural (schema )
103113 if err != nil {
@@ -148,3 +158,103 @@ func TestDefaultValidationWithCostBudget(t *testing.T) {
148158 })
149159 }
150160}
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