mirror of https://github.com/k3s-io/k3s
Merge pull request #78426 from sttts/sttts-crd-conversion-beta
apiextensions: promote CRD conversion to betak3s-v1.15.3
commit
b18846cf4c
|
@ -16340,7 +16340,7 @@
|
||||||
"type": "array"
|
"type": "array"
|
||||||
},
|
},
|
||||||
"strategy": {
|
"strategy": {
|
||||||
"description": "`strategy` specifies the conversion strategy. Allowed values are: - `None`: The converter only change the apiVersion and would not touch any other field in the CR. - `Webhook`: API Server will call to an external webhook to do the conversion. Additional information is needed for this option.",
|
"description": "`strategy` specifies the conversion strategy. Allowed values are: - `None`: The converter only change the apiVersion and would not touch any other field in the CR. - `Webhook`: API Server will call to an external webhook to do the conversion. Additional information\n is needed for this option. This requires spec.preserveUnknownFields to be false.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"webhookClientConfig": {
|
"webhookClientConfig": {
|
||||||
|
|
|
@ -545,7 +545,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||||
// unintentionally on either side:
|
// unintentionally on either side:
|
||||||
apiextensionsfeatures.CustomResourceValidation: {Default: true, PreRelease: featuregate.Beta},
|
apiextensionsfeatures.CustomResourceValidation: {Default: true, PreRelease: featuregate.Beta},
|
||||||
apiextensionsfeatures.CustomResourceSubresources: {Default: true, PreRelease: featuregate.Beta},
|
apiextensionsfeatures.CustomResourceSubresources: {Default: true, PreRelease: featuregate.Beta},
|
||||||
apiextensionsfeatures.CustomResourceWebhookConversion: {Default: false, PreRelease: featuregate.Alpha},
|
apiextensionsfeatures.CustomResourceWebhookConversion: {Default: true, PreRelease: featuregate.Beta},
|
||||||
apiextensionsfeatures.CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.Beta},
|
apiextensionsfeatures.CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.Beta},
|
||||||
apiextensionsfeatures.CustomResourceDefaulting: {Default: false, PreRelease: featuregate.Alpha},
|
apiextensionsfeatures.CustomResourceDefaulting: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,8 @@ type CustomResourceDefinitionSpec struct {
|
||||||
type CustomResourceConversion struct {
|
type CustomResourceConversion struct {
|
||||||
// `strategy` specifies the conversion strategy. Allowed values are:
|
// `strategy` specifies the conversion strategy. Allowed values are:
|
||||||
// - `None`: The converter only change the apiVersion and would not touch any other field in the CR.
|
// - `None`: The converter only change the apiVersion and would not touch any other field in the CR.
|
||||||
// - `Webhook`: API Server will call to an external webhook to do the conversion. Additional information is needed for this option.
|
// - `Webhook`: API Server will call to an external webhook to do the conversion. Additional information
|
||||||
|
// is needed for this option. This requires spec.preserveUnknownFields to be false.
|
||||||
Strategy ConversionStrategyType
|
Strategy ConversionStrategyType
|
||||||
|
|
||||||
// `webhookClientConfig` is the instructions for how to call the webhook if strategy is `Webhook`.
|
// `webhookClientConfig` is the instructions for how to call the webhook if strategy is `Webhook`.
|
||||||
|
|
|
@ -107,7 +107,8 @@ message CustomResourceColumnDefinition {
|
||||||
message CustomResourceConversion {
|
message CustomResourceConversion {
|
||||||
// `strategy` specifies the conversion strategy. Allowed values are:
|
// `strategy` specifies the conversion strategy. Allowed values are:
|
||||||
// - `None`: The converter only change the apiVersion and would not touch any other field in the CR.
|
// - `None`: The converter only change the apiVersion and would not touch any other field in the CR.
|
||||||
// - `Webhook`: API Server will call to an external webhook to do the conversion. Additional information is needed for this option.
|
// - `Webhook`: API Server will call to an external webhook to do the conversion. Additional information
|
||||||
|
// is needed for this option. This requires spec.preserveUnknownFields to be false.
|
||||||
optional string strategy = 1;
|
optional string strategy = 1;
|
||||||
|
|
||||||
// `webhookClientConfig` is the instructions for how to call the webhook if strategy is `Webhook`. This field is
|
// `webhookClientConfig` is the instructions for how to call the webhook if strategy is `Webhook`. This field is
|
||||||
|
|
|
@ -90,7 +90,8 @@ type CustomResourceDefinitionSpec struct {
|
||||||
type CustomResourceConversion struct {
|
type CustomResourceConversion struct {
|
||||||
// `strategy` specifies the conversion strategy. Allowed values are:
|
// `strategy` specifies the conversion strategy. Allowed values are:
|
||||||
// - `None`: The converter only change the apiVersion and would not touch any other field in the CR.
|
// - `None`: The converter only change the apiVersion and would not touch any other field in the CR.
|
||||||
// - `Webhook`: API Server will call to an external webhook to do the conversion. Additional information is needed for this option.
|
// - `Webhook`: API Server will call to an external webhook to do the conversion. Additional information
|
||||||
|
// is needed for this option. This requires spec.preserveUnknownFields to be false.
|
||||||
Strategy ConversionStrategyType `json:"strategy" protobuf:"bytes,1,name=strategy"`
|
Strategy ConversionStrategyType `json:"strategy" protobuf:"bytes,1,name=strategy"`
|
||||||
|
|
||||||
// `webhookClientConfig` is the instructions for how to call the webhook if strategy is `Webhook`. This field is
|
// `webhookClientConfig` is the instructions for how to call the webhook if strategy is `Webhook`. This field is
|
||||||
|
|
|
@ -121,10 +121,10 @@ func ValidateCustomResourceDefinitionVersion(version *apiextensions.CustomResour
|
||||||
// ValidateCustomResourceDefinitionSpec statically validates
|
// ValidateCustomResourceDefinitionSpec statically validates
|
||||||
func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, fldPath *field.Path) field.ErrorList {
|
func ValidateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, fldPath *field.Path) field.ErrorList {
|
||||||
allowDefaults := utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceDefaulting)
|
allowDefaults := utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceDefaulting)
|
||||||
return validateCustomResourceDefinitionSpec(spec, true, allowDefaults, fldPath)
|
return validateCustomResourceDefinitionSpec(spec, true, allowDefaults, false, fldPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, requireRecognizedVersion, allowDefaults bool, fldPath *field.Path) field.ErrorList {
|
func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefinitionSpec, requireRecognizedVersion, allowDefaults, allowConversionWithPreserveUnknownFields bool, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if len(spec.Group) == 0 {
|
if len(spec.Group) == 0 {
|
||||||
|
@ -237,11 +237,18 @@ func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !allowConversionWithPreserveUnknownFields && conversionAndPreserveUnknownFields(spec) {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("conversion").Child("strategy"), spec.Conversion.Strategy, "must be None if spec.preserveUnknownFields is true"))
|
||||||
|
}
|
||||||
allErrs = append(allErrs, validateCustomResourceConversion(spec.Conversion, requireRecognizedVersion, fldPath.Child("conversion"))...)
|
allErrs = append(allErrs, validateCustomResourceConversion(spec.Conversion, requireRecognizedVersion, fldPath.Child("conversion"))...)
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func conversionAndPreserveUnknownFields(spec *apiextensions.CustomResourceDefinitionSpec) bool {
|
||||||
|
return (spec.Conversion != nil && spec.Conversion.Strategy != apiextensions.NoneConverter) && (spec.PreserveUnknownFields == nil || *spec.PreserveUnknownFields)
|
||||||
|
}
|
||||||
|
|
||||||
func validateEnumStrings(fldPath *field.Path, value string, accepted []string, required bool) field.ErrorList {
|
func validateEnumStrings(fldPath *field.Path, value string, accepted []string, required bool) field.ErrorList {
|
||||||
if value == "" {
|
if value == "" {
|
||||||
if required {
|
if required {
|
||||||
|
@ -359,7 +366,7 @@ func ValidateCustomResourceDefinitionSpecUpdate(spec, oldSpec *apiextensions.Cus
|
||||||
// find out whether any schema had default before. Then we keep allowing it.
|
// find out whether any schema had default before. Then we keep allowing it.
|
||||||
allowDefaults := utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceDefaulting) || specHasDefaults(oldSpec)
|
allowDefaults := utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceDefaulting) || specHasDefaults(oldSpec)
|
||||||
|
|
||||||
allErrs := validateCustomResourceDefinitionSpec(spec, requireRecognizedVersion, allowDefaults, fldPath)
|
allErrs := validateCustomResourceDefinitionSpec(spec, requireRecognizedVersion, allowDefaults, conversionAndPreserveUnknownFields(oldSpec), fldPath)
|
||||||
|
|
||||||
if established {
|
if established {
|
||||||
// these effect the storage and cannot be changed therefore
|
// these effect the storage and cannot be changed therefore
|
||||||
|
|
|
@ -116,7 +116,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -161,7 +166,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -206,7 +216,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -247,7 +262,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
URL: strPtr(""),
|
URL: strPtr(""),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -370,7 +390,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
},
|
},
|
||||||
ConversionReviewVersions: []string{"invalid-version"},
|
ConversionReviewVersions: []string{"invalid-version"},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -412,7 +437,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
},
|
},
|
||||||
ConversionReviewVersions: []string{"0v"},
|
ConversionReviewVersions: []string{"0v"},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -455,7 +485,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
},
|
},
|
||||||
ConversionReviewVersions: []string{"invalid-version", "v1beta1"},
|
ConversionReviewVersions: []string{"invalid-version", "v1beta1"},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -495,7 +530,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
},
|
},
|
||||||
ConversionReviewVersions: []string{"v1beta1", "v1beta1"},
|
ConversionReviewVersions: []string{"v1beta1", "v1beta1"},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -533,7 +573,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
Conversion: &apiextensions.CustomResourceConversion{
|
Conversion: &apiextensions.CustomResourceConversion{
|
||||||
Strategy: apiextensions.ConversionStrategyType("Webhook"),
|
Strategy: apiextensions.ConversionStrategyType("Webhook"),
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -571,7 +616,12 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
Conversion: &apiextensions.CustomResourceConversion{
|
Conversion: &apiextensions.CustomResourceConversion{
|
||||||
Strategy: apiextensions.ConversionStrategyType("non_existing_conversion"),
|
Strategy: apiextensions.ConversionStrategyType("non_existing_conversion"),
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -581,6 +631,129 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||||
unsupported("spec", "conversion", "strategy"),
|
unsupported("spec", "conversion", "strategy"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "none conversion without preserveUnknownFields=false",
|
||||||
|
resource: &apiextensions.CustomResourceDefinition{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||||
|
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||||
|
Group: "group.com",
|
||||||
|
Scope: apiextensions.ResourceScope("Cluster"),
|
||||||
|
Names: apiextensions.CustomResourceDefinitionNames{
|
||||||
|
Plural: "plural",
|
||||||
|
Singular: "singular",
|
||||||
|
Kind: "Plural",
|
||||||
|
ListKind: "PluralList",
|
||||||
|
},
|
||||||
|
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "version1",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "version2",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Conversion: &apiextensions.CustomResourceConversion{
|
||||||
|
Strategy: apiextensions.ConversionStrategyType("None"),
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||||
|
},
|
||||||
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
|
StoredVersions: []string{"version1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errors: []validationMatch{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "webhook conversion without preserveUnknownFields=false",
|
||||||
|
resource: &apiextensions.CustomResourceDefinition{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||||
|
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||||
|
Group: "group.com",
|
||||||
|
Scope: apiextensions.ResourceScope("Cluster"),
|
||||||
|
Names: apiextensions.CustomResourceDefinitionNames{
|
||||||
|
Plural: "plural",
|
||||||
|
Singular: "singular",
|
||||||
|
Kind: "Plural",
|
||||||
|
ListKind: "PluralList",
|
||||||
|
},
|
||||||
|
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "version1",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "version2",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Conversion: &apiextensions.CustomResourceConversion{
|
||||||
|
Strategy: apiextensions.ConversionStrategyType("Webhook"),
|
||||||
|
WebhookClientConfig: &apiextensions.WebhookClientConfig{
|
||||||
|
URL: strPtr("https://example.com/webhook"),
|
||||||
|
},
|
||||||
|
ConversionReviewVersions: []string{"v1beta1"},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||||
|
},
|
||||||
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
|
StoredVersions: []string{"version1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errors: []validationMatch{
|
||||||
|
invalid("spec", "conversion", "strategy"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "webhook conversion with preserveUnknownFields=false",
|
||||||
|
resource: &apiextensions.CustomResourceDefinition{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||||
|
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||||
|
Group: "group.com",
|
||||||
|
Scope: apiextensions.ResourceScope("Cluster"),
|
||||||
|
Names: apiextensions.CustomResourceDefinitionNames{
|
||||||
|
Plural: "plural",
|
||||||
|
Singular: "singular",
|
||||||
|
Kind: "Plural",
|
||||||
|
ListKind: "PluralList",
|
||||||
|
},
|
||||||
|
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "version1",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "version2",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Conversion: &apiextensions.CustomResourceConversion{
|
||||||
|
Strategy: apiextensions.ConversionStrategyType("Webhook"),
|
||||||
|
WebhookClientConfig: &apiextensions.WebhookClientConfig{
|
||||||
|
URL: strPtr("https://example.com/webhook"),
|
||||||
|
},
|
||||||
|
ConversionReviewVersions: []string{"v1beta1"},
|
||||||
|
},
|
||||||
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
|
},
|
||||||
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
|
StoredVersions: []string{"version1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errors: []validationMatch{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "no_storage_version",
|
name: "no_storage_version",
|
||||||
resource: &apiextensions.CustomResourceDefinition{
|
resource: &apiextensions.CustomResourceDefinition{
|
||||||
|
@ -1642,7 +1815,12 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||||
},
|
},
|
||||||
ConversionReviewVersions: []string{"invalid-version"},
|
ConversionReviewVersions: []string{"invalid-version"},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -1678,7 +1856,12 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||||
},
|
},
|
||||||
ConversionReviewVersions: []string{"invalid-version_0, invalid-version"},
|
ConversionReviewVersions: []string{"invalid-version_0, invalid-version"},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -1718,7 +1901,12 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||||
},
|
},
|
||||||
ConversionReviewVersions: []string{"invalid-version"},
|
ConversionReviewVersions: []string{"invalid-version"},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -1754,7 +1942,12 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||||
},
|
},
|
||||||
ConversionReviewVersions: []string{"v1beta1", "invalid-version"},
|
ConversionReviewVersions: []string{"v1beta1", "invalid-version"},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -1796,7 +1989,12 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||||
},
|
},
|
||||||
ConversionReviewVersions: []string{"invalid-version"},
|
ConversionReviewVersions: []string{"invalid-version"},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -1831,7 +2029,12 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||||
URL: strPtr("https://example.com/webhook"),
|
URL: strPtr("https://example.com/webhook"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
Validation: &apiextensions.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
},
|
},
|
||||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
StoredVersions: []string{"version"},
|
StoredVersions: []string{"version"},
|
||||||
|
@ -1841,6 +2044,82 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||||
invalid("spec", "conversion", "conversionReviewVersions"),
|
invalid("spec", "conversion", "conversionReviewVersions"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "webhookconfig: should accept preserveUnknownFields=true if set before",
|
||||||
|
resource: &apiextensions.CustomResourceDefinition{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com", ResourceVersion: "42"},
|
||||||
|
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||||
|
Group: "group.com",
|
||||||
|
Scope: apiextensions.ResourceScope("Cluster"),
|
||||||
|
Names: apiextensions.CustomResourceDefinitionNames{
|
||||||
|
Plural: "plural",
|
||||||
|
Singular: "singular",
|
||||||
|
Kind: "Plural",
|
||||||
|
ListKind: "PluralList",
|
||||||
|
},
|
||||||
|
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "version",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "version2",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Conversion: &apiextensions.CustomResourceConversion{
|
||||||
|
Strategy: apiextensions.ConversionStrategyType("Webhook"),
|
||||||
|
WebhookClientConfig: &apiextensions.WebhookClientConfig{
|
||||||
|
URL: strPtr("https://example.com/webhook"),
|
||||||
|
},
|
||||||
|
ConversionReviewVersions: []string{"v1beta1"},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||||
|
},
|
||||||
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
|
StoredVersions: []string{"version"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
old: &apiextensions.CustomResourceDefinition{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com", ResourceVersion: "42"},
|
||||||
|
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||||
|
Group: "group.com",
|
||||||
|
Scope: apiextensions.ResourceScope("Cluster"),
|
||||||
|
Names: apiextensions.CustomResourceDefinitionNames{
|
||||||
|
Plural: "plural",
|
||||||
|
Singular: "singular",
|
||||||
|
Kind: "Plural",
|
||||||
|
ListKind: "PluralList",
|
||||||
|
},
|
||||||
|
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||||
|
{
|
||||||
|
Name: "version",
|
||||||
|
Served: true,
|
||||||
|
Storage: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "version2",
|
||||||
|
Served: true,
|
||||||
|
Storage: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Conversion: &apiextensions.CustomResourceConversion{
|
||||||
|
Strategy: apiextensions.ConversionStrategyType("Webhook"),
|
||||||
|
WebhookClientConfig: &apiextensions.WebhookClientConfig{
|
||||||
|
URL: strPtr("https://example.com/webhook"),
|
||||||
|
},
|
||||||
|
ConversionReviewVersions: []string{"v1beta1"},
|
||||||
|
},
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||||
|
},
|
||||||
|
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||||
|
StoredVersions: []string{"version"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errors: []validationMatch{},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "unchanged",
|
name: "unchanged",
|
||||||
old: &apiextensions.CustomResourceDefinition{
|
old: &apiextensions.CustomResourceDefinition{
|
||||||
|
|
|
@ -71,7 +71,7 @@ func init() {
|
||||||
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
|
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
|
||||||
CustomResourceValidation: {Default: true, PreRelease: featuregate.Beta},
|
CustomResourceValidation: {Default: true, PreRelease: featuregate.Beta},
|
||||||
CustomResourceSubresources: {Default: true, PreRelease: featuregate.Beta},
|
CustomResourceSubresources: {Default: true, PreRelease: featuregate.Beta},
|
||||||
CustomResourceWebhookConversion: {Default: false, PreRelease: featuregate.Alpha},
|
CustomResourceWebhookConversion: {Default: true, PreRelease: featuregate.Beta},
|
||||||
CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.Beta},
|
CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.Beta},
|
||||||
CustomResourceDefaulting: {Default: false, PreRelease: featuregate.Alpha},
|
CustomResourceDefaulting: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,6 @@ func TestDropDisableFieldsCustomResourceDefinition(t *testing.T) {
|
||||||
t.Run(fmt.Sprintf("subresources feature enabled=%v, old CRD %v, new CRD %v", validationEnabled, oldCRDInfo.name, newCRDInfo.name),
|
t.Run(fmt.Sprintf("subresources feature enabled=%v, old CRD %v, new CRD %v", validationEnabled, oldCRDInfo.name, newCRDInfo.name),
|
||||||
func(t *testing.T) {
|
func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceSubresources, validationEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceSubresources, validationEnabled)()
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec
|
var oldCRDSpec *apiextensions.CustomResourceDefinitionSpec
|
||||||
if oldCRD != nil {
|
if oldCRD != nil {
|
||||||
oldCRDSpec = &oldCRD.Spec
|
oldCRDSpec = &oldCRD.Spec
|
||||||
|
|
|
@ -28,10 +28,6 @@ import (
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
|
||||||
serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
|
||||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
@ -48,6 +44,10 @@ import (
|
||||||
"k8s.io/utils/pointer"
|
"k8s.io/utils/pointer"
|
||||||
|
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
||||||
|
serveroptions "k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
|
||||||
|
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/storage"
|
"k8s.io/apiextensions-apiserver/test/integration/storage"
|
||||||
)
|
)
|
||||||
|
@ -59,18 +59,14 @@ func checks(checkers ...Checker) []Checker {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWebhookConverter(t *testing.T) {
|
func TestWebhookConverter(t *testing.T) {
|
||||||
testWebhookConverter(t, false, false)
|
testWebhookConverter(t, false)
|
||||||
}
|
|
||||||
|
|
||||||
func TestWebhookConverterWithPruning(t *testing.T) {
|
|
||||||
testWebhookConverter(t, true, false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWebhookConverterWithDefaulting(t *testing.T) {
|
func TestWebhookConverterWithDefaulting(t *testing.T) {
|
||||||
testWebhookConverter(t, true, true)
|
testWebhookConverter(t, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testWebhookConverter(t *testing.T, pruning, defaulting bool) {
|
func testWebhookConverter(t *testing.T, defaulting bool) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
group string
|
group string
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
|
@ -115,7 +111,6 @@ func testWebhookConverter(t *testing.T, pruning, defaulting bool) {
|
||||||
defer etcd3watcher.TestOnlySetFatalOnDecodeError(true)
|
defer etcd3watcher.TestOnlySetFatalOnDecodeError(true)
|
||||||
|
|
||||||
// enable necessary features
|
// enable necessary features
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
if defaulting {
|
if defaulting {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceDefaulting, true)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceDefaulting, true)()
|
||||||
}
|
}
|
||||||
|
@ -139,7 +134,6 @@ func testWebhookConverter(t *testing.T, pruning, defaulting bool) {
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
crd := multiVersionFixture.DeepCopy()
|
crd := multiVersionFixture.DeepCopy()
|
||||||
crd.Spec.PreserveUnknownFields = pointer.BoolPtr(!pruning)
|
|
||||||
|
|
||||||
if !defaulting {
|
if !defaulting {
|
||||||
for i := range crd.Spec.Versions {
|
for i := range crd.Spec.Versions {
|
||||||
|
@ -994,7 +988,8 @@ var multiVersionFixture = &apiextensionsv1beta1.CustomResourceDefinition{
|
||||||
ListKind: "MultiVersionList",
|
ListKind: "MultiVersionList",
|
||||||
Categories: []string{"all"},
|
Categories: []string{"all"},
|
||||||
},
|
},
|
||||||
Scope: apiextensionsv1beta1.NamespaceScoped,
|
Scope: apiextensionsv1beta1.NamespaceScoped,
|
||||||
|
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||||
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
Versions: []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||||
{
|
{
|
||||||
// storage version, same schema as v1alpha1
|
// storage version, same schema as v1alpha1
|
||||||
|
|
|
@ -25,18 +25,16 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
autoscaling "k8s.io/api/autoscaling/v1"
|
autoscaling "k8s.io/api/autoscaling/v1"
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
|
||||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
)
|
)
|
||||||
|
|
||||||
var labelSelectorPath = ".status.labelSelector"
|
var labelSelectorPath = ".status.labelSelector"
|
||||||
|
@ -148,7 +146,6 @@ func NewNoxuSubresourceInstance(namespace, name, version string) *unstructured.U
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStatusSubresource(t *testing.T) {
|
func TestStatusSubresource(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -261,7 +258,6 @@ func TestStatusSubresource(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScaleSubresource(t *testing.T) {
|
func TestScaleSubresource(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
groupResource := schema.GroupResource{
|
groupResource := schema.GroupResource{
|
||||||
Group: "mygroup.example.com",
|
Group: "mygroup.example.com",
|
||||||
Resource: "noxus",
|
Resource: "noxus",
|
||||||
|
@ -407,7 +403,6 @@ func TestScaleSubresource(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidationSchemaWithStatus(t *testing.T) {
|
func TestValidationSchemaWithStatus(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -455,7 +450,6 @@ func TestValidationSchemaWithStatus(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateOnlyStatus(t *testing.T) {
|
func TestValidateOnlyStatus(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -562,7 +556,6 @@ func TestValidateOnlyStatus(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubresourcesDiscovery(t *testing.T) {
|
func TestSubresourcesDiscovery(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -654,7 +647,6 @@ func TestSubresourcesDiscovery(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGeneration(t *testing.T) {
|
func TestGeneration(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -728,7 +720,6 @@ func TestGeneration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSubresourcePatch(t *testing.T) {
|
func TestSubresourcePatch(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
groupResource := schema.GroupResource{
|
groupResource := schema.GroupResource{
|
||||||
Group: "mygroup.example.com",
|
Group: "mygroup.example.com",
|
||||||
Resource: "noxus",
|
Resource: "noxus",
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
|
||||||
|
@ -29,13 +28,11 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
|
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -104,7 +101,6 @@ func newTableInstance(name string) *unstructured.Unstructured {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTableGet(t *testing.T) {
|
func TestTableGet(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -262,7 +258,6 @@ func TestTableGet(t *testing.T) {
|
||||||
// TestColumnsPatch tests the case that a CRD was created with no top-level or
|
// TestColumnsPatch tests the case that a CRD was created with no top-level or
|
||||||
// per-version columns. One should be able to PATCH the CRD setting per-version columns.
|
// per-version columns. One should be able to PATCH the CRD setting per-version columns.
|
||||||
func TestColumnsPatch(t *testing.T) {
|
func TestColumnsPatch(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -307,7 +302,6 @@ func TestColumnsPatch(t *testing.T) {
|
||||||
// One should be able to PATCH the CRD cleaning the top-level columns and setting per-version
|
// One should be able to PATCH the CRD cleaning the top-level columns and setting per-version
|
||||||
// columns.
|
// columns.
|
||||||
func TestPatchCleanTopLevelColumns(t *testing.T) {
|
func TestPatchCleanTopLevelColumns(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -22,17 +22,14 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
clientschema "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
|
|
||||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apimachinery/pkg/util/yaml"
|
"k8s.io/apimachinery/pkg/util/yaml"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
|
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
clientschema "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -224,7 +221,6 @@ func newNoxuValidationInstance(namespace, name string) *unstructured.Unstructure
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomResourceValidation(t *testing.T) {
|
func TestCustomResourceValidation(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -256,7 +252,6 @@ func TestCustomResourceValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomResourceUpdateValidation(t *testing.T) {
|
func TestCustomResourceUpdateValidation(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -310,7 +305,6 @@ func TestCustomResourceUpdateValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCustomResourceValidationErrors(t *testing.T) {
|
func TestCustomResourceValidationErrors(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -411,7 +405,6 @@ func TestCustomResourceValidationErrors(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCRValidationOnCRDUpdate(t *testing.T) {
|
func TestCRValidationOnCRDUpdate(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -483,7 +476,6 @@ func TestCRValidationOnCRDUpdate(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestForbiddenFieldsInSchema(t *testing.T) {
|
func TestForbiddenFieldsInSchema(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -542,8 +534,6 @@ func TestForbiddenFieldsInSchema(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNonStructuralSchemaConditionUpdate(t *testing.T) {
|
func TestNonStructuralSchemaConditionUpdate(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
|
|
||||||
tearDown, apiExtensionClient, _, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, _, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -684,8 +674,6 @@ spec:
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNonStructuralSchemaCondition(t *testing.T) {
|
func TestNonStructuralSchemaCondition(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
|
|
||||||
tearDown, apiExtensionClient, _, err := fixtures.StartDefaultServerWithClients(t)
|
tearDown, apiExtensionClient, _, err := fixtures.StartDefaultServerWithClients(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -27,13 +27,10 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/dynamic"
|
"k8s.io/client-go/dynamic"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
|
|
||||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
|
||||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -357,7 +354,6 @@ values:
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestYAMLSubresource(t *testing.T) {
|
func TestYAMLSubresource(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
|
||||||
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
tearDown, config, _, err := fixtures.StartDefaultServer(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
@ -19,11 +19,12 @@ package apimachinery
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/onsi/ginkgo"
|
||||||
|
"github.com/onsi/gomega"
|
||||||
|
|
||||||
apps "k8s.io/api/apps/v1"
|
apps "k8s.io/api/apps/v1"
|
||||||
"k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
|
||||||
"k8s.io/apiextensions-apiserver/test/integration"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
@ -38,8 +39,8 @@ import (
|
||||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||||
"k8s.io/utils/pointer"
|
"k8s.io/utils/pointer"
|
||||||
|
|
||||||
"github.com/onsi/ginkgo"
|
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||||
"github.com/onsi/gomega"
|
"k8s.io/apiextensions-apiserver/test/integration"
|
||||||
|
|
||||||
// ensure libs have a chance to initialize
|
// ensure libs have a chance to initialize
|
||||||
_ "github.com/stretchr/testify/assert"
|
_ "github.com/stretchr/testify/assert"
|
||||||
|
@ -60,11 +61,28 @@ var apiVersions = []v1beta1.CustomResourceDefinitionVersion{
|
||||||
Name: "v1",
|
Name: "v1",
|
||||||
Served: true,
|
Served: true,
|
||||||
Storage: true,
|
Storage: true,
|
||||||
|
Schema: &v1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &v1beta1.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]v1beta1.JSONSchemaProps{
|
||||||
|
"hostPort": {Type: "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "v2",
|
Name: "v2",
|
||||||
Served: true,
|
Served: true,
|
||||||
Storage: false,
|
Storage: false,
|
||||||
|
Schema: &v1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &v1beta1.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]v1beta1.JSONSchemaProps{
|
||||||
|
"host": {Type: "string"},
|
||||||
|
"port": {Type: "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,15 +91,32 @@ var alternativeAPIVersions = []v1beta1.CustomResourceDefinitionVersion{
|
||||||
Name: "v1",
|
Name: "v1",
|
||||||
Served: true,
|
Served: true,
|
||||||
Storage: false,
|
Storage: false,
|
||||||
|
Schema: &v1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &v1beta1.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]v1beta1.JSONSchemaProps{
|
||||||
|
"hostPort": {Type: "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "v2",
|
Name: "v2",
|
||||||
Served: true,
|
Served: true,
|
||||||
Storage: true,
|
Storage: true,
|
||||||
|
Schema: &v1beta1.CustomResourceValidation{
|
||||||
|
OpenAPIV3Schema: &v1beta1.JSONSchemaProps{
|
||||||
|
Type: "object",
|
||||||
|
Properties: map[string]v1beta1.JSONSchemaProps{
|
||||||
|
"host": {Type: "string"},
|
||||||
|
"port": {Type: "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ = SIGDescribe("CustomResourceConversionWebhook [Feature:CustomResourceWebhookConversion]", func() {
|
var _ = SIGDescribe("CustomResourceConversionWebhook", func() {
|
||||||
var context *certContext
|
var context *certContext
|
||||||
f := framework.NewDefaultFramework("crd-webhook")
|
f := framework.NewDefaultFramework("crd-webhook")
|
||||||
|
|
||||||
|
@ -121,6 +156,7 @@ var _ = SIGDescribe("CustomResourceConversionWebhook [Feature:CustomResourceWebh
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
crd.Spec.PreserveUnknownFields = pointer.BoolPtr(false)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -144,6 +180,7 @@ var _ = SIGDescribe("CustomResourceConversionWebhook [Feature:CustomResourceWebh
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
crd.Spec.PreserveUnknownFields = pointer.BoolPtr(false)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
Loading…
Reference in New Issue