@@ -29,10 +29,14 @@ import (
29
29
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
30
30
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
31
31
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model"
32
+ apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
32
33
"k8s.io/apimachinery/pkg/util/version"
33
34
celconfig "k8s.io/apiserver/pkg/apis/cel"
34
35
"k8s.io/apiserver/pkg/cel"
35
36
"k8s.io/apiserver/pkg/cel/environment"
37
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
38
+ featuregatetesting "k8s.io/component-base/featuregate/testing"
39
+ "k8s.io/utils/ptr"
36
40
)
37
41
38
42
const (
@@ -151,12 +155,99 @@ func (v transitionRuleMatcher) String() string {
151
155
}
152
156
153
157
func TestCelCompilation (t * testing.T ) {
158
+ defer featuregatetesting .SetFeatureGateDuringTest (t , utilfeature .DefaultFeatureGate , apiextensionsfeatures .CRDValidationRatcheting , true )()
154
159
cases := []struct {
155
160
name string
156
161
input schema.Structural
157
162
expectedResults []validationMatcher
158
163
unmodified bool
159
164
}{
165
+ {
166
+ name : "optional primitive transition rule type checking" ,
167
+ input : schema.Structural {
168
+ Generic : schema.Generic {
169
+ Type : "integer" ,
170
+ },
171
+ Extensions : schema.Extensions {
172
+ XValidations : apiextensions.ValidationRules {
173
+ {Rule : "self >= oldSelf.value()" , OptionalOldSelf : ptr .To (true )},
174
+ {Rule : "self >= oldSelf.orValue(1)" , OptionalOldSelf : ptr .To (true )},
175
+ {Rule : "oldSelf.hasValue() ? self >= oldSelf.value() : true" , OptionalOldSelf : ptr .To (true )},
176
+ {Rule : "self >= oldSelf" , OptionalOldSelf : ptr .To (true )},
177
+ {Rule : "self >= oldSelf.orValue('')" , OptionalOldSelf : ptr .To (true )},
178
+ },
179
+ },
180
+ },
181
+ expectedResults : []validationMatcher {
182
+ matchesAll (noError (), transitionRule (true )),
183
+ matchesAll (noError (), transitionRule (true )),
184
+ matchesAll (noError (), transitionRule (true )),
185
+ matchesAll (invalidError ("optional" )),
186
+ matchesAll (invalidError ("orValue" )),
187
+ },
188
+ },
189
+ {
190
+ name : "optional complex transition rule type checking" ,
191
+ input : schema.Structural {
192
+ Generic : schema.Generic {
193
+ Type : "object" ,
194
+ },
195
+ Properties : map [string ]schema.Structural {
196
+ "i" : {Generic : schema.Generic {Type : "integer" }},
197
+ "b" : {Generic : schema.Generic {Type : "boolean" }},
198
+ "s" : {Generic : schema.Generic {Type : "string" }},
199
+ "a" : {
200
+ Generic : schema.Generic {Type : "array" },
201
+ Items : & schema.Structural {Generic : schema.Generic {Type : "integer" }},
202
+ },
203
+ "o" : {
204
+ Generic : schema.Generic {Type : "object" },
205
+ Properties : map [string ]schema.Structural {
206
+ "i" : {Generic : schema.Generic {Type : "integer" }},
207
+ "b" : {Generic : schema.Generic {Type : "boolean" }},
208
+ "s" : {Generic : schema.Generic {Type : "string" }},
209
+ "a" : {
210
+ Generic : schema.Generic {Type : "array" },
211
+ Items : & schema.Structural {Generic : schema.Generic {Type : "integer" }},
212
+ },
213
+ "o" : {
214
+ Generic : schema.Generic {Type : "object" },
215
+ },
216
+ },
217
+ },
218
+ },
219
+ Extensions : schema.Extensions {
220
+ XValidations : apiextensions.ValidationRules {
221
+ {Rule : "self.i >= oldSelf.i.value()" , OptionalOldSelf : ptr .To (true )},
222
+ {Rule : "self.s == oldSelf.s.value()" , OptionalOldSelf : ptr .To (true )},
223
+ {Rule : "self.b == oldSelf.b.value()" , OptionalOldSelf : ptr .To (true )},
224
+ {Rule : "self.o == oldSelf.o.value()" , OptionalOldSelf : ptr .To (true )},
225
+ {Rule : "self.o.i >= oldSelf.o.i.value()" , OptionalOldSelf : ptr .To (true )},
226
+ {Rule : "self.o.s == oldSelf.o.s.value()" , OptionalOldSelf : ptr .To (true )},
227
+ {Rule : "self.o.b == oldSelf.o.b.value()" , OptionalOldSelf : ptr .To (true )},
228
+ {Rule : "self.o.o == oldSelf.o.o.value()" , OptionalOldSelf : ptr .To (true )},
229
+ {Rule : "self.o.i >= oldSelf.o.i.orValue(1)" , OptionalOldSelf : ptr .To (true )},
230
+ {Rule : "oldSelf.hasValue() ? self.o.i >= oldSelf.o.i.value() : true" , OptionalOldSelf : ptr .To (true )},
231
+ {Rule : "self.o.i >= oldSelf.o.i" , OptionalOldSelf : ptr .To (true )},
232
+ {Rule : "self.o.i >= oldSelf.o.s.orValue(0)" , OptionalOldSelf : ptr .To (true )},
233
+ },
234
+ },
235
+ },
236
+ expectedResults : []validationMatcher {
237
+ matchesAll (noError (), transitionRule (true )),
238
+ matchesAll (noError (), transitionRule (true )),
239
+ matchesAll (noError (), transitionRule (true )),
240
+ matchesAll (noError (), transitionRule (true )),
241
+ matchesAll (noError (), transitionRule (true )),
242
+ matchesAll (noError (), transitionRule (true )),
243
+ matchesAll (noError (), transitionRule (true )),
244
+ matchesAll (noError (), transitionRule (true )),
245
+ matchesAll (noError (), transitionRule (true )),
246
+ matchesAll (noError (), transitionRule (true )),
247
+ matchesAll (invalidError ("optional" )),
248
+ matchesAll (invalidError ("orValue" )),
249
+ },
250
+ },
160
251
{
161
252
name : "valid object" ,
162
253
input : schema.Structural {
0 commit comments