Skip to content

Commit 9b28b91

Browse files
committed
Add ConversionReviewVersions to CustomResourceDefinitionSpec and defdefault it to v1beta1
1 parent f7dff47 commit 9b28b91

File tree

7 files changed

+555
-6
lines changed

7 files changed

+555
-6
lines changed

staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/fuzzer/fuzzer.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
6666
Strategy: apiextensions.NoneConverter,
6767
}
6868
}
69+
if obj.Conversion.Strategy == apiextensions.WebhookConverter && len(obj.Conversion.ConversionReviewVersions) == 0 {
70+
obj.Conversion.ConversionReviewVersions = []string{"v1beta1"}
71+
}
6972
},
7073
func(obj *apiextensions.CustomResourceDefinition, c fuzz.Continue) {
7174
c.FuzzNoCustom(obj)

staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ type CustomResourceConversion struct {
8484

8585
// `webhookClientConfig` is the instructions for how to call the webhook if strategy is `Webhook`.
8686
WebhookClientConfig *WebhookClientConfig
87+
88+
// ConversionReviewVersions is an ordered list of preferred `ConversionReview`
89+
// versions the Webhook expects. API server will try to use first version in
90+
// the list which it supports. If none of the versions specified in this list
91+
// supported by API server, conversion will fail for this object.
92+
// If a persisted Webhook configuration specifies allowed versions and does not
93+
// include any versions known to the API Server, calls to the webhook will fail.
94+
// +optional
95+
ConversionReviewVersions []string
8796
}
8897

8998
// WebhookClientConfig contains the information to make a TLS

staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/defaults.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ func SetDefaults_CustomResourceDefinitionSpec(obj *CustomResourceDefinitionSpec)
6868
Strategy: NoneConverter,
6969
}
7070
}
71+
if obj.Conversion.Strategy == WebhookConverter && len(obj.Conversion.ConversionReviewVersions) == 0 {
72+
obj.Conversion.ConversionReviewVersions = []string{SchemeGroupVersion.Version}
73+
}
7174
}
7275

7376
// hasPerVersionColumns returns true if a CRD uses per-version columns.

staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ type CustomResourceConversion struct {
9191
// alpha-level and is only honored by servers that enable the CustomResourceWebhookConversion feature.
9292
// +optional
9393
WebhookClientConfig *WebhookClientConfig `json:"webhookClientConfig,omitempty" protobuf:"bytes,2,name=webhookClientConfig"`
94+
95+
// ConversionReviewVersions is an ordered list of preferred `ConversionReview`
96+
// versions the Webhook expects. API server will try to use first version in
97+
// the list which it supports. If none of the versions specified in this list
98+
// supported by API server, conversion will fail for this object.
99+
// If a persisted Webhook configuration specifies allowed versions and does not
100+
// include any versions known to the API Server, calls to the webhook will fail.
101+
// Default to `['v1beta1']`.
102+
// +optional
103+
ConversionReviewVersions []string `json:"conversionReviewVersions,omitempty" protobuf:"bytes,3,rep,name=conversionReviewVersions"`
94104
}
95105

96106
// WebhookClientConfig contains the information to make a TLS

staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ import (
2424
apiequality "k8s.io/apimachinery/pkg/api/equality"
2525
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
2626
"k8s.io/apimachinery/pkg/util/sets"
27+
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
2728
validationutil "k8s.io/apimachinery/pkg/util/validation"
2829
"k8s.io/apimachinery/pkg/util/validation/field"
2930
utilfeature "k8s.io/apiserver/pkg/util/feature"
3031
"k8s.io/apiserver/pkg/util/webhook"
3132

3233
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
34+
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
3335
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
3436
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
3537
)
@@ -113,6 +115,10 @@ func ValidateCustomResourceDefinitionVersion(version *apiextensions.CustomResour
113115

114116
// ValidateCustomResourceDefinitionSpec statically validates
115117
func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, fldPath *field.Path) field.ErrorList {
118+
return validateCustomResourceDefinitionSpec(spec, true, fldPath)
119+
}
120+
121+
func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, requireRecognizedVersion bool, fldPath *field.Path) field.ErrorList {
116122
allErrs := field.ErrorList{}
117123

118124
if len(spec.Group) == 0 {
@@ -205,7 +211,7 @@ func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
205211
}
206212
}
207213

208-
allErrs = append(allErrs, ValidateCustomResourceConversion(spec.Conversion, fldPath.Child("conversion"))...)
214+
allErrs = append(allErrs, validateCustomResourceConversion(spec.Conversion, requireRecognizedVersion, fldPath.Child("conversion"))...)
209215

210216
return allErrs
211217
}
@@ -225,8 +231,66 @@ func validateEnumStrings(fldPath *field.Path, value string, accepted []string, r
225231
return field.ErrorList{field.NotSupported(fldPath, value, accepted)}
226232
}
227233

234+
var acceptedConversionReviewVersion = []string{v1beta1.SchemeGroupVersion.Version}
235+
236+
func isAcceptedConversionReviewVersion(v string) bool {
237+
for _, version := range acceptedConversionReviewVersion {
238+
if v == version {
239+
return true
240+
}
241+
}
242+
return false
243+
}
244+
245+
func validateConversionReviewVersions(versions []string, requireRecognizedVersion bool, fldPath *field.Path) field.ErrorList {
246+
allErrs := field.ErrorList{}
247+
if len(versions) < 1 {
248+
allErrs = append(allErrs, field.Required(fldPath, ""))
249+
} else {
250+
seen := map[string]bool{}
251+
hasAcceptedVersion := false
252+
for i, v := range versions {
253+
if seen[v] {
254+
allErrs = append(allErrs, field.Invalid(fldPath.Index(i), v, "duplicate version"))
255+
continue
256+
}
257+
seen[v] = true
258+
for _, errString := range utilvalidation.IsDNS1035Label(v) {
259+
allErrs = append(allErrs, field.Invalid(fldPath.Index(i), v, errString))
260+
}
261+
if isAcceptedConversionReviewVersion(v) {
262+
hasAcceptedVersion = true
263+
}
264+
}
265+
if requireRecognizedVersion && !hasAcceptedVersion {
266+
allErrs = append(allErrs, field.Invalid(
267+
fldPath, versions,
268+
fmt.Sprintf("none of the versions accepted by this server. accepted version(s) are %v",
269+
strings.Join(acceptedConversionReviewVersion, ", "))))
270+
}
271+
}
272+
return allErrs
273+
}
274+
275+
// hasValidConversionReviewVersion return true if there is a valid version or if the list is empty.
276+
func hasValidConversionReviewVersionOrEmpty(versions []string) bool {
277+
if len(versions) < 1 {
278+
return true
279+
}
280+
for _, v := range versions {
281+
if isAcceptedConversionReviewVersion(v) {
282+
return true
283+
}
284+
}
285+
return false
286+
}
287+
228288
// ValidateCustomResourceConversion statically validates
229289
func ValidateCustomResourceConversion(conversion *apiextensions.CustomResourceConversion, fldPath *field.Path) field.ErrorList {
290+
return validateCustomResourceConversion(conversion, true, fldPath)
291+
}
292+
293+
func validateCustomResourceConversion(conversion *apiextensions.CustomResourceConversion, requireRecognizedVersion bool, fldPath *field.Path) field.ErrorList {
230294
allErrs := field.ErrorList{}
231295
if conversion == nil {
232296
return allErrs
@@ -250,15 +314,22 @@ func ValidateCustomResourceConversion(conversion *apiextensions.CustomResourceCo
250314
allErrs = append(allErrs, webhook.ValidateWebhookService(fldPath.Child("webhookClientConfig").Child("service"), cc.Service.Name, cc.Service.Namespace, cc.Service.Path)...)
251315
}
252316
}
253-
} else if conversion.WebhookClientConfig != nil {
254-
allErrs = append(allErrs, field.Forbidden(fldPath.Child("webhookClientConfig"), "should not be set when strategy is not set to Webhook"))
317+
allErrs = append(allErrs, validateConversionReviewVersions(conversion.ConversionReviewVersions, requireRecognizedVersion, fldPath.Child("conversionReviewVersions"))...)
318+
} else {
319+
if conversion.WebhookClientConfig != nil {
320+
allErrs = append(allErrs, field.Forbidden(fldPath.Child("webhookClientConfig"), "should not be set when strategy is not set to Webhook"))
321+
}
322+
if len(conversion.ConversionReviewVersions) > 0 {
323+
allErrs = append(allErrs, field.Forbidden(fldPath.Child("conversionReviewVersions"), "should not be set when strategy is not set to Webhook"))
324+
}
255325
}
256326
return allErrs
257327
}
258328

259329
// ValidateCustomResourceDefinitionSpecUpdate statically validates
260330
func ValidateCustomResourceDefinitionSpecUpdate(spec, oldSpec *apiextensions.CustomResourceDefinitionSpec, established bool, fldPath *field.Path) field.ErrorList {
261-
allErrs := ValidateCustomResourceDefinitionSpec(spec, fldPath)
331+
requireRecognizedVersion := oldSpec.Conversion == nil || hasValidConversionReviewVersionOrEmpty(oldSpec.Conversion.ConversionReviewVersions)
332+
allErrs := validateCustomResourceDefinitionSpec(spec, requireRecognizedVersion, fldPath)
262333

263334
if established {
264335
// these effect the storage and cannot be changed therefore

0 commit comments

Comments
 (0)