apiextensions-apiserver: promote defaulting to GA

This commit is contained in:
Dr. Stefan Schimanski 2019-11-04 11:44:03 +01:00
parent a89265b441
commit c56f07672b
4 changed files with 6 additions and 510 deletions

View File

@ -593,7 +593,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
apiextensionsfeatures.CustomResourceSubresources: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
apiextensionsfeatures.CustomResourceWebhookConversion: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
apiextensionsfeatures.CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
apiextensionsfeatures.CustomResourceDefaulting: {Default: true, PreRelease: featuregate.Beta},
apiextensionsfeatures.CustomResourceDefaulting: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // TODO: remove in 1.18
// features that enable backwards compatibility but are scheduled to be removed
// ...

View File

@ -1048,9 +1048,6 @@ func allowDefaults(requestGV schema.GroupVersion, oldCRDSpec *apiextensions.Cust
// don't tighten validation on existing persisted data
return true
}
if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceDefaulting) {
return false
}
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
return false
}

View File

@ -1613,44 +1613,6 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
invalid("spec", "versions[3]", "subresources", "scale", "labelSelectorPath"),
},
},
{
name: "defaults with disabled feature gate",
resource: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: singleVersionList,
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "number",
Default: jsonPtr(42.0),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(true),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
requestGV: apiextensionsv1beta1.SchemeGroupVersion,
disabledFeatures: []featuregate.Feature{features.CustomResourceDefaulting},
errors: []validationMatch{
forbidden("spec", "validation", "openAPIV3Schema", "properties[a]", "default"), // disabled feature-gate
},
},
{
name: "defaults with enabled feature gate via v1beta1",
resource: &apiextensions.CustomResourceDefinition{
@ -5871,473 +5833,6 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
errors: []validationMatch{},
enabledFeatures: []featuregate.Feature{features.CustomResourceDefaulting},
},
{
name: "ratcheting validation of defaults with disabled feature gate via v1beta1",
old: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "number",
Default: jsonPtr(42.0),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
resource: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "number",
Default: jsonPtr(42.0),
},
"b": {
Type: "number",
Default: jsonPtr(43.0),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
requestGV: apiextensionsv1beta1.SchemeGroupVersion,
errors: []validationMatch{},
disabledFeatures: []featuregate.Feature{features.CustomResourceDefaulting},
},
{
name: "ratcheting validation of defaults with disabled feature gate via v1",
old: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "number",
Default: jsonPtr(42.0),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
resource: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "number",
Default: jsonPtr(42.0),
},
"b": {
Type: "number",
Default: jsonPtr(43.0),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
requestGV: apiextensionsv1.SchemeGroupVersion,
errors: []validationMatch{},
disabledFeatures: []featuregate.Feature{features.CustomResourceDefaulting},
},
{
name: "ratcheting validation of defaults with disabled feature gate via v1, non-structural, no defaults before",
old: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
resource: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "number",
Default: jsonPtr(42.0),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
requestGV: apiextensionsv1.SchemeGroupVersion,
errors: []validationMatch{
forbidden("spec", "validation", "openAPIV3Schema", "properties[a]", "default"),
},
disabledFeatures: []featuregate.Feature{features.CustomResourceDefaulting},
},
{
name: "ratcheting validation of defaults with disabled feature gate via v1, unpruned => unpruned",
old: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"foo": {Type: "string"},
},
Default: jsonPtr(map[string]interface{}{
"unknown": "unknown",
}),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
resource: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"foo": {Type: "string"},
},
Default: jsonPtr(map[string]interface{}{
"unknown": "unknown",
}),
},
"b": {
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"foo": {Type: "string"},
},
Default: jsonPtr(map[string]interface{}{
"unknown": "unknown",
}),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
requestGV: apiextensionsv1.SchemeGroupVersion,
errors: []validationMatch{},
disabledFeatures: []featuregate.Feature{features.CustomResourceDefaulting},
},
{
name: "ratcheting validation of defaults with disabled feature gate via v1, pruned => unpruned",
old: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"foo": {Type: "string"},
},
Default: jsonPtr(map[string]interface{}{
"foo": "foo",
}),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
resource: &apiextensions.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: "plural.group.com",
ResourceVersion: "42",
},
Spec: apiextensions.CustomResourceDefinitionSpec{
Group: "group.com",
Version: "version",
Versions: []apiextensions.CustomResourceDefinitionVersion{
{
Name: "version",
Served: true,
Storage: true,
},
},
Scope: apiextensions.NamespaceScoped,
Names: apiextensions.CustomResourceDefinitionNames{
Plural: "plural",
Singular: "singular",
Kind: "Plural",
ListKind: "PluralList",
},
Validation: &apiextensions.CustomResourceValidation{
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"a": {
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"foo": {Type: "string"},
},
Default: jsonPtr(map[string]interface{}{
"foo": "foo",
}),
},
"b": {
Type: "object",
Properties: map[string]apiextensions.JSONSchemaProps{
"foo": {Type: "string"},
},
Default: jsonPtr(map[string]interface{}{
"unknown": "unknown",
}),
},
},
},
},
PreserveUnknownFields: pointer.BoolPtr(false),
},
Status: apiextensions.CustomResourceDefinitionStatus{
StoredVersions: []string{"version"},
},
},
requestGV: apiextensionsv1.SchemeGroupVersion,
errors: []validationMatch{
invalid("spec", "validation", "openAPIV3Schema", "properties[b]", "default"),
},
disabledFeatures: []featuregate.Feature{features.CustomResourceDefaulting},
},
{
name: "add default with enabled feature gate, structural schema, without pruning",
old: &apiextensions.CustomResourceDefinition{

View File

@ -62,8 +62,12 @@ const (
// owner: @sttts
// alpha: v1.15
// beta: v1.16
// GA: v1.17
//
// CustomResourceDefaulting enables OpenAPI defaulting in CustomResources.
//
// TODO: remove in 1.18
CustomResourceDefaulting featuregate.Feature = "CustomResourceDefaulting"
)
@ -79,5 +83,5 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
CustomResourceSubresources: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
CustomResourceWebhookConversion: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
CustomResourcePublishOpenAPI: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
CustomResourceDefaulting: {Default: true, PreRelease: featuregate.Beta},
CustomResourceDefaulting: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
}