Merge pull request #104248 from liggitt/drop-beta

Drop dead beta storage and validation code
This commit is contained in:
Kubernetes Prow Robot
2021-08-10 11:52:40 -07:00
committed by GitHub
43 changed files with 511 additions and 8571 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -249,9 +249,7 @@ var apiVersionPriorities = map[schema.GroupVersion]priority{
{Group: "events.k8s.io", Version: "v1"}: {group: 17750, version: 15}, {Group: "events.k8s.io", Version: "v1"}: {group: 17750, version: 15},
{Group: "events.k8s.io", Version: "v1beta1"}: {group: 17750, version: 5}, {Group: "events.k8s.io", Version: "v1beta1"}: {group: 17750, version: 5},
{Group: "authentication.k8s.io", Version: "v1"}: {group: 17700, version: 15}, {Group: "authentication.k8s.io", Version: "v1"}: {group: 17700, version: 15},
{Group: "authentication.k8s.io", Version: "v1beta1"}: {group: 17700, version: 9},
{Group: "authorization.k8s.io", Version: "v1"}: {group: 17600, version: 15}, {Group: "authorization.k8s.io", Version: "v1"}: {group: 17600, version: 15},
{Group: "authorization.k8s.io", Version: "v1beta1"}: {group: 17600, version: 9},
{Group: "autoscaling", Version: "v1"}: {group: 17500, version: 15}, {Group: "autoscaling", Version: "v1"}: {group: 17500, version: 15},
{Group: "autoscaling", Version: "v2beta1"}: {group: 17500, version: 9}, {Group: "autoscaling", Version: "v2beta1"}: {group: 17500, version: 9},
{Group: "autoscaling", Version: "v2beta2"}: {group: 17500, version: 1}, {Group: "autoscaling", Version: "v2beta2"}: {group: 17500, version: 1},
@@ -259,27 +257,17 @@ var apiVersionPriorities = map[schema.GroupVersion]priority{
{Group: "batch", Version: "v1beta1"}: {group: 17400, version: 9}, {Group: "batch", Version: "v1beta1"}: {group: 17400, version: 9},
{Group: "batch", Version: "v2alpha1"}: {group: 17400, version: 9}, {Group: "batch", Version: "v2alpha1"}: {group: 17400, version: 9},
{Group: "certificates.k8s.io", Version: "v1"}: {group: 17300, version: 15}, {Group: "certificates.k8s.io", Version: "v1"}: {group: 17300, version: 15},
{Group: "certificates.k8s.io", Version: "v1beta1"}: {group: 17300, version: 9},
{Group: "networking.k8s.io", Version: "v1"}: {group: 17200, version: 15}, {Group: "networking.k8s.io", Version: "v1"}: {group: 17200, version: 15},
{Group: "networking.k8s.io", Version: "v1beta1"}: {group: 17200, version: 9},
{Group: "extensions", Version: "v1beta1"}: {group: 17150, version: 1}, // prioritize below networking.k8s.io, which contains the GA version of Ingress, the only resource remaining in extensions/v1beta1
{Group: "policy", Version: "v1"}: {group: 17100, version: 15}, {Group: "policy", Version: "v1"}: {group: 17100, version: 15},
{Group: "policy", Version: "v1beta1"}: {group: 17100, version: 9}, {Group: "policy", Version: "v1beta1"}: {group: 17100, version: 9},
{Group: "rbac.authorization.k8s.io", Version: "v1"}: {group: 17000, version: 15}, {Group: "rbac.authorization.k8s.io", Version: "v1"}: {group: 17000, version: 15},
{Group: "rbac.authorization.k8s.io", Version: "v1beta1"}: {group: 17000, version: 12},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1"}: {group: 17000, version: 9},
{Group: "storage.k8s.io", Version: "v1"}: {group: 16800, version: 15}, {Group: "storage.k8s.io", Version: "v1"}: {group: 16800, version: 15},
{Group: "storage.k8s.io", Version: "v1beta1"}: {group: 16800, version: 9}, {Group: "storage.k8s.io", Version: "v1beta1"}: {group: 16800, version: 9},
{Group: "storage.k8s.io", Version: "v1alpha1"}: {group: 16800, version: 1}, {Group: "storage.k8s.io", Version: "v1alpha1"}: {group: 16800, version: 1},
{Group: "apiextensions.k8s.io", Version: "v1"}: {group: 16700, version: 15}, {Group: "apiextensions.k8s.io", Version: "v1"}: {group: 16700, version: 15},
{Group: "apiextensions.k8s.io", Version: "v1beta1"}: {group: 16700, version: 9},
{Group: "admissionregistration.k8s.io", Version: "v1"}: {group: 16700, version: 15}, {Group: "admissionregistration.k8s.io", Version: "v1"}: {group: 16700, version: 15},
{Group: "admissionregistration.k8s.io", Version: "v1beta1"}: {group: 16700, version: 12},
{Group: "scheduling.k8s.io", Version: "v1"}: {group: 16600, version: 15}, {Group: "scheduling.k8s.io", Version: "v1"}: {group: 16600, version: 15},
{Group: "scheduling.k8s.io", Version: "v1beta1"}: {group: 16600, version: 12},
{Group: "scheduling.k8s.io", Version: "v1alpha1"}: {group: 16600, version: 9},
{Group: "coordination.k8s.io", Version: "v1"}: {group: 16500, version: 15}, {Group: "coordination.k8s.io", Version: "v1"}: {group: 16500, version: 15},
{Group: "coordination.k8s.io", Version: "v1beta1"}: {group: 16500, version: 9},
{Group: "node.k8s.io", Version: "v1"}: {group: 16300, version: 15}, {Group: "node.k8s.io", Version: "v1"}: {group: 16300, version: 15},
{Group: "node.k8s.io", Version: "v1alpha1"}: {group: 16300, version: 1}, {Group: "node.k8s.io", Version: "v1alpha1"}: {group: 16300, version: 1},
{Group: "node.k8s.io", Version: "v1beta1"}: {group: 16300, version: 9}, {Group: "node.k8s.io", Version: "v1beta1"}: {group: 16300, version: 9},

View File

@@ -22,7 +22,6 @@ import (
genericvalidation "k8s.io/apimachinery/pkg/api/validation" genericvalidation "k8s.io/apimachinery/pkg/api/validation"
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation" utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
@@ -201,11 +200,11 @@ func validateAdmissionReviewVersions(versions []string, requireRecognizedAdmissi
} }
// ValidateValidatingWebhookConfiguration validates a webhook before creation. // ValidateValidatingWebhookConfiguration validates a webhook before creation.
func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration, requestGV schema.GroupVersion) field.ErrorList { func ValidateValidatingWebhookConfiguration(e *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList {
return validateValidatingWebhookConfiguration(e, validationOptions{ return validateValidatingWebhookConfiguration(e, validationOptions{
requireNoSideEffects: requireNoSideEffects(requestGV), requireNoSideEffects: true,
requireRecognizedAdmissionReviewVersion: true, requireRecognizedAdmissionReviewVersion: true,
requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV), requireUniqueWebhookNames: true,
}) })
} }
@@ -226,11 +225,11 @@ func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingW
} }
// ValidateMutatingWebhookConfiguration validates a webhook before creation. // ValidateMutatingWebhookConfiguration validates a webhook before creation.
func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration, requestGV schema.GroupVersion) field.ErrorList { func ValidateMutatingWebhookConfiguration(e *admissionregistration.MutatingWebhookConfiguration) field.ErrorList {
return validateMutatingWebhookConfiguration(e, validationOptions{ return validateMutatingWebhookConfiguration(e, validationOptions{
requireNoSideEffects: requireNoSideEffects(requestGV), requireNoSideEffects: true,
requireRecognizedAdmissionReviewVersion: true, requireRecognizedAdmissionReviewVersion: true,
requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV), requireUniqueWebhookNames: true,
}) })
} }
@@ -497,29 +496,19 @@ func validatingHasNoSideEffects(webhooks []admissionregistration.ValidatingWebho
} }
// ValidateValidatingWebhookConfigurationUpdate validates update of validating webhook configuration // ValidateValidatingWebhookConfigurationUpdate validates update of validating webhook configuration
func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration, requestGV schema.GroupVersion) field.ErrorList { func ValidateValidatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.ValidatingWebhookConfiguration) field.ErrorList {
return validateValidatingWebhookConfiguration(newC, validationOptions{ return validateValidatingWebhookConfiguration(newC, validationOptions{
requireNoSideEffects: requireNoSideEffects(requestGV) && validatingHasNoSideEffects(oldC.Webhooks), requireNoSideEffects: validatingHasNoSideEffects(oldC.Webhooks),
requireRecognizedAdmissionReviewVersion: validatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks), requireRecognizedAdmissionReviewVersion: validatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks),
requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV) && validatingHasUniqueWebhookNames(oldC.Webhooks), requireUniqueWebhookNames: validatingHasUniqueWebhookNames(oldC.Webhooks),
}) })
} }
// ValidateMutatingWebhookConfigurationUpdate validates update of mutating webhook configuration // ValidateMutatingWebhookConfigurationUpdate validates update of mutating webhook configuration
func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.MutatingWebhookConfiguration, requestGV schema.GroupVersion) field.ErrorList { func ValidateMutatingWebhookConfigurationUpdate(newC, oldC *admissionregistration.MutatingWebhookConfiguration) field.ErrorList {
return validateMutatingWebhookConfiguration(newC, validationOptions{ return validateMutatingWebhookConfiguration(newC, validationOptions{
requireNoSideEffects: requireNoSideEffects(requestGV) && mutatingHasNoSideEffects(oldC.Webhooks), requireNoSideEffects: mutatingHasNoSideEffects(oldC.Webhooks),
requireRecognizedAdmissionReviewVersion: mutatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks), requireRecognizedAdmissionReviewVersion: mutatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks),
requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV) && mutatingHasUniqueWebhookNames(oldC.Webhooks), requireUniqueWebhookNames: mutatingHasUniqueWebhookNames(oldC.Webhooks),
}) })
} }
// requireUniqueWebhookNames returns true for all requests except v1beta1 (for backwards compatibility)
func requireUniqueWebhookNames(requestGV schema.GroupVersion) bool {
return requestGV != (schema.GroupVersion{Group: admissionregistration.GroupName, Version: "v1beta1"})
}
// requireNoSideEffects returns true for all requests except v1beta1 (for backwards compatibility)
func requireNoSideEffects(requestGV schema.GroupVersion) bool {
return requestGV != (schema.GroupVersion{Group: admissionregistration.GroupName, Version: "v1beta1"})
}

View File

@@ -21,7 +21,6 @@ import (
"testing" "testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/apis/admissionregistration" "k8s.io/kubernetes/pkg/apis/admissionregistration"
) )
@@ -46,6 +45,7 @@ func newValidatingWebhookConfiguration(hooks []admissionregistration.ValidatingW
} }
func TestValidateValidatingWebhookConfiguration(t *testing.T) { func TestValidateValidatingWebhookConfiguration(t *testing.T) {
noSideEffect := admissionregistration.SideEffectClassNone
unknownSideEffect := admissionregistration.SideEffectClassUnknown unknownSideEffect := admissionregistration.SideEffectClassUnknown
validClientConfig := admissionregistration.WebhookClientConfig{ validClientConfig := admissionregistration.WebhookClientConfig{
URL: strPtr("https://example.com"), URL: strPtr("https://example.com"),
@@ -53,7 +53,6 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
config *admissionregistration.ValidatingWebhookConfiguration config *admissionregistration.ValidatingWebhookConfiguration
gv schema.GroupVersion
expectedError string expectedError string
}{ }{
{ {
@@ -83,11 +82,10 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
{ {
@@ -96,11 +94,10 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
AdmissionReviewVersions: []string{"v1beta1", "invalid-version"}, AdmissionReviewVersions: []string{"v1beta1", "invalid-version"},
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
{ {
@@ -131,24 +128,23 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
{ {
Name: "k8s.io", Name: "k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
{ {
Name: "", Name: "",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: `webhooks[1].name: Invalid value: "k8s.io": should be a domain with at least three segments separated by dots, webhooks[2].name: Required value`, expectedError: `webhooks[1].name: Invalid value: "k8s.io": should be a domain with at least three segments separated by dots, webhooks[2].name: Required value`,
}, },
{ {
name: "Webhooks must have unique names when not created via v1beta1", name: "Webhooks must have unique names when created",
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
@@ -161,26 +157,8 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
SideEffects: &unknownSideEffect, SideEffects: &unknownSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`, expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
}, },
{
name: "Webhooks can have duplicate names when created via v1beta1",
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{ {
name: "Operations must not be empty or nil", name: "Operations must not be empty or nil",
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
@@ -271,7 +249,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
Rules: []admissionregistration.RuleWithOperations{ Rules: []admissionregistration.RuleWithOperations{
{ {
Operations: []admissionregistration.OperationType{"CREATE"}, Operations: []admissionregistration.OperationType{"CREATE"},
@@ -284,7 +262,6 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
}, },
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
}, },
{ {
name: `resource "*" cannot mix with resources that don't have subresources`, name: `resource "*" cannot mix with resources that don't have subresources`,
@@ -334,7 +311,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
Rules: []admissionregistration.RuleWithOperations{ Rules: []admissionregistration.RuleWithOperations{
{ {
Operations: []admissionregistration.OperationType{"CREATE"}, Operations: []admissionregistration.OperationType{"CREATE"},
@@ -347,7 +324,6 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
}, },
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
}, },
{ {
name: "resource */a cannot mix with x/a", name: "resource */a cannot mix with x/a",
@@ -429,7 +405,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`, expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`,
}, },
{ {
name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\" via v1beta1", name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" when created",
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
@@ -440,22 +416,6 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
}(), }(),
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun", "Some", "Unknown"`,
},
{
name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" via v1",
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: func() *admissionregistration.SideEffectClass {
r := admissionregistration.SideEffectClass("other")
return &r
}(),
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1"},
expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`, expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`,
}, },
{ {
@@ -599,10 +559,9 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
Port: 443, Port: 443,
}, },
}, },
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
{ {
@@ -618,10 +577,9 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
Port: 443, Port: 443,
}, },
}, },
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
{ {
@@ -637,7 +595,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
Port: 443, Port: 443,
}, },
}, },
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
}, true), }, true),
expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`, expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`,
@@ -775,28 +733,27 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(1), TimeoutSeconds: int32Ptr(1),
}, },
{ {
Name: "webhook2.k8s.io", Name: "webhook2.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(15), TimeoutSeconds: int32Ptr(15),
}, },
{ {
Name: "webhook3.k8s.io", Name: "webhook3.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(30), TimeoutSeconds: int32Ptr(30),
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
errs := ValidateValidatingWebhookConfiguration(test.config, test.gv) errs := ValidateValidatingWebhookConfiguration(test.config)
err := errs.ToAggregate() err := errs.ToAggregate()
if err != nil { if err != nil {
if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" { if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
@@ -821,7 +778,6 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
name string name string
oldconfig *admissionregistration.ValidatingWebhookConfiguration oldconfig *admissionregistration.ValidatingWebhookConfiguration
config *admissionregistration.ValidatingWebhookConfiguration config *admissionregistration.ValidatingWebhookConfiguration
gv schema.GroupVersion
expectedError string expectedError string
}{ }{
{ {
@@ -903,7 +859,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
expectedError: `Invalid value: []string{"invalid-v1"}`, expectedError: `Invalid value: []string{"invalid-v1"}`,
}, },
{ {
name: "Webhooks must have unique names when not updated via v1beta1", name: "Webhooks must have unique names when old config has unique names",
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
@@ -923,7 +879,6 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &unknownSideEffect, SideEffects: &unknownSideEffect,
}, },
}, false), }, false),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`, expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
}, },
{ {
@@ -952,37 +907,12 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &unknownSideEffect, SideEffects: &unknownSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
expectedError: ``,
},
{
name: "Webhooks can have duplicate names when updated via v1beta1",
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
}, true),
oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
}, false),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
errs := ValidateValidatingWebhookConfigurationUpdate(test.config, test.oldconfig, test.gv) errs := ValidateValidatingWebhookConfigurationUpdate(test.config, test.oldconfig)
err := errs.ToAggregate() err := errs.ToAggregate()
if err != nil { if err != nil {
if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" { if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
@@ -1015,6 +945,7 @@ func newMutatingWebhookConfiguration(hooks []admissionregistration.MutatingWebho
} }
func TestValidateMutatingWebhookConfiguration(t *testing.T) { func TestValidateMutatingWebhookConfiguration(t *testing.T) {
noSideEffect := admissionregistration.SideEffectClassNone
unknownSideEffect := admissionregistration.SideEffectClassUnknown unknownSideEffect := admissionregistration.SideEffectClassUnknown
validClientConfig := admissionregistration.WebhookClientConfig{ validClientConfig := admissionregistration.WebhookClientConfig{
URL: strPtr("https://example.com"), URL: strPtr("https://example.com"),
@@ -1022,7 +953,6 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
config *admissionregistration.MutatingWebhookConfiguration config *admissionregistration.MutatingWebhookConfiguration
gv schema.GroupVersion
expectedError string expectedError string
}{ }{
{ {
@@ -1052,11 +982,10 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
AdmissionReviewVersions: []string{"v1beta1"}, AdmissionReviewVersions: []string{"v1beta1"},
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
{ {
@@ -1065,11 +994,10 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
AdmissionReviewVersions: []string{"v1beta1", "invalid-version"}, AdmissionReviewVersions: []string{"v1beta1", "invalid-version"},
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
{ {
@@ -1100,24 +1028,23 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
{ {
Name: "k8s.io", Name: "k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
{ {
Name: "", Name: "",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: `webhooks[1].name: Invalid value: "k8s.io": should be a domain with at least three segments separated by dots, webhooks[2].name: Required value`, expectedError: `webhooks[1].name: Invalid value: "k8s.io": should be a domain with at least three segments separated by dots, webhooks[2].name: Required value`,
}, },
{ {
name: "Webhooks must have unique names when not created via v1beta1", name: "Webhooks must have unique names when created",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
@@ -1130,26 +1057,8 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
SideEffects: &unknownSideEffect, SideEffects: &unknownSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`, expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
}, },
{
name: "Webhooks can have duplicate names when created via v1beta1",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{ {
name: "Operations must not be empty or nil", name: "Operations must not be empty or nil",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
@@ -1240,7 +1149,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
Rules: []admissionregistration.RuleWithOperations{ Rules: []admissionregistration.RuleWithOperations{
{ {
Operations: []admissionregistration.OperationType{"CREATE"}, Operations: []admissionregistration.OperationType{"CREATE"},
@@ -1253,7 +1162,6 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
}, },
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
}, },
{ {
name: `resource "*" cannot mix with resources that don't have subresources`, name: `resource "*" cannot mix with resources that don't have subresources`,
@@ -1303,7 +1211,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
Rules: []admissionregistration.RuleWithOperations{ Rules: []admissionregistration.RuleWithOperations{
{ {
Operations: []admissionregistration.OperationType{"CREATE"}, Operations: []admissionregistration.OperationType{"CREATE"},
@@ -1316,7 +1224,6 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
}, },
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
}, },
{ {
name: "resource */a cannot mix with x/a", name: "resource */a cannot mix with x/a",
@@ -1398,7 +1305,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`, expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`,
}, },
{ {
name: "SideEffects can only be \"Unknown\", \"None\", \"Some\", or \"NoneOnDryRun\" via v1beta1", name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" when created",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
@@ -1409,22 +1316,6 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
}(), }(),
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun", "Some", "Unknown"`,
},
{
name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" via v1",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: func() *admissionregistration.SideEffectClass {
r := admissionregistration.SideEffectClass("other")
return &r
}(),
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1"},
expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`, expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`,
}, },
{ {
@@ -1568,10 +1459,9 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
Port: 443, Port: 443,
}, },
}, },
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
{ {
@@ -1587,10 +1477,9 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
Port: 443, Port: 443,
}, },
}, },
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
{ {
@@ -1606,7 +1495,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
Port: 443, Port: 443,
}, },
}, },
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
}, },
}, true), }, true),
expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`, expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`,
@@ -1744,28 +1633,27 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(1), TimeoutSeconds: int32Ptr(1),
}, },
{ {
Name: "webhook2.k8s.io", Name: "webhook2.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(15), TimeoutSeconds: int32Ptr(15),
}, },
{ {
Name: "webhook3.k8s.io", Name: "webhook3.k8s.io",
ClientConfig: validClientConfig, ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect, SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(30), TimeoutSeconds: int32Ptr(30),
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
errs := ValidateMutatingWebhookConfiguration(test.config, test.gv) errs := ValidateMutatingWebhookConfiguration(test.config)
err := errs.ToAggregate() err := errs.ToAggregate()
if err != nil { if err != nil {
if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" { if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
@@ -1791,7 +1679,6 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) {
name string name string
oldconfig *admissionregistration.MutatingWebhookConfiguration oldconfig *admissionregistration.MutatingWebhookConfiguration
config *admissionregistration.MutatingWebhookConfiguration config *admissionregistration.MutatingWebhookConfiguration
gv schema.GroupVersion
expectedError string expectedError string
}{ }{
{ {
@@ -1917,35 +1804,10 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &unknownSideEffect, SideEffects: &unknownSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
expectedError: ``, expectedError: ``,
}, },
{ {
name: "Webhooks can have duplicate names when updated via v1beta1", name: "Webhooks can't have side effects when old config has no side effects",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
}, true),
oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
}, false),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{
name: "Webhooks can't have side effects when old config has no side effects via v1",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{ {
Name: "webhook.k8s.io", Name: "webhook.k8s.io",
@@ -1960,7 +1822,6 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &noSideEffect, SideEffects: &noSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1"},
expectedError: `Unsupported value: "Unknown": supported values: "None", "NoneOnDryRun"`, expectedError: `Unsupported value: "Unknown": supported values: "None", "NoneOnDryRun"`,
}, },
{ {
@@ -1979,32 +1840,12 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &unknownSideEffect, SideEffects: &unknownSideEffect,
}, },
}, true), }, true),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
expectedError: ``,
},
{
name: "Webhooks can have side effects when updated via v1beta1",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
},
}, true),
oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &noSideEffect,
},
}, false),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``, expectedError: ``,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
errs := ValidateMutatingWebhookConfigurationUpdate(test.config, test.oldconfig, test.gv) errs := ValidateMutatingWebhookConfigurationUpdate(test.config, test.oldconfig)
err := errs.ToAggregate() err := errs.ToAggregate()
if err != nil { if err != nil {
if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" { if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {

View File

@@ -25,14 +25,12 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation" utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
utilcert "k8s.io/client-go/util/cert" utilcert "k8s.io/client-go/util/cert"
"k8s.io/kubernetes/pkg/apis/certificates" "k8s.io/kubernetes/pkg/apis/certificates"
certificatesv1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation" apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
) )
@@ -139,8 +137,8 @@ func ValidateCertificateRequestName(name string, prefix bool) []string {
return nil return nil
} }
func ValidateCertificateSigningRequestCreate(csr *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList { func ValidateCertificateSigningRequestCreate(csr *certificates.CertificateSigningRequest) field.ErrorList {
opts := getValidationOptions(version, csr, nil) opts := getValidationOptions(csr, nil)
return validateCertificateSigningRequest(csr, opts) return validateCertificateSigningRequest(csr, opts)
} }
@@ -347,19 +345,19 @@ func ValidateCertificateSigningRequestSignerName(fldPath *field.Path, signerName
return el return el
} }
func ValidateCertificateSigningRequestUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList { func ValidateCertificateSigningRequestUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest) field.ErrorList {
opts := getValidationOptions(version, newCSR, oldCSR) opts := getValidationOptions(newCSR, oldCSR)
return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts) return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts)
} }
func ValidateCertificateSigningRequestStatusUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList { func ValidateCertificateSigningRequestStatusUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest) field.ErrorList {
opts := getValidationOptions(version, newCSR, oldCSR) opts := getValidationOptions(newCSR, oldCSR)
opts.allowSettingCertificate = true opts.allowSettingCertificate = true
return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts) return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts)
} }
func ValidateCertificateSigningRequestApprovalUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList { func ValidateCertificateSigningRequestApprovalUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest) field.ErrorList {
opts := getValidationOptions(version, newCSR, oldCSR) opts := getValidationOptions(newCSR, oldCSR)
opts.allowSettingApprovalConditions = true opts.allowSettingApprovalConditions = true
return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts) return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts)
} }
@@ -420,24 +418,19 @@ func findConditions(csr *certificates.CertificateSigningRequest, conditionType c
// compatible with the specified version and existing CSR. // compatible with the specified version and existing CSR.
// oldCSR may be nil if this is a create request. // oldCSR may be nil if this is a create request.
// validation options related to subresource-specific capabilities are set to false. // validation options related to subresource-specific capabilities are set to false.
func getValidationOptions(version schema.GroupVersion, newCSR, oldCSR *certificates.CertificateSigningRequest) certificateValidationOptions { func getValidationOptions(newCSR, oldCSR *certificates.CertificateSigningRequest) certificateValidationOptions {
return certificateValidationOptions{ return certificateValidationOptions{
allowResettingCertificate: allowResettingCertificate(version), allowResettingCertificate: false,
allowBothApprovedAndDenied: allowBothApprovedAndDenied(oldCSR), allowBothApprovedAndDenied: allowBothApprovedAndDenied(oldCSR),
allowLegacySignerName: allowLegacySignerName(version, oldCSR), allowLegacySignerName: allowLegacySignerName(oldCSR),
allowDuplicateConditionTypes: allowDuplicateConditionTypes(version, oldCSR), allowDuplicateConditionTypes: allowDuplicateConditionTypes(oldCSR),
allowEmptyConditionType: allowEmptyConditionType(version, oldCSR), allowEmptyConditionType: allowEmptyConditionType(oldCSR),
allowArbitraryCertificate: allowArbitraryCertificate(version, newCSR, oldCSR), allowArbitraryCertificate: allowArbitraryCertificate(newCSR, oldCSR),
allowDuplicateUsages: allowDuplicateUsages(version, oldCSR), allowDuplicateUsages: allowDuplicateUsages(oldCSR),
allowUnknownUsages: allowUnknownUsages(version, oldCSR), allowUnknownUsages: allowUnknownUsages(oldCSR),
} }
} }
func allowResettingCertificate(version schema.GroupVersion) bool {
// compatibility with v1beta1
return version == certificatesv1beta1.SchemeGroupVersion
}
func allowBothApprovedAndDenied(oldCSR *certificates.CertificateSigningRequest) bool { func allowBothApprovedAndDenied(oldCSR *certificates.CertificateSigningRequest) bool {
if oldCSR == nil { if oldCSR == nil {
return false return false
@@ -455,10 +448,8 @@ func allowBothApprovedAndDenied(oldCSR *certificates.CertificateSigningRequest)
return approved && denied return approved && denied
} }
func allowLegacySignerName(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool { func allowLegacySignerName(oldCSR *certificates.CertificateSigningRequest) bool {
switch { switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && oldCSR.Spec.SignerName == certificates.LegacyUnknownSignerName: case oldCSR != nil && oldCSR.Spec.SignerName == certificates.LegacyUnknownSignerName:
return true // compatibility with existing data return true // compatibility with existing data
default: default:
@@ -466,10 +457,8 @@ func allowLegacySignerName(version schema.GroupVersion, oldCSR *certificates.Cer
} }
} }
func allowDuplicateConditionTypes(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool { func allowDuplicateConditionTypes(oldCSR *certificates.CertificateSigningRequest) bool {
switch { switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && hasDuplicateConditionTypes(oldCSR): case oldCSR != nil && hasDuplicateConditionTypes(oldCSR):
return true // compatibility with existing data return true // compatibility with existing data
default: default:
@@ -487,10 +476,8 @@ func hasDuplicateConditionTypes(csr *certificates.CertificateSigningRequest) boo
return false return false
} }
func allowEmptyConditionType(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool { func allowEmptyConditionType(oldCSR *certificates.CertificateSigningRequest) bool {
switch { switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && hasEmptyConditionType(oldCSR): case oldCSR != nil && hasEmptyConditionType(oldCSR):
return true // compatibility with existing data return true // compatibility with existing data
default: default:
@@ -506,10 +493,8 @@ func hasEmptyConditionType(csr *certificates.CertificateSigningRequest) bool {
return false return false
} }
func allowArbitraryCertificate(version schema.GroupVersion, newCSR, oldCSR *certificates.CertificateSigningRequest) bool { func allowArbitraryCertificate(newCSR, oldCSR *certificates.CertificateSigningRequest) bool {
switch { switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case newCSR != nil && oldCSR != nil && bytes.Equal(newCSR.Status.Certificate, oldCSR.Status.Certificate): case newCSR != nil && oldCSR != nil && bytes.Equal(newCSR.Status.Certificate, oldCSR.Status.Certificate):
return true // tolerate updates that don't touch status.certificate return true // tolerate updates that don't touch status.certificate
case oldCSR != nil && validateCertificate(oldCSR.Status.Certificate) != nil: case oldCSR != nil && validateCertificate(oldCSR.Status.Certificate) != nil:
@@ -519,10 +504,8 @@ func allowArbitraryCertificate(version schema.GroupVersion, newCSR, oldCSR *cert
} }
} }
func allowUnknownUsages(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool { func allowUnknownUsages(oldCSR *certificates.CertificateSigningRequest) bool {
switch { switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && hasUnknownUsage(oldCSR.Spec.Usages): case oldCSR != nil && hasUnknownUsage(oldCSR.Spec.Usages):
return true // compatibility with existing data return true // compatibility with existing data
default: default:
@@ -539,10 +522,8 @@ func hasUnknownUsage(usages []certificates.KeyUsage) bool {
return false return false
} }
func allowDuplicateUsages(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool { func allowDuplicateUsages(oldCSR *certificates.CertificateSigningRequest) bool {
switch { switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && hasDuplicateUsage(oldCSR.Spec.Usages): case oldCSR != nil && hasDuplicateUsage(oldCSR.Spec.Usages):
return true // compatibility with existing data return true // compatibility with existing data
default: default:

View File

@@ -29,12 +29,10 @@ import (
"time" "time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/client-go/util/certificate/csr" "k8s.io/client-go/util/certificate/csr"
capi "k8s.io/kubernetes/pkg/apis/certificates" capi "k8s.io/kubernetes/pkg/apis/certificates"
capiv1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
"k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
) )
@@ -54,7 +52,6 @@ func TestValidateCertificateSigningRequestCreate(t *testing.T) {
maxLengthSignerName := fmt.Sprintf("%s/%s.%s", maxLengthFQDN, repeatString("a", 63), repeatString("a", 253)) maxLengthSignerName := fmt.Sprintf("%s/%s.%s", maxLengthFQDN, repeatString("a", 63), repeatString("a", 253))
tests := map[string]struct { tests := map[string]struct {
csr capi.CertificateSigningRequest csr capi.CertificateSigningRequest
gv schema.GroupVersion
errs field.ErrorList errs field.ErrorList
}{ }{
"CSR with empty request data should fail": { "CSR with empty request data should fail": {
@@ -342,19 +339,7 @@ func TestValidateCertificateSigningRequestCreate(t *testing.T) {
field.Required(specPath.Child("usages"), "usages must be provided"), field.Required(specPath.Child("usages"), "usages must be provided"),
}, },
}, },
"unknown and duplicate usages - v1beta1": { "unknown and duplicate usages": {
gv: schema.GroupVersion{Group: capi.SchemeGroupVersion.Group, Version: "v1beta1"},
csr: capi.CertificateSigningRequest{
ObjectMeta: validObjectMeta,
Spec: capi.CertificateSigningRequestSpec{
Usages: []capi.KeyUsage{"unknown", "unknown"},
Request: newCSRPEM(t),
SignerName: validSignerName,
},
},
},
"unknown and duplicate usages - v1": {
gv: schema.GroupVersion{Group: capi.SchemeGroupVersion.Group, Version: "v1"},
csr: capi.CertificateSigningRequest{ csr: capi.CertificateSigningRequest{
ObjectMeta: validObjectMeta, ObjectMeta: validObjectMeta,
Spec: capi.CertificateSigningRequestSpec{ Spec: capi.CertificateSigningRequestSpec{
@@ -372,7 +357,7 @@ func TestValidateCertificateSigningRequestCreate(t *testing.T) {
} }
for name, test := range tests { for name, test := range tests {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
el := ValidateCertificateSigningRequestCreate(&test.csr, test.gv) el := ValidateCertificateSigningRequestCreate(&test.csr)
if !reflect.DeepEqual(el, test.errs) { if !reflect.DeepEqual(el, test.errs) {
t.Errorf("returned and expected errors did not match - expected\n%v\nbut got\n%v", test.errs.ToAggregate(), el.ToAggregate()) t.Errorf("returned and expected errors did not match - expected\n%v\nbut got\n%v", test.errs.ToAggregate(), el.ToAggregate())
} }
@@ -420,59 +405,23 @@ func newCSRPEM(t *testing.T) []byte {
func Test_getValidationOptions(t *testing.T) { func Test_getValidationOptions(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
version schema.GroupVersion newCSR *capi.CertificateSigningRequest
newCSR *capi.CertificateSigningRequest oldCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest want certificateValidationOptions
want certificateValidationOptions
}{ }{
{ {
name: "v1beta1 compatible create", name: "strict create",
version: capiv1beta1.SchemeGroupVersion, oldCSR: nil,
oldCSR: nil, want: certificateValidationOptions{},
want: certificateValidationOptions{
allowResettingCertificate: true,
allowBothApprovedAndDenied: false,
allowLegacySignerName: true,
allowDuplicateConditionTypes: true,
allowEmptyConditionType: true,
allowArbitraryCertificate: true,
allowUnknownUsages: true,
allowDuplicateUsages: true,
},
}, },
{ {
name: "v1 strict create", name: "strict update",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"}, oldCSR: &capi.CertificateSigningRequest{},
oldCSR: nil, want: certificateValidationOptions{},
want: certificateValidationOptions{},
}, },
{ {
name: "v1beta1 compatible update", name: "compatible update, approved+denied",
version: capiv1beta1.SchemeGroupVersion,
oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateDenied}},
}},
want: certificateValidationOptions{
allowResettingCertificate: true,
allowBothApprovedAndDenied: true, // existing object has both approved and denied
allowLegacySignerName: true,
allowDuplicateConditionTypes: true,
allowEmptyConditionType: true,
allowArbitraryCertificate: true,
allowUnknownUsages: true,
allowDuplicateUsages: true,
},
},
{
name: "v1 strict update",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
oldCSR: &capi.CertificateSigningRequest{},
want: certificateValidationOptions{},
},
{
name: "v1 compatible update, approved+denied",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateDenied}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateDenied}},
}}, }},
@@ -481,16 +430,14 @@ func Test_getValidationOptions(t *testing.T) {
}, },
}, },
{ {
name: "v1 compatible update, legacy signerName", name: "compatible update, legacy signerName",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"}, oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{SignerName: capi.LegacyUnknownSignerName}},
oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{SignerName: capi.LegacyUnknownSignerName}},
want: certificateValidationOptions{ want: certificateValidationOptions{
allowLegacySignerName: true, allowLegacySignerName: true,
}, },
}, },
{ {
name: "v1 compatible update, duplicate condition types", name: "compatible update, duplicate condition types",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateApproved}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateApproved}},
}}, }},
@@ -499,8 +446,7 @@ func Test_getValidationOptions(t *testing.T) {
}, },
}, },
{ {
name: "v1 compatible update, empty condition types", name: "compatible update, empty condition types",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{}}, Conditions: []capi.CertificateSigningRequestCondition{{}},
}}, }},
@@ -509,8 +455,7 @@ func Test_getValidationOptions(t *testing.T) {
}, },
}, },
{ {
name: "v1 compatible update, no diff to certificate", name: "compatible update, no diff to certificate",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{ newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
Certificate: validCertificate, Certificate: validCertificate,
}}, }},
@@ -522,8 +467,7 @@ func Test_getValidationOptions(t *testing.T) {
}, },
}, },
{ {
name: "v1 compatible update, existing invalid certificate", name: "compatible update, existing invalid certificate",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{ newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
Certificate: []byte(`new - no PEM blocks`), Certificate: []byte(`new - no PEM blocks`),
}}, }},
@@ -535,17 +479,15 @@ func Test_getValidationOptions(t *testing.T) {
}, },
}, },
{ {
name: "v1 compatible update, existing unknown usages", name: "compatible update, existing unknown usages",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"}, oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"unknown"}}},
oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"unknown"}}},
want: certificateValidationOptions{ want: certificateValidationOptions{
allowUnknownUsages: true, allowUnknownUsages: true,
}, },
}, },
{ {
name: "v1 compatible update, existing duplicate usages", name: "compatible update, existing duplicate usages",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"}, oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"any", "any"}}},
oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"any", "any"}}},
want: certificateValidationOptions{ want: certificateValidationOptions{
allowDuplicateUsages: true, allowDuplicateUsages: true,
}, },
@@ -553,7 +495,7 @@ func Test_getValidationOptions(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := getValidationOptions(tt.version, tt.newCSR, tt.oldCSR); !reflect.DeepEqual(got, tt.want) { if got := getValidationOptions(tt.newCSR, tt.oldCSR); !reflect.DeepEqual(got, tt.want) {
t.Errorf("got %#v\nwant %#v", got, tt.want) t.Errorf("got %#v\nwant %#v", got, tt.want)
} }
}) })
@@ -574,10 +516,10 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string name string
newCSR *capi.CertificateSigningRequest newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest oldCSR *capi.CertificateSigningRequest
versionErrs map[string][]string errs []string
}{ }{
{ {
name: "no-op", name: "no-op",
@@ -595,9 +537,8 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not add a condition of type "Approved"`}, `status.conditions: Forbidden: updates may not add a condition of type "Approved"`,
"v1beta1": {`status.conditions: Forbidden: updates may not add a condition of type "Approved"`},
}, },
}, },
{ {
@@ -606,9 +547,8 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`}, `status.conditions: Forbidden: updates may not remove a condition of type "Approved"`,
"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`},
}, },
}, },
{ {
@@ -617,9 +557,8 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not add a condition of type "Denied"`}, `status.conditions: Forbidden: updates may not add a condition of type "Denied"`,
"v1beta1": {`status.conditions: Forbidden: updates may not add a condition of type "Denied"`},
}, },
}, },
{ {
@@ -628,9 +567,8 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`}, `status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`},
}, },
}, },
{ {
@@ -638,8 +576,8 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{}, errs: []string{},
}, },
{ {
name: "remove Failed condition", name: "remove Failed condition",
@@ -647,9 +585,8 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`}, `status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`},
}, },
}, },
{ {
@@ -658,29 +595,26 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
Certificate: validCertificate, Certificate: validCertificate,
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.certificate: Forbidden: updates may not set certificate content`}, `status.certificate: Forbidden: updates may not set certificate content`,
"v1beta1": {`status.certificate: Forbidden: updates may not set certificate content`},
}, },
}, },
} }
for _, tt := range tests { for _, tt := range tests {
for _, version := range []string{"v1", "v1beta1"} { t.Run(tt.name, func(t *testing.T) {
t.Run(tt.name+"_"+version, func(t *testing.T) { gotErrs := sets.NewString()
gotErrs := sets.NewString() for _, err := range ValidateCertificateSigningRequestUpdate(tt.newCSR, tt.oldCSR) {
for _, err := range ValidateCertificateSigningRequestUpdate(tt.newCSR, tt.oldCSR, schema.GroupVersion{Group: capi.GroupName, Version: version}) { gotErrs.Insert(err.Error())
gotErrs.Insert(err.Error()) }
} wantErrs := sets.NewString(tt.errs...)
wantErrs := sets.NewString(tt.versionErrs[version]...) for _, missing := range wantErrs.Difference(gotErrs).List() {
for _, missing := range wantErrs.Difference(gotErrs).List() { t.Errorf("missing expected error: %s", missing)
t.Errorf("missing expected error: %s", missing) }
} for _, unexpected := range gotErrs.Difference(wantErrs).List() {
for _, unexpected := range gotErrs.Difference(wantErrs).List() { t.Errorf("unexpected error: %s", unexpected)
t.Errorf("unexpected error: %s", unexpected) }
} })
})
}
} }
} }
@@ -698,10 +632,10 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string name string
newCSR *capi.CertificateSigningRequest newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest oldCSR *capi.CertificateSigningRequest
versionErrs map[string][]string errs []string
}{ }{
{ {
name: "no-op", name: "no-op",
@@ -732,9 +666,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not add a condition of type "Approved"`}, `status.conditions: Forbidden: updates may not add a condition of type "Approved"`,
"v1beta1": {`status.conditions: Forbidden: updates may not add a condition of type "Approved"`},
}, },
}, },
{ {
@@ -743,9 +676,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`}, `status.conditions: Forbidden: updates may not remove a condition of type "Approved"`,
"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`},
}, },
}, },
{ {
@@ -754,9 +686,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not add a condition of type "Denied"`}, `status.conditions: Forbidden: updates may not add a condition of type "Denied"`,
"v1beta1": {`status.conditions: Forbidden: updates may not add a condition of type "Denied"`},
}, },
}, },
{ {
@@ -765,9 +696,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`}, `status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`},
}, },
}, },
{ {
@@ -775,8 +705,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{}, errs: []string{},
}, },
{ {
name: "remove Failed condition", name: "remove Failed condition",
@@ -784,9 +714,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`}, `status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`},
}, },
}, },
{ {
@@ -794,8 +723,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Certificate: validCertificate, Certificate: validCertificate,
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{}, errs: []string{},
}, },
{ {
name: "set invalid certificate", name: "set invalid certificate",
@@ -803,8 +732,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
Certificate: invalidCertificateNoPEM, Certificate: invalidCertificateNoPEM,
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.certificate: Invalid value: "<certificate data>": must contain at least one CERTIFICATE PEM block`}, `status.certificate: Invalid value: "<certificate data>": must contain at least one CERTIFICATE PEM block`,
}, },
}, },
{ {
@@ -815,28 +744,26 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Certificate: invalidCertificateNoPEM, Certificate: invalidCertificateNoPEM,
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.certificate: Forbidden: updates may not modify existing certificate content`}, `status.certificate: Forbidden: updates may not modify existing certificate content`,
}, },
}, },
} }
for _, tt := range tests { for _, tt := range tests {
for _, version := range []string{"v1", "v1beta1"} { t.Run(tt.name, func(t *testing.T) {
t.Run(tt.name+"_"+version, func(t *testing.T) { gotErrs := sets.NewString()
gotErrs := sets.NewString() for _, err := range ValidateCertificateSigningRequestStatusUpdate(tt.newCSR, tt.oldCSR) {
for _, err := range ValidateCertificateSigningRequestStatusUpdate(tt.newCSR, tt.oldCSR, schema.GroupVersion{Group: capi.GroupName, Version: version}) { gotErrs.Insert(err.Error())
gotErrs.Insert(err.Error()) }
} wantErrs := sets.NewString(tt.errs...)
wantErrs := sets.NewString(tt.versionErrs[version]...) for _, missing := range wantErrs.Difference(gotErrs).List() {
for _, missing := range wantErrs.Difference(gotErrs).List() { t.Errorf("missing expected error: %s", missing)
t.Errorf("missing expected error: %s", missing) }
} for _, unexpected := range gotErrs.Difference(wantErrs).List() {
for _, unexpected := range gotErrs.Difference(wantErrs).List() { t.Errorf("unexpected error: %s", unexpected)
t.Errorf("unexpected error: %s", unexpected) }
} })
})
}
} }
} }
@@ -854,10 +781,10 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string name string
newCSR *capi.CertificateSigningRequest newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest oldCSR *capi.CertificateSigningRequest
versionErrs map[string][]string errs []string
}{ }{
{ {
name: "no-op", name: "no-op",
@@ -882,9 +809,8 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`}, `status.conditions: Forbidden: updates may not remove a condition of type "Approved"`,
"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Approved"`},
}, },
}, },
{ {
@@ -900,9 +826,8 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`}, `status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`},
}, },
}, },
{ {
@@ -910,8 +835,8 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{}, errs: []string{},
}, },
{ {
name: "remove Failed condition", name: "remove Failed condition",
@@ -919,9 +844,8 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}}, Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}}, }},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`}, `status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
"v1beta1": {`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`},
}, },
}, },
{ {
@@ -930,29 +854,26 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
Certificate: validCertificate, Certificate: validCertificate,
}}, }},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec}, oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{ errs: []string{
"v1": {`status.certificate: Forbidden: updates may not set certificate content`}, `status.certificate: Forbidden: updates may not set certificate content`,
"v1beta1": {`status.certificate: Forbidden: updates may not set certificate content`},
}, },
}, },
} }
for _, tt := range tests { for _, tt := range tests {
for _, version := range []string{"v1", "v1beta1"} { t.Run(tt.name, func(t *testing.T) {
t.Run(tt.name+"_"+version, func(t *testing.T) { gotErrs := sets.NewString()
gotErrs := sets.NewString() for _, err := range ValidateCertificateSigningRequestApprovalUpdate(tt.newCSR, tt.oldCSR) {
for _, err := range ValidateCertificateSigningRequestApprovalUpdate(tt.newCSR, tt.oldCSR, schema.GroupVersion{Group: capi.GroupName, Version: version}) { gotErrs.Insert(err.Error())
gotErrs.Insert(err.Error()) }
} wantErrs := sets.NewString(tt.errs...)
wantErrs := sets.NewString(tt.versionErrs[version]...) for _, missing := range wantErrs.Difference(gotErrs).List() {
for _, missing := range wantErrs.Difference(gotErrs).List() { t.Errorf("missing expected error: %s", missing)
t.Errorf("missing expected error: %s", missing) }
} for _, unexpected := range gotErrs.Difference(wantErrs).List() {
for _, unexpected := range gotErrs.Difference(wantErrs).List() { t.Errorf("unexpected error: %s", unexpected)
t.Errorf("unexpected error: %s", unexpected) }
} })
})
}
} }
} }

View File

@@ -21,12 +21,9 @@ import (
"net" "net"
"strings" "strings"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
pathvalidation "k8s.io/apimachinery/pkg/api/validation/path" pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation" unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation" "k8s.io/apimachinery/pkg/util/validation"
@@ -230,20 +227,20 @@ type IngressValidationOptions struct {
} }
// ValidateIngress validates Ingresses on create and update. // ValidateIngress validates Ingresses on create and update.
func validateIngress(ingress *networking.Ingress, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateIngress(ingress *networking.Ingress, opts IngressValidationOptions) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata")) allErrs := apivalidation.ValidateObjectMeta(&ingress.ObjectMeta, true, ValidateIngressName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"), opts, requestGV)...) allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"), opts)...)
return allErrs return allErrs
} }
// ValidateIngressCreate validates Ingresses on create. // ValidateIngressCreate validates Ingresses on create.
func ValidateIngressCreate(ingress *networking.Ingress, requestGV schema.GroupVersion) field.ErrorList { func ValidateIngressCreate(ingress *networking.Ingress) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
opts := IngressValidationOptions{ opts := IngressValidationOptions{
AllowInvalidSecretName: allowInvalidSecretName(requestGV, nil), AllowInvalidSecretName: false,
AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(requestGV, nil), AllowInvalidWildcardHostRule: false,
} }
allErrs = append(allErrs, validateIngress(ingress, opts, requestGV)...) allErrs = append(allErrs, validateIngress(ingress, opts)...)
annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass] annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass]
if annotationIsSet && ingress.Spec.IngressClassName != nil { if annotationIsSet && ingress.Spec.IngressClassName != nil {
annotationPath := field.NewPath("annotations").Child(annotationIngressClass) annotationPath := field.NewPath("annotations").Child(annotationIngressClass)
@@ -253,14 +250,14 @@ func ValidateIngressCreate(ingress *networking.Ingress, requestGV schema.GroupVe
} }
// ValidateIngressUpdate validates ingresses on update. // ValidateIngressUpdate validates ingresses on update.
func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress, requestGV schema.GroupVersion) field.ErrorList { func ValidateIngressUpdate(ingress, oldIngress *networking.Ingress) field.ErrorList {
allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata")) allErrs := apivalidation.ValidateObjectMetaUpdate(&ingress.ObjectMeta, &oldIngress.ObjectMeta, field.NewPath("metadata"))
opts := IngressValidationOptions{ opts := IngressValidationOptions{
AllowInvalidSecretName: allowInvalidSecretName(requestGV, oldIngress), AllowInvalidSecretName: allowInvalidSecretName(oldIngress),
AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(requestGV, oldIngress), AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(oldIngress),
} }
allErrs = append(allErrs, validateIngress(ingress, opts, requestGV)...) allErrs = append(allErrs, validateIngress(ingress, opts)...)
return allErrs return allErrs
} }
@@ -291,29 +288,18 @@ func validateIngressTLS(spec *networking.IngressSpec, fldPath *field.Path, opts
return allErrs return allErrs
} }
// defaultBackendFieldName returns the name of the field used for defaultBackend
// in the provided GroupVersion.
func defaultBackendFieldName(gv schema.GroupVersion) string {
switch gv {
case networkingv1beta1.SchemeGroupVersion, extensionsv1beta1.SchemeGroupVersion:
return "backend"
default:
return "defaultBackend"
}
}
// ValidateIngressSpec tests if required fields in the IngressSpec are set. // ValidateIngressSpec tests if required fields in the IngressSpec are set.
func ValidateIngressSpec(spec *networking.IngressSpec, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func ValidateIngressSpec(spec *networking.IngressSpec, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(spec.Rules) == 0 && spec.DefaultBackend == nil { if len(spec.Rules) == 0 && spec.DefaultBackend == nil {
errMsg := fmt.Sprintf("either `%s` or `rules` must be specified", defaultBackendFieldName(requestGV)) errMsg := fmt.Sprintf("either `%s` or `rules` must be specified", "defaultBackend")
allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, errMsg)) allErrs = append(allErrs, field.Invalid(fldPath, spec.Rules, errMsg))
} }
if spec.DefaultBackend != nil { if spec.DefaultBackend != nil {
allErrs = append(allErrs, validateIngressBackend(spec.DefaultBackend, fldPath.Child(defaultBackendFieldName(requestGV)), opts, requestGV)...) allErrs = append(allErrs, validateIngressBackend(spec.DefaultBackend, fldPath.Child("defaultBackend"), opts)...)
} }
if len(spec.Rules) > 0 { if len(spec.Rules) > 0 {
allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"), opts, requestGV)...) allErrs = append(allErrs, validateIngressRules(spec.Rules, fldPath.Child("rules"), opts)...)
} }
if len(spec.TLS) > 0 { if len(spec.TLS) > 0 {
allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"), opts)...) allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"), opts)...)
@@ -333,7 +319,7 @@ func ValidateIngressStatusUpdate(ingress, oldIngress *networking.Ingress) field.
return allErrs return allErrs
} }
func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(ingressRules) == 0 { if len(ingressRules) == 0 {
return append(allErrs, field.Required(fldPath, "")) return append(allErrs, field.Required(fldPath, ""))
@@ -359,32 +345,32 @@ func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.
} }
if !wildcardHost || !opts.AllowInvalidWildcardHostRule { if !wildcardHost || !opts.AllowInvalidWildcardHostRule {
allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(i), opts, requestGV)...) allErrs = append(allErrs, validateIngressRuleValue(&ih.IngressRuleValue, fldPath.Index(i), opts)...)
} }
} }
return allErrs return allErrs
} }
func validateIngressRuleValue(ingressRule *networking.IngressRuleValue, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateIngressRuleValue(ingressRule *networking.IngressRuleValue, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if ingressRule.HTTP != nil { if ingressRule.HTTP != nil {
allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"), opts, requestGV)...) allErrs = append(allErrs, validateHTTPIngressRuleValue(ingressRule.HTTP, fldPath.Child("http"), opts)...)
} }
return allErrs return allErrs
} }
func validateHTTPIngressRuleValue(httpIngressRuleValue *networking.HTTPIngressRuleValue, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateHTTPIngressRuleValue(httpIngressRuleValue *networking.HTTPIngressRuleValue, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(httpIngressRuleValue.Paths) == 0 { if len(httpIngressRuleValue.Paths) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("paths"), "")) allErrs = append(allErrs, field.Required(fldPath.Child("paths"), ""))
} }
for i, path := range httpIngressRuleValue.Paths { for i, path := range httpIngressRuleValue.Paths {
allErrs = append(allErrs, validateHTTPIngressPath(&path, fldPath.Child("paths").Index(i), opts, requestGV)...) allErrs = append(allErrs, validateHTTPIngressPath(&path, fldPath.Child("paths").Index(i), opts)...)
} }
return allErrs return allErrs
} }
func validateHTTPIngressPath(path *networking.HTTPIngressPath, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateHTTPIngressPath(path *networking.HTTPIngressPath, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if path.PathType == nil { if path.PathType == nil {
@@ -418,45 +404,12 @@ func validateHTTPIngressPath(path *networking.HTTPIngressPath, fldPath *field.Pa
default: default:
allErrs = append(allErrs, field.NotSupported(fldPath.Child("pathType"), *path.PathType, supportedPathTypes.List())) allErrs = append(allErrs, field.NotSupported(fldPath.Child("pathType"), *path.PathType, supportedPathTypes.List()))
} }
allErrs = append(allErrs, validateIngressBackend(&path.Backend, fldPath.Child("backend"), opts, requestGV)...) allErrs = append(allErrs, validateIngressBackend(&path.Backend, fldPath.Child("backend"), opts)...)
return allErrs return allErrs
} }
// numberPortField returns the field path to a service port number field
// relative to a backend struct in the provided GroupVersion
func numberPortField(numberPortFieldPath *field.Path, gv schema.GroupVersion) *field.Path {
switch gv {
case networkingv1beta1.SchemeGroupVersion, extensionsv1beta1.SchemeGroupVersion:
return numberPortFieldPath.Child("servicePort")
default:
return numberPortFieldPath.Child("service", "port", "number")
}
}
// namedPortField returns the field path to a service port name field
// relative to a backend struct in the provided GroupVersion
func namedPortField(namedPortFieldPath *field.Path, gv schema.GroupVersion) *field.Path {
switch gv {
case networkingv1beta1.SchemeGroupVersion, extensionsv1beta1.SchemeGroupVersion:
return namedPortFieldPath.Child("servicePort")
default:
return namedPortFieldPath.Child("service", "port", "name")
}
}
// serviceNameFieldPath returns the name of the field used for a
// service name in the provided GroupVersion.
func serviceNameFieldPath(backendFieldPath *field.Path, gv schema.GroupVersion) *field.Path {
switch gv {
case networkingv1beta1.SchemeGroupVersion, extensionsv1beta1.SchemeGroupVersion:
return backendFieldPath.Child("serviceName")
default:
return backendFieldPath.Child("service", "name")
}
}
// validateIngressBackend tests if a given backend is valid. // validateIngressBackend tests if a given backend is valid.
func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.Path, opts IngressValidationOptions, requestGV schema.GroupVersion) field.ErrorList { func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.Path, opts IngressValidationOptions) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
hasResourceBackend := backend.Resource != nil hasResourceBackend := backend.Resource != nil
@@ -470,10 +423,10 @@ func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.P
case hasServiceBackend: case hasServiceBackend:
if len(backend.Service.Name) == 0 { if len(backend.Service.Name) == 0 {
allErrs = append(allErrs, field.Required(serviceNameFieldPath(fldPath, requestGV), "")) allErrs = append(allErrs, field.Required(fldPath.Child("service", "name"), ""))
} else { } else {
for _, msg := range apivalidation.ValidateServiceName(backend.Service.Name, false) { for _, msg := range apivalidation.ValidateServiceName(backend.Service.Name, false) {
allErrs = append(allErrs, field.Invalid(serviceNameFieldPath(fldPath, requestGV), backend.Service.Name, msg)) allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "name"), backend.Service.Name, msg))
} }
} }
@@ -483,11 +436,11 @@ func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.P
allErrs = append(allErrs, field.Invalid(fldPath, "", "cannot set both port name & port number")) allErrs = append(allErrs, field.Invalid(fldPath, "", "cannot set both port name & port number"))
} else if hasPortName { } else if hasPortName {
for _, msg := range validation.IsValidPortName(backend.Service.Port.Name) { for _, msg := range validation.IsValidPortName(backend.Service.Port.Name) {
allErrs = append(allErrs, field.Invalid(namedPortField(fldPath, requestGV), backend.Service.Port.Name, msg)) allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "port", "name"), backend.Service.Port.Name, msg))
} }
} else if hasPortNumber { } else if hasPortNumber {
for _, msg := range validation.IsValidPortNum(int(backend.Service.Port.Number)) { for _, msg := range validation.IsValidPortNum(int(backend.Service.Port.Number)) {
allErrs = append(allErrs, field.Invalid(numberPortField(fldPath, requestGV), backend.Service.Port.Number, msg)) allErrs = append(allErrs, field.Invalid(fldPath.Child("service", "port", "number"), backend.Service.Port.Number, msg))
} }
} else { } else {
allErrs = append(allErrs, field.Required(fldPath, "port name or number is required")) allErrs = append(allErrs, field.Required(fldPath, "port name or number is required"))
@@ -616,11 +569,7 @@ func validateIngressClassParametersReference(params *networking.IngressClassPara
return allErrs return allErrs
} }
func allowInvalidSecretName(gv schema.GroupVersion, oldIngress *networking.Ingress) bool { func allowInvalidSecretName(oldIngress *networking.Ingress) bool {
if gv == networkingv1beta1.SchemeGroupVersion || gv == extensionsv1beta1.SchemeGroupVersion {
// backwards compatibility with released API versions that allowed invalid names
return true
}
if oldIngress != nil { if oldIngress != nil {
for _, tls := range oldIngress.Spec.TLS { for _, tls := range oldIngress.Spec.TLS {
if len(validateTLSSecretName(tls.SecretName)) > 0 { if len(validateTLSSecretName(tls.SecretName)) > 0 {
@@ -639,14 +588,10 @@ func validateTLSSecretName(name string) []string {
return apivalidation.ValidateSecretName(name, false) return apivalidation.ValidateSecretName(name, false)
} }
func allowInvalidWildcardHostRule(gv schema.GroupVersion, oldIngress *networking.Ingress) bool { func allowInvalidWildcardHostRule(oldIngress *networking.Ingress) bool {
if gv == networkingv1beta1.SchemeGroupVersion || gv == extensionsv1beta1.SchemeGroupVersion {
// backwards compatibility with released API versions that allowed invalid rules
return true
}
if oldIngress != nil { if oldIngress != nil {
for _, rule := range oldIngress.Spec.Rules { for _, rule := range oldIngress.Spec.Rules {
if strings.Contains(rule.Host, "*") && len(validateIngressRuleValue(&rule.IngressRuleValue, nil, IngressValidationOptions{}, gv)) > 0 { if strings.Contains(rule.Host, "*") && len(validateIngressRuleValue(&rule.IngressRuleValue, nil, IngressValidationOptions{})) > 0 {
// backwards compatibility with existing invalid data // backwards compatibility with existing invalid data
return true return true
} }

View File

@@ -21,11 +21,8 @@ import (
"strings" "strings"
"testing" "testing"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation" apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
@@ -514,7 +511,6 @@ func TestValidateIngress(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
groupVersion *schema.GroupVersion
tweakIngress func(ing *networking.Ingress) tweakIngress func(ing *networking.Ingress)
expectErrsOnFields []string expectErrsOnFields []string
}{ }{
@@ -531,13 +527,12 @@ func TestValidateIngress(t *testing.T) {
expectErrsOnFields: []string{}, expectErrsOnFields: []string{},
}, },
// invalid use cases // invalid use cases
"backend (v1beta1) with no service": { "backend with no service": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ing *networking.Ingress) { tweakIngress: func(ing *networking.Ingress) {
ing.Spec.DefaultBackend.Service.Name = "" ing.Spec.DefaultBackend.Service.Name = ""
}, },
expectErrsOnFields: []string{ expectErrsOnFields: []string{
"spec.backend.serviceName", "spec.defaultBackend.service.name",
}, },
}, },
"invalid path type": { "invalid path type": {
@@ -654,7 +649,6 @@ func TestValidateIngress(t *testing.T) {
}, },
}, },
"spec.backend resource and service name are not allowed together": { "spec.backend resource and service name are not allowed together": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ing *networking.Ingress) { tweakIngress: func(ing *networking.Ingress) {
ing.Spec.DefaultBackend = &networking.IngressBackend{ ing.Spec.DefaultBackend = &networking.IngressBackend{
Service: serviceBackend, Service: serviceBackend,
@@ -666,11 +660,10 @@ func TestValidateIngress(t *testing.T) {
} }
}, },
expectErrsOnFields: []string{ expectErrsOnFields: []string{
"spec.backend", "spec.defaultBackend",
}, },
}, },
"spec.backend resource and service port are not allowed together": { "spec.backend resource and service port are not allowed together": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ing *networking.Ingress) { tweakIngress: func(ing *networking.Ingress) {
ing.Spec.DefaultBackend = &networking.IngressBackend{ ing.Spec.DefaultBackend = &networking.IngressBackend{
Service: serviceBackend, Service: serviceBackend,
@@ -682,7 +675,7 @@ func TestValidateIngress(t *testing.T) {
} }
}, },
expectErrsOnFields: []string{ expectErrsOnFields: []string{
"spec.backend", "spec.defaultBackend",
}, },
}, },
} }
@@ -691,11 +684,7 @@ func TestValidateIngress(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
ingress := baseIngress.DeepCopy() ingress := baseIngress.DeepCopy()
testCase.tweakIngress(ingress) testCase.tweakIngress(ingress)
gv := testCase.groupVersion errs := validateIngress(ingress, IngressValidationOptions{})
if gv == nil {
gv = &networkingv1.SchemeGroupVersion
}
errs := validateIngress(ingress, IngressValidationOptions{}, *gv)
if len(testCase.expectErrsOnFields) != len(errs) { if len(testCase.expectErrsOnFields) != len(errs) {
t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectErrsOnFields), len(errs), errs) t.Fatalf("Expected %d errors, got %d errors: %v", len(testCase.expectErrsOnFields), len(errs), errs)
} }
@@ -718,7 +707,6 @@ func TestValidateIngressRuleValue(t *testing.T) {
} }
fldPath := field.NewPath("testing.http.paths[0].path") fldPath := field.NewPath("testing.http.paths[0].path")
testCases := map[string]struct { testCases := map[string]struct {
groupVersion *schema.GroupVersion
pathType networking.PathType pathType networking.PathType
path string path string
expectedErrs field.ErrorList expectedErrs field.ErrorList
@@ -820,11 +808,7 @@ func TestValidateIngressRuleValue(t *testing.T) {
}, },
}, },
} }
gv := testCase.groupVersion errs := validateIngressRuleValue(irv, field.NewPath("testing"), IngressValidationOptions{})
if gv == nil {
gv = &networkingv1.SchemeGroupVersion
}
errs := validateIngressRuleValue(irv, field.NewPath("testing"), IngressValidationOptions{}, *gv)
if len(errs) != len(testCase.expectedErrs) { if len(errs) != len(testCase.expectedErrs) {
t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs)
} }
@@ -868,7 +852,6 @@ func TestValidateIngressCreate(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
groupVersion *schema.GroupVersion
tweakIngress func(ingress *networking.Ingress) tweakIngress func(ingress *networking.Ingress)
expectedErrs field.ErrorList expectedErrs field.ErrorList
}{ }{
@@ -950,33 +933,18 @@ func TestValidateIngressCreate(t *testing.T) {
}, },
expectedErrs: field.ErrorList{}, expectedErrs: field.ErrorList{},
}, },
"v1: valid secret": { "valid secret": {
groupVersion: &networkingv1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) { tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}} ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
}, },
}, },
"v1: invalid secret": { "invalid secret": {
groupVersion: &networkingv1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) { tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}} ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}}
}, },
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("tls").Index(0).Child("secretName"), "invalid name", `a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`)}, expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("tls").Index(0).Child("secretName"), "invalid name", `a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`)},
}, },
"v1beta1: valid secret": { "valid rules with wildcard host": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
},
},
"v1beta1: invalid secret": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 1"}}
},
},
"v1: valid rules with wildcard host": {
groupVersion: &networkingv1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) { tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}} ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
ingress.Spec.Rules = []networking.IngressRule{{ ingress.Spec.Rules = []networking.IngressRule{{
@@ -993,8 +961,7 @@ func TestValidateIngressCreate(t *testing.T) {
}} }}
}, },
}, },
"v1: invalid rules with wildcard host": { "invalid rules with wildcard host": {
groupVersion: &networkingv1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) { tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}} ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
ingress.Spec.Rules = []networking.IngressRule{{ ingress.Spec.Rules = []networking.IngressRule{{
@@ -1012,53 +979,13 @@ func TestValidateIngressCreate(t *testing.T) {
}, },
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("http").Child("paths").Index(0).Child("path"), "foo", `must be an absolute path`)}, expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("http").Child("paths").Index(0).Child("path"), "foo", `must be an absolute path`)},
}, },
"v1beta1: valid rules with wildcard host": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
ingress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "/foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
},
},
"v1beta1: invalid rules with wildcard host": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
ingress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
},
},
} }
for name, testCase := range testCases { for name, testCase := range testCases {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
newIngress := baseIngress.DeepCopy() newIngress := baseIngress.DeepCopy()
testCase.tweakIngress(newIngress) testCase.tweakIngress(newIngress)
gv := testCase.groupVersion errs := ValidateIngressCreate(newIngress)
if gv == nil {
gv = &networkingv1.SchemeGroupVersion
}
errs := ValidateIngressCreate(newIngress, *gv)
if len(errs) != len(testCase.expectedErrs) { if len(errs) != len(testCase.expectedErrs) {
t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs)
} }
@@ -1101,7 +1028,6 @@ func TestValidateIngressUpdate(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
gv schema.GroupVersion
tweakIngresses func(newIngress, oldIngress *networking.Ingress) tweakIngresses func(newIngress, oldIngress *networking.Ingress)
expectedErrs field.ErrorList expectedErrs field.ErrorList
}{ }{
@@ -1385,37 +1311,20 @@ func TestValidateIngressUpdate(t *testing.T) {
}, },
expectedErrs: field.ErrorList{}, expectedErrs: field.ErrorList{},
}, },
"v1: change valid secret -> invalid secret": { "change valid secret -> invalid secret": {
gv: networkingv1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}} oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}} newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}}
}, },
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("tls").Index(0).Child("secretName"), "invalid name", `a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`)}, expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("tls").Index(0).Child("secretName"), "invalid name", `a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')`)},
}, },
"v1: change invalid secret -> invalid secret": { "change invalid secret -> invalid secret": {
gv: networkingv1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 1"}} oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 1"}}
newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 2"}} newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 2"}}
}, },
}, },
"v1beta1: change valid secret -> invalid secret": { "change valid rules with wildcard host -> invalid rules": {
gv: networkingv1beta1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name"}}
},
},
"v1beta1: change invalid secret -> invalid secret": {
gv: networkingv1beta1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 1"}}
newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 2"}}
},
},
"v1: change valid rules with wildcard host -> invalid rules": {
gv: networkingv1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}} oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
oldIngress.Spec.Rules = []networking.IngressRule{{ oldIngress.Spec.Rules = []networking.IngressRule{{
@@ -1446,70 +1355,7 @@ func TestValidateIngressUpdate(t *testing.T) {
}, },
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("http").Child("paths").Index(0).Child("path"), "foo", `must be an absolute path`)}, expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("http").Child("paths").Index(0).Child("path"), "foo", `must be an absolute path`)},
}, },
"v1: change invalid rules with wildcard host -> invalid rules": { "change invalid rules with wildcard host -> invalid rules": {
gv: networkingv1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
oldIngress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
newIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
newIngress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "bar",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
},
},
"v1beta1: change valid rules with wildcard host -> invalid rules": {
gv: networkingv1beta1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
oldIngress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "/foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
newIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
newIngress.Spec.Rules = []networking.IngressRule{{
Host: "*.foo.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{{
Path: "foo",
PathType: &exactPathType,
Backend: defaultBackend,
}},
},
},
}}
},
},
"v1beta1: change invalid rules with wildcard host -> invalid rules": {
gv: networkingv1beta1.SchemeGroupVersion,
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) { tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}} oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
oldIngress.Spec.Rules = []networking.IngressRule{{ oldIngress.Spec.Rules = []networking.IngressRule{{
@@ -1547,11 +1393,7 @@ func TestValidateIngressUpdate(t *testing.T) {
oldIngress := baseIngress.DeepCopy() oldIngress := baseIngress.DeepCopy()
testCase.tweakIngresses(newIngress, oldIngress) testCase.tweakIngresses(newIngress, oldIngress)
gv := testCase.gv errs := ValidateIngressUpdate(newIngress, oldIngress)
if gv.Empty() {
gv = networkingv1beta1.SchemeGroupVersion
}
errs := ValidateIngressUpdate(newIngress, oldIngress, gv)
if len(errs) != len(testCase.expectedErrs) { if len(errs) != len(testCase.expectedErrs) {
t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs) t.Fatalf("Expected %d errors, got %d (%+v)", len(testCase.expectedErrs), len(errs), errs)
@@ -1861,7 +1703,7 @@ func TestValidateIngressTLS(t *testing.T) {
errorCases[badWildcardTLSErr] = badWildcardTLS errorCases[badWildcardTLSErr] = badWildcardTLS
for k, v := range errorCases { for k, v := range errorCases {
errs := validateIngress(&v, IngressValidationOptions{}, networkingv1beta1.SchemeGroupVersion) errs := validateIngress(&v, IngressValidationOptions{})
if len(errs) == 0 { if len(errs) == 0 {
t.Errorf("expected failure for %q", k) t.Errorf("expected failure for %q", k)
} else { } else {
@@ -1885,7 +1727,7 @@ func TestValidateIngressTLS(t *testing.T) {
} }
validCases[fmt.Sprintf("spec.tls[0].hosts: Valid value: '%v'", wildHost)] = goodWildcardTLS validCases[fmt.Sprintf("spec.tls[0].hosts: Valid value: '%v'", wildHost)] = goodWildcardTLS
for k, v := range validCases { for k, v := range validCases {
errs := validateIngress(&v, IngressValidationOptions{}, networkingv1beta1.SchemeGroupVersion) errs := validateIngress(&v, IngressValidationOptions{})
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("expected success for %q", k) t.Errorf("expected success for %q", k)
} }
@@ -1946,7 +1788,7 @@ func TestValidateEmptyIngressTLS(t *testing.T) {
} }
validCases[fmt.Sprintf("spec.tls[0]: Valid value: %v", goodEmptyHosts.Spec.TLS[0])] = goodEmptyHosts validCases[fmt.Sprintf("spec.tls[0]: Valid value: %v", goodEmptyHosts.Spec.TLS[0])] = goodEmptyHosts
for k, v := range validCases { for k, v := range validCases {
errs := validateIngress(&v, IngressValidationOptions{}, networkingv1beta1.SchemeGroupVersion) errs := validateIngress(&v, IngressValidationOptions{})
if len(errs) != 0 { if len(errs) != 0 {
t.Errorf("expected success for %q", k) t.Errorf("expected success for %q", k)
} }

View File

@@ -26,42 +26,31 @@ import (
"time" "time"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1" admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
apiserverinternalv1alpha1 "k8s.io/api/apiserverinternal/v1alpha1" apiserverinternalv1alpha1 "k8s.io/api/apiserverinternal/v1alpha1"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
authenticationv1 "k8s.io/api/authentication/v1" authenticationv1 "k8s.io/api/authentication/v1"
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
authorizationapiv1 "k8s.io/api/authorization/v1" authorizationapiv1 "k8s.io/api/authorization/v1"
authorizationapiv1beta1 "k8s.io/api/authorization/v1beta1"
autoscalingapiv1 "k8s.io/api/autoscaling/v1" autoscalingapiv1 "k8s.io/api/autoscaling/v1"
autoscalingapiv2beta1 "k8s.io/api/autoscaling/v2beta1" autoscalingapiv2beta1 "k8s.io/api/autoscaling/v2beta1"
autoscalingapiv2beta2 "k8s.io/api/autoscaling/v2beta2" autoscalingapiv2beta2 "k8s.io/api/autoscaling/v2beta2"
batchapiv1 "k8s.io/api/batch/v1" batchapiv1 "k8s.io/api/batch/v1"
batchapiv1beta1 "k8s.io/api/batch/v1beta1" batchapiv1beta1 "k8s.io/api/batch/v1beta1"
certificatesapiv1 "k8s.io/api/certificates/v1" certificatesapiv1 "k8s.io/api/certificates/v1"
certificatesapiv1beta1 "k8s.io/api/certificates/v1beta1"
coordinationapiv1 "k8s.io/api/coordination/v1" coordinationapiv1 "k8s.io/api/coordination/v1"
coordinationapiv1beta1 "k8s.io/api/coordination/v1beta1"
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1" discoveryv1 "k8s.io/api/discovery/v1"
discoveryv1beta1 "k8s.io/api/discovery/v1beta1" discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
eventsv1 "k8s.io/api/events/v1" eventsv1 "k8s.io/api/events/v1"
eventsv1beta1 "k8s.io/api/events/v1beta1" eventsv1beta1 "k8s.io/api/events/v1beta1"
extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1"
flowcontrolv1alpha1 "k8s.io/api/flowcontrol/v1alpha1" flowcontrolv1alpha1 "k8s.io/api/flowcontrol/v1alpha1"
networkingapiv1 "k8s.io/api/networking/v1" networkingapiv1 "k8s.io/api/networking/v1"
networkingapiv1beta1 "k8s.io/api/networking/v1beta1"
nodev1 "k8s.io/api/node/v1" nodev1 "k8s.io/api/node/v1"
nodev1alpha1 "k8s.io/api/node/v1alpha1" nodev1alpha1 "k8s.io/api/node/v1alpha1"
nodev1beta1 "k8s.io/api/node/v1beta1" nodev1beta1 "k8s.io/api/node/v1beta1"
policyapiv1 "k8s.io/api/policy/v1" policyapiv1 "k8s.io/api/policy/v1"
policyapiv1beta1 "k8s.io/api/policy/v1beta1" policyapiv1beta1 "k8s.io/api/policy/v1beta1"
rbacv1 "k8s.io/api/rbac/v1" rbacv1 "k8s.io/api/rbac/v1"
rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
schedulingapiv1 "k8s.io/api/scheduling/v1" schedulingapiv1 "k8s.io/api/scheduling/v1"
schedulingv1alpha1 "k8s.io/api/scheduling/v1alpha1"
schedulingapiv1beta1 "k8s.io/api/scheduling/v1beta1"
storageapiv1 "k8s.io/api/storage/v1" storageapiv1 "k8s.io/api/storage/v1"
storageapiv1alpha1 "k8s.io/api/storage/v1alpha1" storageapiv1alpha1 "k8s.io/api/storage/v1alpha1"
storageapiv1beta1 "k8s.io/api/storage/v1beta1" storageapiv1beta1 "k8s.io/api/storage/v1beta1"
@@ -108,7 +97,6 @@ import (
corerest "k8s.io/kubernetes/pkg/registry/core/rest" corerest "k8s.io/kubernetes/pkg/registry/core/rest"
discoveryrest "k8s.io/kubernetes/pkg/registry/discovery/rest" discoveryrest "k8s.io/kubernetes/pkg/registry/discovery/rest"
eventsrest "k8s.io/kubernetes/pkg/registry/events/rest" eventsrest "k8s.io/kubernetes/pkg/registry/events/rest"
extensionsrest "k8s.io/kubernetes/pkg/registry/extensions/rest"
flowcontrolrest "k8s.io/kubernetes/pkg/registry/flowcontrol/rest" flowcontrolrest "k8s.io/kubernetes/pkg/registry/flowcontrol/rest"
networkingrest "k8s.io/kubernetes/pkg/registry/networking/rest" networkingrest "k8s.io/kubernetes/pkg/registry/networking/rest"
noderest "k8s.io/kubernetes/pkg/registry/node/rest" noderest "k8s.io/kubernetes/pkg/registry/node/rest"
@@ -428,7 +416,6 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
certificatesrest.RESTStorageProvider{}, certificatesrest.RESTStorageProvider{},
coordinationrest.RESTStorageProvider{}, coordinationrest.RESTStorageProvider{},
discoveryrest.StorageProvider{}, discoveryrest.StorageProvider{},
extensionsrest.RESTStorageProvider{},
networkingrest.RESTStorageProvider{}, networkingrest.RESTStorageProvider{},
noderest.RESTStorageProvider{}, noderest.RESTStorageProvider{},
policyrest.RESTStorageProvider{}, policyrest.RESTStorageProvider{},
@@ -643,51 +630,36 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig {
// NOTE: GroupVersions listed here will be enabled by default. Don't put alpha versions in the list. // NOTE: GroupVersions listed here will be enabled by default. Don't put alpha versions in the list.
ret.EnableVersions( ret.EnableVersions(
admissionregistrationv1.SchemeGroupVersion, admissionregistrationv1.SchemeGroupVersion,
admissionregistrationv1beta1.SchemeGroupVersion,
apiv1.SchemeGroupVersion, apiv1.SchemeGroupVersion,
appsv1.SchemeGroupVersion, appsv1.SchemeGroupVersion,
authenticationv1.SchemeGroupVersion, authenticationv1.SchemeGroupVersion,
authenticationv1beta1.SchemeGroupVersion,
authorizationapiv1.SchemeGroupVersion, authorizationapiv1.SchemeGroupVersion,
authorizationapiv1beta1.SchemeGroupVersion,
autoscalingapiv1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion,
autoscalingapiv2beta1.SchemeGroupVersion, autoscalingapiv2beta1.SchemeGroupVersion,
autoscalingapiv2beta2.SchemeGroupVersion, autoscalingapiv2beta2.SchemeGroupVersion,
batchapiv1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion,
batchapiv1beta1.SchemeGroupVersion, batchapiv1beta1.SchemeGroupVersion,
certificatesapiv1.SchemeGroupVersion, certificatesapiv1.SchemeGroupVersion,
certificatesapiv1beta1.SchemeGroupVersion,
coordinationapiv1.SchemeGroupVersion, coordinationapiv1.SchemeGroupVersion,
coordinationapiv1beta1.SchemeGroupVersion,
discoveryv1.SchemeGroupVersion, discoveryv1.SchemeGroupVersion,
discoveryv1beta1.SchemeGroupVersion, discoveryv1beta1.SchemeGroupVersion,
eventsv1.SchemeGroupVersion, eventsv1.SchemeGroupVersion,
eventsv1beta1.SchemeGroupVersion, eventsv1beta1.SchemeGroupVersion,
extensionsapiv1beta1.SchemeGroupVersion,
networkingapiv1.SchemeGroupVersion, networkingapiv1.SchemeGroupVersion,
networkingapiv1beta1.SchemeGroupVersion,
nodev1.SchemeGroupVersion, nodev1.SchemeGroupVersion,
nodev1beta1.SchemeGroupVersion, nodev1beta1.SchemeGroupVersion,
policyapiv1.SchemeGroupVersion, policyapiv1.SchemeGroupVersion,
policyapiv1beta1.SchemeGroupVersion, policyapiv1beta1.SchemeGroupVersion,
rbacv1.SchemeGroupVersion, rbacv1.SchemeGroupVersion,
rbacv1beta1.SchemeGroupVersion,
storageapiv1.SchemeGroupVersion, storageapiv1.SchemeGroupVersion,
storageapiv1beta1.SchemeGroupVersion, storageapiv1beta1.SchemeGroupVersion,
schedulingapiv1beta1.SchemeGroupVersion,
schedulingapiv1.SchemeGroupVersion, schedulingapiv1.SchemeGroupVersion,
flowcontrolv1beta1.SchemeGroupVersion, flowcontrolv1beta1.SchemeGroupVersion,
) )
// enable non-deprecated beta resources in extensions/v1beta1 explicitly so we have a full list of what's possible to serve
ret.EnableResources(
extensionsapiv1beta1.SchemeGroupVersion.WithResource("ingresses"),
)
// disable alpha versions explicitly so we have a full list of what's possible to serve // disable alpha versions explicitly so we have a full list of what's possible to serve
ret.DisableVersions( ret.DisableVersions(
apiserverinternalv1alpha1.SchemeGroupVersion, apiserverinternalv1alpha1.SchemeGroupVersion,
nodev1alpha1.SchemeGroupVersion, nodev1alpha1.SchemeGroupVersion,
rbacv1alpha1.SchemeGroupVersion,
schedulingv1alpha1.SchemeGroupVersion,
storageapiv1alpha1.SchemeGroupVersion, storageapiv1alpha1.SchemeGroupVersion,
flowcontrolv1alpha1.SchemeGroupVersion, flowcontrolv1alpha1.SchemeGroupVersion,
) )

View File

@@ -31,12 +31,6 @@ var NoStorageVersionHash = sets.NewString(
"authorization.k8s.io/v1/selfsubjectaccessreviews", "authorization.k8s.io/v1/selfsubjectaccessreviews",
"authorization.k8s.io/v1/selfsubjectrulesreviews", "authorization.k8s.io/v1/selfsubjectrulesreviews",
"authorization.k8s.io/v1/subjectaccessreviews", "authorization.k8s.io/v1/subjectaccessreviews",
"authentication.k8s.io/v1beta1/tokenreviews",
"authorization.k8s.io/v1beta1/localsubjectaccessreviews",
"authorization.k8s.io/v1beta1/selfsubjectaccessreviews",
"authorization.k8s.io/v1beta1/selfsubjectrulesreviews",
"authorization.k8s.io/v1beta1/subjectaccessreviews",
"extensions/v1beta1/replicationcontrollers",
) )
// GVRToStorageVersionHash shouldn't change unless we intentionally change the // GVRToStorageVersionHash shouldn't change unless we intentionally change the
@@ -63,53 +57,37 @@ var GVRToStorageVersionHash = map[string]string{
"batch/v1/jobs": "mudhfqk/qZY=", "batch/v1/jobs": "mudhfqk/qZY=",
"batch/v1/cronjobs": "h/JlFAZkyyY=", "batch/v1/cronjobs": "h/JlFAZkyyY=",
"batch/v1beta1/cronjobs": "h/JlFAZkyyY=", "batch/v1beta1/cronjobs": "h/JlFAZkyyY=",
"certificates.k8s.io/v1/certificatesigningrequests": "95fRKMXA+00=", "certificates.k8s.io/v1/certificatesigningrequests": "95fRKMXA+00=",
"certificates.k8s.io/v1beta1/certificatesigningrequests": "95fRKMXA+00=", "coordination.k8s.io/v1/leases": "gqkMMb/YqFM=",
"coordination.k8s.io/v1beta1/leases": "gqkMMb/YqFM=", "discovery.k8s.io/v1/endpointslices": "Nx3SIv6I0mE=",
"coordination.k8s.io/v1/leases": "gqkMMb/YqFM=", "discovery.k8s.io/v1beta1/endpointslices": "Nx3SIv6I0mE=",
"discovery.k8s.io/v1/endpointslices": "Nx3SIv6I0mE=", "networking.k8s.io/v1/networkpolicies": "YpfwF18m1G8=",
"discovery.k8s.io/v1beta1/endpointslices": "Nx3SIv6I0mE=", "networking.k8s.io/v1/ingresses": "ZOAfGflaKd0=",
"extensions/v1beta1/ingresses": "ZOAfGflaKd0=", "networking.k8s.io/v1/ingressclasses": "l/iqIbDgFyQ=",
"networking.k8s.io/v1/networkpolicies": "YpfwF18m1G8=", "node.k8s.io/v1/runtimeclasses": "WQTu1GL3T2Q=",
"networking.k8s.io/v1beta1/ingresses": "ZOAfGflaKd0=", "node.k8s.io/v1beta1/runtimeclasses": "WQTu1GL3T2Q=",
"networking.k8s.io/v1beta1/ingressclasses": "l/iqIbDgFyQ=", "policy/v1/poddisruptionbudgets": "6BGBu0kpHtk=",
"networking.k8s.io/v1/ingresses": "ZOAfGflaKd0=", "policy/v1beta1/poddisruptionbudgets": "6BGBu0kpHtk=",
"networking.k8s.io/v1/ingressclasses": "l/iqIbDgFyQ=", "policy/v1beta1/podsecuritypolicies": "khBLobUXkqA=",
"node.k8s.io/v1/runtimeclasses": "WQTu1GL3T2Q=", "rbac.authorization.k8s.io/v1/clusterrolebindings": "48tpQ8gZHFc=",
"node.k8s.io/v1beta1/runtimeclasses": "WQTu1GL3T2Q=", "rbac.authorization.k8s.io/v1/clusterroles": "bYE5ZWDrJ44=",
"policy/v1/poddisruptionbudgets": "6BGBu0kpHtk=", "rbac.authorization.k8s.io/v1/rolebindings": "eGsCzGH6b1g=",
"policy/v1beta1/poddisruptionbudgets": "6BGBu0kpHtk=", "rbac.authorization.k8s.io/v1/roles": "7FuwZcIIItM=",
"policy/v1beta1/podsecuritypolicies": "khBLobUXkqA=", "scheduling.k8s.io/v1/priorityclasses": "1QwjyaZjj3Y=",
"rbac.authorization.k8s.io/v1/clusterrolebindings": "48tpQ8gZHFc=", "storage.k8s.io/v1/csidrivers": "hL6j/rwBV5w=",
"rbac.authorization.k8s.io/v1/clusterroles": "bYE5ZWDrJ44=", "storage.k8s.io/v1/csinodes": "Pe62DkZtjuo=",
"rbac.authorization.k8s.io/v1/rolebindings": "eGsCzGH6b1g=", "storage.k8s.io/v1/storageclasses": "K+m6uJwbjGY=",
"rbac.authorization.k8s.io/v1/roles": "7FuwZcIIItM=", "storage.k8s.io/v1/volumeattachments": "vQAqD28V4AY=",
"rbac.authorization.k8s.io/v1beta1/clusterrolebindings": "48tpQ8gZHFc=", "storage.k8s.io/v1beta1/csistoragecapacities": "xeVl+2Ly1kE=",
"rbac.authorization.k8s.io/v1beta1/clusterroles": "bYE5ZWDrJ44=", "apps/v1/controllerrevisions": "85nkx63pcBU=",
"rbac.authorization.k8s.io/v1beta1/rolebindings": "eGsCzGH6b1g=", "apps/v1/daemonsets": "dd7pWHUlMKQ=",
"rbac.authorization.k8s.io/v1beta1/roles": "7FuwZcIIItM=", "apps/v1/deployments": "8aSe+NMegvE=",
"scheduling.k8s.io/v1beta1/priorityclasses": "1QwjyaZjj3Y=", "apps/v1/replicasets": "P1RzHs8/mWQ=",
"scheduling.k8s.io/v1/priorityclasses": "1QwjyaZjj3Y=", "apps/v1/statefulsets": "H+vl74LkKdo=",
"storage.k8s.io/v1/csidrivers": "hL6j/rwBV5w=", "admissionregistration.k8s.io/v1/mutatingwebhookconfigurations": "Sqi0GUgDaX0=",
"storage.k8s.io/v1/csinodes": "Pe62DkZtjuo=", "admissionregistration.k8s.io/v1/validatingwebhookconfigurations": "B0wHjQmsGNk=",
"storage.k8s.io/v1/storageclasses": "K+m6uJwbjGY=", "events.k8s.io/v1/events": "r2yiGXH7wu8=",
"storage.k8s.io/v1/volumeattachments": "vQAqD28V4AY=", "events.k8s.io/v1beta1/events": "r2yiGXH7wu8=",
"storage.k8s.io/v1beta1/csidrivers": "hL6j/rwBV5w=", "flowcontrol.apiserver.k8s.io/v1beta1/flowschemas": "9bSnTLYweJ0=",
"storage.k8s.io/v1beta1/csinodes": "Pe62DkZtjuo=", "flowcontrol.apiserver.k8s.io/v1beta1/prioritylevelconfigurations": "BFVwf8eYnsw=",
"storage.k8s.io/v1beta1/csistoragecapacities": "xeVl+2Ly1kE=",
"storage.k8s.io/v1beta1/storageclasses": "K+m6uJwbjGY=",
"storage.k8s.io/v1beta1/volumeattachments": "vQAqD28V4AY=",
"apps/v1/controllerrevisions": "85nkx63pcBU=",
"apps/v1/daemonsets": "dd7pWHUlMKQ=",
"apps/v1/deployments": "8aSe+NMegvE=",
"apps/v1/replicasets": "P1RzHs8/mWQ=",
"apps/v1/statefulsets": "H+vl74LkKdo=",
"admissionregistration.k8s.io/v1beta1/mutatingwebhookconfigurations": "Sqi0GUgDaX0=",
"admissionregistration.k8s.io/v1beta1/validatingwebhookconfigurations": "B0wHjQmsGNk=",
"admissionregistration.k8s.io/v1/mutatingwebhookconfigurations": "Sqi0GUgDaX0=",
"admissionregistration.k8s.io/v1/validatingwebhookconfigurations": "B0wHjQmsGNk=",
"events.k8s.io/v1/events": "r2yiGXH7wu8=",
"events.k8s.io/v1beta1/events": "r2yiGXH7wu8=",
"flowcontrol.apiserver.k8s.io/v1beta1/flowschemas": "9bSnTLYweJ0=",
"flowcontrol.apiserver.k8s.io/v1beta1/prioritylevelconfigurations": "BFVwf8eYnsw=",
} }

View File

@@ -21,9 +21,7 @@ import (
"reflect" "reflect"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration" "k8s.io/kubernetes/pkg/apis/admissionregistration"
@@ -70,13 +68,8 @@ func (mutatingWebhookConfigurationStrategy) PrepareForUpdate(ctx context.Context
// Validate validates a new mutatingWebhookConfiguration. // Validate validates a new mutatingWebhookConfiguration.
func (mutatingWebhookConfigurationStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { func (mutatingWebhookConfigurationStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
var groupVersion schema.GroupVersion
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
ic := obj.(*admissionregistration.MutatingWebhookConfiguration) ic := obj.(*admissionregistration.MutatingWebhookConfiguration)
return validation.ValidateMutatingWebhookConfiguration(ic, groupVersion) return validation.ValidateMutatingWebhookConfiguration(ic)
} }
// Canonicalize normalizes the object after validation. // Canonicalize normalizes the object after validation.
@@ -90,12 +83,7 @@ func (mutatingWebhookConfigurationStrategy) AllowCreateOnUpdate() bool {
// ValidateUpdate is the default update validation for an end user. // ValidateUpdate is the default update validation for an end user.
func (mutatingWebhookConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (mutatingWebhookConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
var groupVersion schema.GroupVersion return validation.ValidateMutatingWebhookConfigurationUpdate(obj.(*admissionregistration.MutatingWebhookConfiguration), old.(*admissionregistration.MutatingWebhookConfiguration))
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
return validation.ValidateMutatingWebhookConfigurationUpdate(obj.(*admissionregistration.MutatingWebhookConfiguration), old.(*admissionregistration.MutatingWebhookConfiguration), groupVersion)
} }
// WarningsOnUpdate returns warnings for the given update. // WarningsOnUpdate returns warnings for the given update.

View File

@@ -18,7 +18,6 @@ package rest
import ( import (
admissionregistrationv1 "k8s.io/api/admissionregistration/v1" admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
@@ -36,13 +35,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities. // If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo // TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.VersionEnabled(admissionregistrationv1beta1.SchemeGroupVersion) {
if storageMap, err := p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
} else {
apiGroupInfo.VersionedResourcesStorageMap[admissionregistrationv1beta1.SchemeGroupVersion.Version] = storageMap
}
}
if apiResourceConfigSource.VersionEnabled(admissionregistrationv1.SchemeGroupVersion) { if apiResourceConfigSource.VersionEnabled(admissionregistrationv1.SchemeGroupVersion) {
if storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil { if storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err return genericapiserver.APIGroupInfo{}, false, err
@@ -53,25 +45,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
return apiGroupInfo, true, nil return apiGroupInfo, true, nil
} }
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
// validatingwebhookconfigurations
validatingStorage, err := validatingwebhookconfigurationstorage.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage["validatingwebhookconfigurations"] = validatingStorage
// mutatingwebhookconfigurations
mutatingStorage, err := mutatingwebhookconfigurationstorage.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage["mutatingwebhookconfigurations"] = mutatingStorage
return storage, err
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) { func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
// validatingwebhookconfigurations // validatingwebhookconfigurations

View File

@@ -21,9 +21,7 @@ import (
"reflect" "reflect"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration" "k8s.io/kubernetes/pkg/apis/admissionregistration"
@@ -65,12 +63,7 @@ func (validatingWebhookConfigurationStrategy) PrepareForUpdate(ctx context.Conte
// Validate validates a new validatingWebhookConfiguration. // Validate validates a new validatingWebhookConfiguration.
func (validatingWebhookConfigurationStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { func (validatingWebhookConfigurationStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
var groupVersion schema.GroupVersion return validation.ValidateValidatingWebhookConfiguration(obj.(*admissionregistration.ValidatingWebhookConfiguration))
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
return validation.ValidateValidatingWebhookConfiguration(obj.(*admissionregistration.ValidatingWebhookConfiguration), groupVersion)
} }
// WarningsOnCreate returns warnings for the creation of the given object. // WarningsOnCreate returns warnings for the creation of the given object.
@@ -89,12 +82,7 @@ func (validatingWebhookConfigurationStrategy) AllowCreateOnUpdate() bool {
// ValidateUpdate is the default update validation for an end user. // ValidateUpdate is the default update validation for an end user.
func (validatingWebhookConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (validatingWebhookConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
var groupVersion schema.GroupVersion return validation.ValidateValidatingWebhookConfigurationUpdate(obj.(*admissionregistration.ValidatingWebhookConfiguration), old.(*admissionregistration.ValidatingWebhookConfiguration))
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
return validation.ValidateValidatingWebhookConfigurationUpdate(obj.(*admissionregistration.ValidatingWebhookConfiguration), old.(*admissionregistration.ValidatingWebhookConfiguration), groupVersion)
} }
// WarningsOnUpdate returns warnings for the given update. // WarningsOnUpdate returns warnings for the given update.

View File

@@ -19,7 +19,6 @@ package daemonset
import ( import (
"context" "context"
appsv1beta2 "k8s.io/api/apps/v1beta2"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
apivalidation "k8s.io/apimachinery/pkg/api/validation" apivalidation "k8s.io/apimachinery/pkg/api/validation"
@@ -48,20 +47,12 @@ type daemonSetStrategy struct {
// Strategy is the default logic that applies when creating and updating DaemonSet objects. // Strategy is the default logic that applies when creating and updating DaemonSet objects.
var Strategy = daemonSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} var Strategy = daemonSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns OrphanDependents for extensions/v1beta1 and apps/v1beta2 for backwards compatibility, // Make sure we correctly implement the interface.
// and DeleteDependents for all other versions. var _ = rest.GarbageCollectionDeleteStrategy(Strategy)
// DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions.
func (daemonSetStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy { func (daemonSetStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
var groupVersion schema.GroupVersion return rest.DeleteDependents
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
switch groupVersion {
case extensionsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
// for back compatibility
return rest.OrphanDependents
default:
return rest.DeleteDependents
}
} }
// NamespaceScoped returns true because all DaemonSets need to be within a namespace. // NamespaceScoped returns true because all DaemonSets need to be within a namespace.

View File

@@ -25,7 +25,6 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing" featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/apps"
@@ -40,60 +39,6 @@ const (
namespace = "test-namespace" namespace = "test-namespace"
) )
func TestDaemonsetDefaultGarbageCollectionPolicy(t *testing.T) {
// Make sure we correctly implement the interface.
// Otherwise a typo could silently change the default.
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
tests := []struct {
requestInfo genericapirequest.RequestInfo
expectedGCPolicy rest.GarbageCollectionPolicy
isNilRequestInfo bool
}{
{
genericapirequest.RequestInfo{
APIGroup: "extensions",
APIVersion: "v1beta1",
Resource: "daemonsets",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1beta2",
Resource: "daemonsets",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1",
Resource: "daemonsets",
},
rest.DeleteDependents,
false,
},
{
expectedGCPolicy: rest.DeleteDependents,
isNilRequestInfo: true,
},
}
for _, test := range tests {
context := genericapirequest.NewContext()
if !test.isNilRequestInfo {
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
}
if got, want := gcds.DefaultGarbageCollectionPolicy(context), test.expectedGCPolicy; got != want {
t.Errorf("%s/%s: DefaultGarbageCollectionPolicy() = %#v, want %#v", test.requestInfo.APIGroup,
test.requestInfo.APIVersion, got, want)
}
}
}
func TestSelectorImmutability(t *testing.T) { func TestSelectorImmutability(t *testing.T) {
tests := []struct { tests := []struct {
requestInfo genericapirequest.RequestInfo requestInfo genericapirequest.RequestInfo

View File

@@ -20,7 +20,6 @@ import (
"context" "context"
appsv1beta1 "k8s.io/api/apps/v1beta1" appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
apivalidation "k8s.io/apimachinery/pkg/api/validation" apivalidation "k8s.io/apimachinery/pkg/api/validation"
@@ -47,20 +46,12 @@ type deploymentStrategy struct {
// objects via the REST API. // objects via the REST API.
var Strategy = deploymentStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} var Strategy = deploymentStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns OrphanDependents for extensions/v1beta1, apps/v1beta1, and apps/v1beta2 for backwards compatibility, // Make sure we correctly implement the interface.
// and DeleteDependents for all other versions. var _ = rest.GarbageCollectionDeleteStrategy(Strategy)
// DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions.
func (deploymentStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy { func (deploymentStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
var groupVersion schema.GroupVersion return rest.DeleteDependents
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
switch groupVersion {
case extensionsv1beta1.SchemeGroupVersion, appsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
// for back compatibility
return rest.OrphanDependents
default:
return rest.DeleteDependents
}
} }
// NamespaceScoped is true for deployment. // NamespaceScoped is true for deployment.

View File

@@ -26,7 +26,6 @@ import (
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/apps"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
@@ -186,69 +185,6 @@ func newDeploymentWithSelectorLabels(selectorLabels map[string]string) *apps.Dep
} }
} }
func TestDeploymentDefaultGarbageCollectionPolicy(t *testing.T) {
// Make sure we correctly implement the interface.
// Otherwise a typo could silently change the default.
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
tests := []struct {
requestInfo genericapirequest.RequestInfo
expectedGCPolicy rest.GarbageCollectionPolicy
isNilRequestInfo bool
}{
{
genericapirequest.RequestInfo{
APIGroup: "extensions",
APIVersion: "v1beta1",
Resource: "deployments",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1beta1",
Resource: "deployments",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1beta2",
Resource: "deployments",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1",
Resource: "deployments",
},
rest.DeleteDependents,
false,
},
{
expectedGCPolicy: rest.DeleteDependents,
isNilRequestInfo: true,
},
}
for _, test := range tests {
context := genericapirequest.NewContext()
if !test.isNilRequestInfo {
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
}
if got, want := gcds.DefaultGarbageCollectionPolicy(context), test.expectedGCPolicy; got != want {
t.Errorf("%s/%s: DefaultGarbageCollectionPolicy() = %#v, want %#v", test.requestInfo.APIGroup,
test.requestInfo.APIVersion, got, want)
}
}
}
func newDeploymentWithHugePageValue(reousreceName api.ResourceName, value resource.Quantity) *apps.Deployment { func newDeploymentWithHugePageValue(reousreceName api.ResourceName, value resource.Quantity) *apps.Deployment {
return &apps.Deployment{ return &apps.Deployment{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{

View File

@@ -23,7 +23,6 @@ import (
"fmt" "fmt"
"strconv" "strconv"
appsv1beta2 "k8s.io/api/apps/v1beta2"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
apivalidation "k8s.io/apimachinery/pkg/api/validation" apivalidation "k8s.io/apimachinery/pkg/api/validation"
@@ -53,20 +52,12 @@ type rsStrategy struct {
// Strategy is the default logic that applies when creating and updating ReplicaSet objects. // Strategy is the default logic that applies when creating and updating ReplicaSet objects.
var Strategy = rsStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} var Strategy = rsStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns OrphanDependents for extensions/v1beta1 and apps/v1beta2 for backwards compatibility, // Make sure we correctly implement the interface.
// and DeleteDependents for all other versions. var _ = rest.GarbageCollectionDeleteStrategy(Strategy)
// DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions.
func (rsStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy { func (rsStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
var groupVersion schema.GroupVersion return rest.DeleteDependents
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
switch groupVersion {
case extensionsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
// for back compatibility
return rest.OrphanDependents
default:
return rest.DeleteDependents
}
} }
// NamespaceScoped returns true because all ReplicaSets need to be within a namespace. // NamespaceScoped returns true because all ReplicaSets need to be within a namespace.

View File

@@ -23,7 +23,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/apps"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
) )
@@ -228,57 +227,3 @@ func newReplicaSetWithSelectorLabels(selectorLabels map[string]string) *apps.Rep
}, },
} }
} }
func TestReplicasetDefaultGarbageCollectionPolicy(t *testing.T) {
// Make sure we correctly implement the interface.
// Otherwise a typo could silently change the default.
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
tests := []struct {
requestInfo genericapirequest.RequestInfo
expectedGCPolicy rest.GarbageCollectionPolicy
isNilRequestInfo bool
}{
{
genericapirequest.RequestInfo{
APIGroup: "extensions",
APIVersion: "v1beta1",
Resource: "replicasets",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1beta2",
Resource: "replicasets",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1",
Resource: "replicasets",
},
rest.DeleteDependents,
false,
},
{
expectedGCPolicy: rest.DeleteDependents,
isNilRequestInfo: true,
},
}
for _, test := range tests {
context := genericapirequest.NewContext()
if !test.isNilRequestInfo {
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
}
if got, want := gcds.DefaultGarbageCollectionPolicy(context), test.expectedGCPolicy; got != want {
t.Errorf("%s/%s: DefaultGarbageCollectionPolicy() = %#v, want %#v", test.requestInfo.APIGroup,
test.requestInfo.APIVersion, got, want)
}
}
}

View File

@@ -21,13 +21,9 @@ import (
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/features"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
@@ -46,20 +42,12 @@ type statefulSetStrategy struct {
// Strategy is the default logic that applies when creating and updating Replication StatefulSet objects. // Strategy is the default logic that applies when creating and updating Replication StatefulSet objects.
var Strategy = statefulSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator} var Strategy = statefulSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns OrphanDependents for apps/v1beta1 and apps/v1beta2 for backwards compatibility, // Make sure we correctly implement the interface.
// and DeleteDependents for all other versions. var _ = rest.GarbageCollectionDeleteStrategy(Strategy)
// DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions.
func (statefulSetStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy { func (statefulSetStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
var groupVersion schema.GroupVersion return rest.DeleteDependents
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
switch groupVersion {
case appsv1beta1.SchemeGroupVersion, appsv1beta2.SchemeGroupVersion:
// for back compatibility
return rest.OrphanDependents
default:
return rest.DeleteDependents
}
} }
// NamespaceScoped returns true because all StatefulSet' need to be within a namespace. // NamespaceScoped returns true because all StatefulSet' need to be within a namespace.

View File

@@ -23,7 +23,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing" featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/apis/apps" "k8s.io/kubernetes/pkg/apis/apps"
@@ -194,60 +193,6 @@ func TestStatefulSetStrategy(t *testing.T) {
} }
} }
func TestStatefulsetDefaultGarbageCollectionPolicy(t *testing.T) {
// Make sure we correctly implement the interface.
// Otherwise a typo could silently change the default.
var gcds rest.GarbageCollectionDeleteStrategy = Strategy
tests := []struct {
requestInfo genericapirequest.RequestInfo
expectedGCPolicy rest.GarbageCollectionPolicy
isNilRequestInfo bool
}{
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1beta1",
Resource: "statefulsets",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1beta2",
Resource: "statefulsets",
},
rest.OrphanDependents,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "apps",
APIVersion: "v1",
Resource: "statefulsets",
},
rest.DeleteDependents,
false,
},
{
expectedGCPolicy: rest.DeleteDependents,
isNilRequestInfo: true,
},
}
for _, test := range tests {
context := genericapirequest.NewContext()
if !test.isNilRequestInfo {
context = genericapirequest.WithRequestInfo(context, &test.requestInfo)
}
if got, want := gcds.DefaultGarbageCollectionPolicy(context), test.expectedGCPolicy; got != want {
t.Errorf("%s/%s: DefaultGarbageCollectionPolicy() = %#v, want %#v", test.requestInfo.APIGroup,
test.requestInfo.APIVersion, got, want)
}
}
}
func TestStatefulSetStatusStrategy(t *testing.T) { func TestStatefulSetStatusStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext() ctx := genericapirequest.NewDefaultContext()
if !StatusStrategy.NamespaceScoped() { if !StatusStrategy.NamespaceScoped() {

View File

@@ -18,7 +18,6 @@ package rest
import ( import (
authenticationv1 "k8s.io/api/authentication/v1" authenticationv1 "k8s.io/api/authentication/v1"
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
"k8s.io/apiserver/pkg/authentication/authenticator" "k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
@@ -44,9 +43,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities. // If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo // TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.VersionEnabled(authenticationv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authenticationv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
}
if apiResourceConfigSource.VersionEnabled(authenticationv1.SchemeGroupVersion) { if apiResourceConfigSource.VersionEnabled(authenticationv1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authenticationv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter) apiGroupInfo.VersionedResourcesStorageMap[authenticationv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
} }
@@ -54,15 +50,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
return apiGroupInfo, true, nil return apiGroupInfo, true, nil
} }
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
storage := map[string]rest.Storage{}
// tokenreviews
tokenReviewStorage := tokenreview.NewREST(p.Authenticator, p.APIAudiences)
storage["tokenreviews"] = tokenReviewStorage
return storage
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage { func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
// tokenreviews // tokenreviews

View File

@@ -18,7 +18,6 @@ package rest
import ( import (
authorizationv1 "k8s.io/api/authorization/v1" authorizationv1 "k8s.io/api/authorization/v1"
authorizationv1beta1 "k8s.io/api/authorization/v1beta1"
"k8s.io/apiserver/pkg/authorization/authorizer" "k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
@@ -46,10 +45,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities. // If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo // TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.VersionEnabled(authorizationv1beta1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authorizationv1beta1.SchemeGroupVersion.Version] = p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
}
if apiResourceConfigSource.VersionEnabled(authorizationv1.SchemeGroupVersion) { if apiResourceConfigSource.VersionEnabled(authorizationv1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[authorizationv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter) apiGroupInfo.VersionedResourcesStorageMap[authorizationv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
} }
@@ -57,20 +52,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
return apiGroupInfo, true, nil return apiGroupInfo, true, nil
} }
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
storage := map[string]rest.Storage{}
// subjectaccessreviews
storage["subjectaccessreviews"] = subjectaccessreview.NewREST(p.Authorizer)
// selfsubjectaccessreviews
storage["selfsubjectaccessreviews"] = selfsubjectaccessreview.NewREST(p.Authorizer)
// localsubjectaccessreviews
storage["localsubjectaccessreviews"] = localsubjectaccessreview.NewREST(p.Authorizer)
// selfsubjectrulesreviews
storage["selfsubjectrulesreviews"] = selfsubjectrulesreview.NewREST(p.RuleResolver)
return storage
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage { func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) map[string]rest.Storage {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
// subjectaccessreviews // subjectaccessreviews

View File

@@ -20,12 +20,10 @@ import (
"context" "context"
"fmt" "fmt"
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
@@ -120,7 +118,7 @@ func (csrStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object
// Validate validates a new CSR. Validation must check for a correct signature. // Validate validates a new CSR. Validation must check for a correct signature.
func (csrStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { func (csrStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
csr := obj.(*certificates.CertificateSigningRequest) csr := obj.(*certificates.CertificateSigningRequest)
return validation.ValidateCertificateSigningRequestCreate(csr, requestGroupVersion(ctx)) return validation.ValidateCertificateSigningRequestCreate(csr)
} }
// WarningsOnCreate returns warnings for the creation of the given object. // WarningsOnCreate returns warnings for the creation of the given object.
@@ -133,7 +131,7 @@ func (csrStrategy) Canonicalize(obj runtime.Object) {}
func (csrStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (csrStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
oldCSR := old.(*certificates.CertificateSigningRequest) oldCSR := old.(*certificates.CertificateSigningRequest)
newCSR := obj.(*certificates.CertificateSigningRequest) newCSR := obj.(*certificates.CertificateSigningRequest)
return validation.ValidateCertificateSigningRequestUpdate(newCSR, oldCSR, requestGroupVersion(ctx)) return validation.ValidateCertificateSigningRequestUpdate(newCSR, oldCSR)
} }
// WarningsOnUpdate returns warnings for the given update. // WarningsOnUpdate returns warnings for the given update.
@@ -181,20 +179,11 @@ func (csrStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.
// Updating /status should not modify spec // Updating /status should not modify spec
newCSR.Spec = oldCSR.Spec newCSR.Spec = oldCSR.Spec
switch requestGroupVersion(ctx) { // Specifically preserve existing Approved/Denied conditions.
case certificatesv1beta1.SchemeGroupVersion: // Adding/removing Approved/Denied conditions will cause these to fail,
// Specifically preserve existing Approved/Denied conditions. // and the change in Approved/Denied conditions will produce a validation error
// If we cannot (if the status update attempted to add/remove Approved/Denied conditions), revert to old conditions for backwards compatibility. preserveConditionInstances(newCSR, oldCSR, certificates.CertificateApproved)
if !preserveConditionInstances(newCSR, oldCSR, certificates.CertificateApproved) || !preserveConditionInstances(newCSR, oldCSR, certificates.CertificateDenied) { preserveConditionInstances(newCSR, oldCSR, certificates.CertificateDenied)
newCSR.Status.Conditions = oldCSR.Status.Conditions
}
default:
// Specifically preserve existing Approved/Denied conditions.
// Adding/removing Approved/Denied conditions will cause these to fail,
// and the change in Approved/Denied conditions will produce a validation error
preserveConditionInstances(newCSR, oldCSR, certificates.CertificateApproved)
preserveConditionInstances(newCSR, oldCSR, certificates.CertificateDenied)
}
populateConditionTimestamps(newCSR, oldCSR) populateConditionTimestamps(newCSR, oldCSR)
} }
@@ -255,7 +244,7 @@ func populateConditionTimestamps(newCSR, oldCSR *certificates.CertificateSigning
} }
func (csrStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (csrStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateCertificateSigningRequestStatusUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest), requestGroupVersion(ctx)) return validation.ValidateCertificateSigningRequestStatusUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest))
} }
// WarningsOnUpdate returns warnings for the given update. // WarningsOnUpdate returns warnings for the given update.
@@ -307,7 +296,7 @@ func (csrApprovalStrategy) PrepareForUpdate(ctx context.Context, obj, old runtim
} }
func (csrApprovalStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (csrApprovalStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateCertificateSigningRequestApprovalUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest), requestGroupVersion(ctx)) return validation.ValidateCertificateSigningRequestApprovalUpdate(obj.(*certificates.CertificateSigningRequest), old.(*certificates.CertificateSigningRequest))
} }
// WarningsOnUpdate returns warnings for the given update. // WarningsOnUpdate returns warnings for the given update.
@@ -332,11 +321,3 @@ func SelectableFields(obj *certificates.CertificateSigningRequest) fields.Set {
} }
return generic.MergeFieldsSets(objectMetaFieldsSet, csrSpecificFieldsSet) return generic.MergeFieldsSets(objectMetaFieldsSet, csrSpecificFieldsSet)
} }
// requestGroupVersion returns the group/version associated with the given context, or a zero-value group/version
func requestGroupVersion(ctx context.Context) schema.GroupVersion {
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
return schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
return schema.GroupVersion{}
}

View File

@@ -174,19 +174,16 @@ func TestStatusUpdate(t *testing.T) {
}() }()
tests := []struct { tests := []struct {
name string name string
newObj *certapi.CertificateSigningRequest newObj *certapi.CertificateSigningRequest
oldObj *certapi.CertificateSigningRequest oldObj *certapi.CertificateSigningRequest
expectedObjs map[string]*certapi.CertificateSigningRequest expectedObj *certapi.CertificateSigningRequest
}{ }{
{ {
name: "no-op", name: "no-op",
newObj: &certapi.CertificateSigningRequest{}, newObj: &certapi.CertificateSigningRequest{},
oldObj: &certapi.CertificateSigningRequest{}, oldObj: &certapi.CertificateSigningRequest{},
expectedObjs: map[string]*certapi.CertificateSigningRequest{ expectedObj: &certapi.CertificateSigningRequest{},
"v1": {},
"v1beta1": {},
},
}, },
{ {
name: "adding failed condition to existing approved/denied conditions", name: "adding failed condition to existing approved/denied conditions",
@@ -211,29 +208,15 @@ func TestStatusUpdate(t *testing.T) {
}, },
}, },
}, },
expectedObjs: map[string]*certapi.CertificateSigningRequest{ expectedObj: &certapi.CertificateSigningRequest{
// preserve existing Approved/Denied conditions // preserve existing Approved/Denied conditions
"v1": { Status: certapi.CertificateSigningRequestStatus{
Status: certapi.CertificateSigningRequestStatus{ Conditions: []certapi.CertificateSigningRequestCondition{
Conditions: []certapi.CertificateSigningRequestCondition{ {Type: certapi.CertificateFailed, LastUpdateTime: now, LastTransitionTime: now},
{Type: certapi.CertificateFailed, LastUpdateTime: now, LastTransitionTime: now}, {Type: certapi.CertificateDenied, LastUpdateTime: now, LastTransitionTime: later, Reason: "because1"},
{Type: certapi.CertificateDenied, LastUpdateTime: now, LastTransitionTime: later, Reason: "because1"}, {Type: certapi.CertificateApproved, LastUpdateTime: now, LastTransitionTime: later, Reason: "because2"},
{Type: certapi.CertificateApproved, LastUpdateTime: now, LastTransitionTime: later, Reason: "because2"}, {Type: certapi.CertificateDenied, LastUpdateTime: later, LastTransitionTime: later, Reason: "because3"},
{Type: certapi.CertificateDenied, LastUpdateTime: later, LastTransitionTime: later, Reason: "because3"}, {Type: certapi.CertificateApproved, LastUpdateTime: later, LastTransitionTime: later, Reason: "because4"},
{Type: certapi.CertificateApproved, LastUpdateTime: later, LastTransitionTime: later, Reason: "because4"},
},
},
},
// preserve existing Approved/Denied conditions
"v1beta1": {
Status: certapi.CertificateSigningRequestStatus{
Conditions: []certapi.CertificateSigningRequestCondition{
{Type: certapi.CertificateFailed, LastUpdateTime: now, LastTransitionTime: now},
{Type: certapi.CertificateDenied, LastUpdateTime: now, LastTransitionTime: later, Reason: "because1"},
{Type: certapi.CertificateApproved, LastUpdateTime: now, LastTransitionTime: later, Reason: "because2"},
{Type: certapi.CertificateDenied, LastUpdateTime: later, LastTransitionTime: later, Reason: "because3"},
{Type: certapi.CertificateApproved, LastUpdateTime: later, LastTransitionTime: later, Reason: "because4"},
},
}, },
}, },
}, },
@@ -248,33 +231,24 @@ func TestStatusUpdate(t *testing.T) {
}, },
}, },
oldObj: &certapi.CertificateSigningRequest{}, oldObj: &certapi.CertificateSigningRequest{},
expectedObjs: map[string]*certapi.CertificateSigningRequest{ expectedObj: &certapi.CertificateSigningRequest{
// preserved submitted conditions if existing Approved/Denied conditions could not be copied over (will fail validation) // preserved submitted conditions if existing Approved/Denied conditions could not be copied over (will fail validation)
"v1": { Status: certapi.CertificateSigningRequestStatus{
Status: certapi.CertificateSigningRequestStatus{ Conditions: []certapi.CertificateSigningRequestCondition{
Conditions: []certapi.CertificateSigningRequestCondition{ {Type: certapi.CertificateApproved, LastUpdateTime: now, LastTransitionTime: now},
{Type: certapi.CertificateApproved, LastUpdateTime: now, LastTransitionTime: now},
},
}, },
}, },
// reset conditions to existing conditions if Approved/Denied conditions could not be copied over
"v1beta1": {
Status: certapi.CertificateSigningRequestStatus{},
},
}, },
}, },
} }
for _, tt := range tests { for _, tt := range tests {
for _, version := range []string{"v1", "v1beta1"} { t.Run(tt.name, func(t *testing.T) {
t.Run(tt.name+"_"+version, func(t *testing.T) { obj := tt.newObj.DeepCopy()
ctx := genericapirequest.WithRequestInfo(context.TODO(), &genericapirequest.RequestInfo{APIGroup: "certificates.k8s.io", APIVersion: version}) StatusStrategy.PrepareForUpdate(context.TODO(), obj, tt.oldObj.DeepCopy())
obj := tt.newObj.DeepCopy() if !reflect.DeepEqual(obj, tt.expectedObj) {
StatusStrategy.PrepareForUpdate(ctx, obj, tt.oldObj.DeepCopy()) t.Errorf("object diff: %s", diff.ObjectDiff(obj, tt.expectedObj))
if !reflect.DeepEqual(obj, tt.expectedObjs[version]) { }
t.Errorf("object diff: %s", diff.ObjectDiff(obj, tt.expectedObjs[version])) })
}
})
}
} }
} }

View File

@@ -18,7 +18,6 @@ package rest
import ( import (
certificatesapiv1 "k8s.io/api/certificates/v1" certificatesapiv1 "k8s.io/api/certificates/v1"
certificatesapiv1beta1 "k8s.io/api/certificates/v1beta1"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
@@ -35,14 +34,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities. // If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo // TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.VersionEnabled(certificatesapiv1beta1.SchemeGroupVersion) {
storageMap, err := p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter)
if err != nil {
return genericapiserver.APIGroupInfo{}, false, err
}
apiGroupInfo.VersionedResourcesStorageMap[certificatesapiv1beta1.SchemeGroupVersion.Version] = storageMap
}
if apiResourceConfigSource.VersionEnabled(certificatesapiv1.SchemeGroupVersion) { if apiResourceConfigSource.VersionEnabled(certificatesapiv1.SchemeGroupVersion) {
storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter) storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter)
if err != nil { if err != nil {
@@ -54,20 +45,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
return apiGroupInfo, true, nil return apiGroupInfo, true, nil
} }
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
// certificatesigningrequests
csrStorage, csrStatusStorage, csrApprovalStorage, err := certificatestore.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage["certificatesigningrequests"] = csrStorage
storage["certificatesigningrequests/status"] = csrStatusStorage
storage["certificatesigningrequests/approval"] = csrApprovalStorage
return storage, err
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) { func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
// certificatesigningrequests // certificatesigningrequests

View File

@@ -18,7 +18,6 @@ package rest
import ( import (
coordinationv1 "k8s.io/api/coordination/v1" coordinationv1 "k8s.io/api/coordination/v1"
coordinationv1beta1 "k8s.io/api/coordination/v1beta1"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
@@ -35,13 +34,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities. // If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo // TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.VersionEnabled(coordinationv1beta1.SchemeGroupVersion) {
if storageMap, err := p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
} else {
apiGroupInfo.VersionedResourcesStorageMap[coordinationv1beta1.SchemeGroupVersion.Version] = storageMap
}
}
if apiResourceConfigSource.VersionEnabled(coordinationv1.SchemeGroupVersion) { if apiResourceConfigSource.VersionEnabled(coordinationv1.SchemeGroupVersion) {
if storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil { if storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err return genericapiserver.APIGroupInfo{}, false, err
@@ -52,18 +44,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
return apiGroupInfo, true, nil return apiGroupInfo, true, nil
} }
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
// leases
leaseStorage, err := leasestorage.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage["leases"] = leaseStorage
return storage, err
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) { func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
// leases // leases

View File

@@ -1,5 +0,0 @@
# See the OWNERS docs at https://go.k8s.io/owners
reviewers:
- deads2k
- hongchaodeng

View File

@@ -1,66 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/extensions"
ingressstore "k8s.io/kubernetes/pkg/registry/networking/ingress/storage"
)
type RESTStorageProvider struct{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool, error) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(extensions.GroupName, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.VersionEnabled(extensionsapiv1beta1.SchemeGroupVersion) {
if storageMap, err := p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
} else {
apiGroupInfo.VersionedResourcesStorageMap[extensionsapiv1beta1.SchemeGroupVersion.Version] = storageMap
}
}
return apiGroupInfo, true, nil
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
// ingresses
if apiResourceConfigSource.ResourceEnabled(extensionsapiv1beta1.SchemeGroupVersion.WithResource("ingresses")) {
ingressStorage, ingressStatusStorage, err := ingressstore.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage["ingresses"] = ingressStorage
storage["ingresses/status"] = ingressStatusStorage
}
return storage, nil
}
func (p RESTStorageProvider) GroupName() string {
return extensions.GroupName
}

View File

@@ -21,9 +21,7 @@ import (
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/networking" "k8s.io/kubernetes/pkg/apis/networking"
@@ -90,12 +88,8 @@ func (ingressStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Ob
// Validate validates ingresses on create. // Validate validates ingresses on create.
func (ingressStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { func (ingressStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
var requestGV schema.GroupVersion
if requestInfo, ok := request.RequestInfoFrom(ctx); ok {
requestGV = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
ingress := obj.(*networking.Ingress) ingress := obj.(*networking.Ingress)
return validation.ValidateIngressCreate(ingress, requestGV) return validation.ValidateIngressCreate(ingress)
} }
// WarningsOnCreate returns warnings for the creation of the given object. // WarningsOnCreate returns warnings for the creation of the given object.
@@ -112,11 +106,7 @@ func (ingressStrategy) AllowCreateOnUpdate() bool {
// ValidateUpdate validates ingresses on update. // ValidateUpdate validates ingresses on update.
func (ingressStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (ingressStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
var requestGV schema.GroupVersion return validation.ValidateIngressUpdate(obj.(*networking.Ingress), old.(*networking.Ingress))
if requestInfo, ok := request.RequestInfoFrom(ctx); ok {
requestGV = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
return validation.ValidateIngressUpdate(obj.(*networking.Ingress), old.(*networking.Ingress), requestGV)
} }
// WarningsOnUpdate returns warnings for the given update. // WarningsOnUpdate returns warnings for the given update.

View File

@@ -18,7 +18,6 @@ package rest
import ( import (
networkingapiv1 "k8s.io/api/networking/v1" networkingapiv1 "k8s.io/api/networking/v1"
networkingapiv1beta1 "k8s.io/api/networking/v1beta1"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server" genericapiserver "k8s.io/apiserver/pkg/server"
@@ -45,14 +44,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
} }
} }
if apiResourceConfigSource.VersionEnabled(networkingapiv1beta1.SchemeGroupVersion) {
if storageMap, err := p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
} else {
apiGroupInfo.VersionedResourcesStorageMap[networkingapiv1beta1.SchemeGroupVersion.Version] = storageMap
}
}
return apiGroupInfo, true, nil return apiGroupInfo, true, nil
} }
@@ -83,26 +74,6 @@ func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.API
return storage, nil return storage, nil
} }
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
// ingresses
ingressStorage, ingressStatusStorage, err := ingressstore.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage["ingresses"] = ingressStorage
storage["ingresses/status"] = ingressStatusStorage
// ingressclasses
ingressClassStorage, err := ingressclassstore.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage["ingressclasses"] = ingressClassStorage
return storage, nil
}
func (p RESTStorageProvider) GroupName() string { func (p RESTStorageProvider) GroupName() string {
return networking.GroupName return networking.GroupName
} }

View File

@@ -24,8 +24,6 @@ import (
"k8s.io/klog/v2" "k8s.io/klog/v2"
rbacapiv1 "k8s.io/api/rbac/v1" rbacapiv1 "k8s.io/api/rbac/v1"
rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1"
rbacapiv1beta1 "k8s.io/api/rbac/v1beta1"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
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"
@@ -72,20 +70,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities. // If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo // TODO refactor the plumbing to provide the information in the APIGroupInfo
if apiResourceConfigSource.VersionEnabled(rbacapiv1alpha1.SchemeGroupVersion) {
if storageMap, err := p.storage(rbacapiv1alpha1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
} else {
apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1alpha1.SchemeGroupVersion.Version] = storageMap
}
}
if apiResourceConfigSource.VersionEnabled(rbacapiv1beta1.SchemeGroupVersion) {
if storageMap, err := p.storage(rbacapiv1beta1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
} else {
apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1beta1.SchemeGroupVersion.Version] = storageMap
}
}
if apiResourceConfigSource.VersionEnabled(rbacapiv1.SchemeGroupVersion) { if apiResourceConfigSource.VersionEnabled(rbacapiv1.SchemeGroupVersion) {
if storageMap, err := p.storage(rbacapiv1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter); err != nil { if storageMap, err := p.storage(rbacapiv1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err return genericapiserver.APIGroupInfo{}, false, err

View File

@@ -35,8 +35,6 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/scheduling" "k8s.io/kubernetes/pkg/apis/scheduling"
schedulingapiv1 "k8s.io/kubernetes/pkg/apis/scheduling/v1" schedulingapiv1 "k8s.io/kubernetes/pkg/apis/scheduling/v1"
schedulingapiv1alpha1 "k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1"
schedulingapiv1beta1 "k8s.io/kubernetes/pkg/apis/scheduling/v1beta1"
priorityclassstore "k8s.io/kubernetes/pkg/registry/scheduling/priorityclass/storage" priorityclassstore "k8s.io/kubernetes/pkg/registry/scheduling/priorityclass/storage"
) )
@@ -49,20 +47,6 @@ var _ genericapiserver.PostStartHookProvider = RESTStorageProvider{}
func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool, error) { func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (genericapiserver.APIGroupInfo, bool, error) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(scheduling.GroupName, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs) apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(scheduling.GroupName, legacyscheme.Scheme, legacyscheme.ParameterCodec, legacyscheme.Codecs)
if apiResourceConfigSource.VersionEnabled(schedulingapiv1alpha1.SchemeGroupVersion) {
if storage, err := p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
} else {
apiGroupInfo.VersionedResourcesStorageMap[schedulingapiv1alpha1.SchemeGroupVersion.Version] = storage
}
}
if apiResourceConfigSource.VersionEnabled(schedulingapiv1beta1.SchemeGroupVersion) {
if storage, err := p.v1beta1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
} else {
apiGroupInfo.VersionedResourcesStorageMap[schedulingapiv1beta1.SchemeGroupVersion.Version] = storage
}
}
if apiResourceConfigSource.VersionEnabled(schedulingapiv1.SchemeGroupVersion) { if apiResourceConfigSource.VersionEnabled(schedulingapiv1.SchemeGroupVersion) {
if storage, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil { if storage, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err return genericapiserver.APIGroupInfo{}, false, err
@@ -73,29 +57,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
return apiGroupInfo, true, nil return apiGroupInfo, true, nil
} }
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
// priorityclasses
if priorityClassStorage, err := priorityclassstore.NewREST(restOptionsGetter); err != nil {
return nil, err
} else {
storage["priorityclasses"] = priorityClassStorage
}
return storage, nil
}
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
// priorityclasses
if priorityClassStorage, err := priorityclassstore.NewREST(restOptionsGetter); err != nil {
return nil, err
} else {
storage["priorityclasses"] = priorityClassStorage
}
return storage, nil
}
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) { func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
// priorityclasses // priorityclasses

View File

@@ -68,12 +68,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) { func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
// volumeattachments
volumeAttachmentStorage, err := volumeattachmentstore.NewStorage(restOptionsGetter)
if err != nil {
return storage, err
}
storage["volumeattachments"] = volumeAttachmentStorage.VolumeAttachment
// register csistoragecapacities // register csistoragecapacities
csiStorageStorage, err := csistoragecapacitystore.NewStorage(restOptionsGetter) csiStorageStorage, err := csistoragecapacitystore.NewStorage(restOptionsGetter)
@@ -87,33 +81,6 @@ func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstora
func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) { func (p RESTStorageProvider) v1beta1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
// storageclasses
storageClassStorage, err := storageclassstore.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage["storageclasses"] = storageClassStorage
// volumeattachments
volumeAttachmentStorage, err := volumeattachmentstore.NewStorage(restOptionsGetter)
if err != nil {
return storage, err
}
storage["volumeattachments"] = volumeAttachmentStorage.VolumeAttachment
// register csinodes
csiNodeStorage, err := csinodestore.NewStorage(restOptionsGetter)
if err != nil {
return storage, err
}
storage["csinodes"] = csiNodeStorage.CSINode
// register csidrivers
csiDriverStorage, err := csidriverstore.NewStorage(restOptionsGetter)
if err != nil {
return storage, err
}
storage["csidrivers"] = csiDriverStorage.CSIDriver
// register csistoragecapacities // register csistoragecapacities
csiStorageStorage, err := csistoragecapacitystore.NewStorage(restOptionsGetter) csiStorageStorage, err := csistoragecapacitystore.NewStorage(restOptionsGetter)

View File

@@ -19,12 +19,9 @@ package volumeattachment
import ( import (
"context" "context"
storageapiv1beta1 "k8s.io/api/storage/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
@@ -62,20 +59,8 @@ func (volumeAttachmentStrategy) GetResetFields() map[fieldpath.APIVersion]*field
// ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation. // ResetBeforeCreate clears the Status field which is not allowed to be set by end users on creation.
func (volumeAttachmentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { func (volumeAttachmentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
var groupVersion schema.GroupVersion
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
volumeAttachment := obj.(*storage.VolumeAttachment) volumeAttachment := obj.(*storage.VolumeAttachment)
volumeAttachment.Status = storage.VolumeAttachmentStatus{}
switch groupVersion {
case storageapiv1beta1.SchemeGroupVersion:
// allow modification of status for v1beta1
default:
volumeAttachment.Status = storage.VolumeAttachmentStatus{}
}
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) { if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) {
volumeAttachment.Spec.Source.InlineVolumeSpec = nil volumeAttachment.Spec.Source.InlineVolumeSpec = nil
@@ -88,19 +73,8 @@ func (volumeAttachmentStrategy) Validate(ctx context.Context, obj runtime.Object
errs := validation.ValidateVolumeAttachment(volumeAttachment) errs := validation.ValidateVolumeAttachment(volumeAttachment)
var groupVersion schema.GroupVersion // tighten up validation of newly created v1 attachments
errs = append(errs, validation.ValidateVolumeAttachmentV1(volumeAttachment)...)
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
switch groupVersion {
case storageapiv1beta1.SchemeGroupVersion:
// no extra validation
default:
// tighten up validation of newly created v1 attachments
errs = append(errs, validation.ValidateVolumeAttachmentV1(volumeAttachment)...)
}
return errs return errs
} }
@@ -119,22 +93,11 @@ func (volumeAttachmentStrategy) AllowCreateOnUpdate() bool {
// PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a VolumeAttachment // PrepareForUpdate sets the Status fields which is not allowed to be set by an end user updating a VolumeAttachment
func (volumeAttachmentStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { func (volumeAttachmentStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
var groupVersion schema.GroupVersion
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
newVolumeAttachment := obj.(*storage.VolumeAttachment) newVolumeAttachment := obj.(*storage.VolumeAttachment)
oldVolumeAttachment := old.(*storage.VolumeAttachment) oldVolumeAttachment := old.(*storage.VolumeAttachment)
switch groupVersion { newVolumeAttachment.Status = oldVolumeAttachment.Status
case storageapiv1beta1.SchemeGroupVersion: // No need to increment Generation because we don't allow updates to spec
// allow modification of Status via main resource for v1beta1
default:
newVolumeAttachment.Status = oldVolumeAttachment.Status
// No need to increment Generation because we don't allow updates to spec
}
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && oldVolumeAttachment.Spec.Source.InlineVolumeSpec == nil { if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) && oldVolumeAttachment.Spec.Source.InlineVolumeSpec == nil {
newVolumeAttachment.Spec.Source.InlineVolumeSpec = nil newVolumeAttachment.Spec.Source.InlineVolumeSpec = nil

View File

@@ -17,13 +17,13 @@ limitations under the License.
package volumeattachment package volumeattachment
import ( import (
"context"
"testing" "testing"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing" featuregatetesting "k8s.io/component-base/featuregate/testing"
@@ -196,77 +196,22 @@ func TestVolumeAttachmentStatusStrategy(t *testing.T) {
} }
} }
func TestBetaAndV1StatusUpdate(t *testing.T) { func TestUpdatePreventsStatusWrite(t *testing.T) {
tests := []struct { va := getValidVolumeAttachment("valid-attachment")
requestInfo genericapirequest.RequestInfo newAttachment := va.DeepCopy()
newStatus bool newAttachment.Status.Attached = true
expectedStatus bool Strategy.PrepareForUpdate(context.TODO(), newAttachment, va)
}{ if newAttachment.Status.Attached {
{ t.Errorf("expected status to be %v got %v", false, newAttachment.Status.Attached)
genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1",
Resource: "volumeattachments",
},
true,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1beta1",
Resource: "volumeattachments",
},
true,
true,
},
} }
for _, test := range tests {
va := getValidVolumeAttachment("valid-attachment")
newAttachment := va.DeepCopy()
newAttachment.Status.Attached = test.newStatus
context := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &test.requestInfo)
Strategy.PrepareForUpdate(context, newAttachment, va)
if newAttachment.Status.Attached != test.expectedStatus {
t.Errorf("expected status to be %v got %v", test.expectedStatus, newAttachment.Status.Attached)
}
}
} }
func TestBetaAndV1StatusCreate(t *testing.T) { func TestCreatePreventsStatusWrite(t *testing.T) {
tests := []struct { va := getValidVolumeAttachment("valid-attachment")
requestInfo genericapirequest.RequestInfo va.Status.Attached = true
newStatus bool Strategy.PrepareForCreate(context.TODO(), va)
expectedStatus bool if va.Status.Attached {
}{ t.Errorf("expected status to be false got %v", va.Status.Attached)
{
genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1",
Resource: "volumeattachments",
},
true,
false,
},
{
genericapirequest.RequestInfo{
APIGroup: "storage.k8s.io",
APIVersion: "v1beta1",
Resource: "volumeattachments",
},
true,
true,
},
}
for _, test := range tests {
va := getValidVolumeAttachment("valid-attachment")
va.Status.Attached = test.newStatus
context := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &test.requestInfo)
Strategy.PrepareForCreate(context, va)
if va.Status.Attached != test.expectedStatus {
t.Errorf("expected status to be %v got %v", test.expectedStatus, va.Status.Attached)
}
} }
} }
@@ -276,14 +221,12 @@ func TestVolumeAttachmentValidation(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
volumeAttachment *storage.VolumeAttachment volumeAttachment *storage.VolumeAttachment
expectBetaError bool expectError bool
expectV1Error bool
}{ }{
{ {
"valid attachment", "valid attachment",
getValidVolumeAttachment("foo"), getValidVolumeAttachment("foo"),
false, false,
false,
}, },
{ {
"invalid PV name", "invalid PV name",
@@ -299,7 +242,6 @@ func TestVolumeAttachmentValidation(t *testing.T) {
NodeName: "valid-node", NodeName: "valid-node",
}, },
}, },
false,
true, true,
}, },
{ {
@@ -316,7 +258,6 @@ func TestVolumeAttachmentValidation(t *testing.T) {
NodeName: "valid-node", NodeName: "valid-node",
}, },
}, },
false,
true, true,
}, },
{ {
@@ -334,36 +275,17 @@ func TestVolumeAttachmentValidation(t *testing.T) {
}, },
}, },
true, true,
true,
}, },
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
err := Strategy.Validate(context.TODO(), test.volumeAttachment)
testValidation := func(va *storage.VolumeAttachment, apiVersion string) field.ErrorList { if len(err) > 0 && !test.expectError {
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewContext(), &genericapirequest.RequestInfo{ t.Errorf("Validation of object failed: %+v", err)
APIGroup: "storage.k8s.io",
APIVersion: apiVersion,
Resource: "volumeattachments",
})
return Strategy.Validate(ctx, va)
} }
if len(err) == 0 && test.expectError {
v1Err := testValidation(test.volumeAttachment, "v1") t.Errorf("Validation of object unexpectedly succeeded")
if len(v1Err) > 0 && !test.expectV1Error {
t.Errorf("Validation of v1 object failed: %+v", v1Err)
}
if len(v1Err) == 0 && test.expectV1Error {
t.Errorf("Validation of v1 object unexpectedly succeeded")
}
betaErr := testValidation(test.volumeAttachment, "v1beta1")
if len(betaErr) > 0 && !test.expectBetaError {
t.Errorf("Validation of v1beta1 object failed: %+v", betaErr)
}
if len(betaErr) == 0 && test.expectBetaError {
t.Errorf("Validation of v1beta1 object unexpectedly succeeded")
} }
}) })
} }

View File

@@ -27,7 +27,6 @@ import (
structuraldefaulting "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting" structuraldefaulting "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting"
apiequality "k8s.io/apimachinery/pkg/api/equality" apiequality "k8s.io/apimachinery/pkg/api/equality"
genericvalidation "k8s.io/apimachinery/pkg/api/validation" genericvalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation" utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
@@ -47,7 +46,7 @@ var (
) )
// ValidateCustomResourceDefinition statically validates // ValidateCustomResourceDefinition statically validates
func ValidateCustomResourceDefinition(obj *apiextensions.CustomResourceDefinition, requestGV schema.GroupVersion) field.ErrorList { func ValidateCustomResourceDefinition(obj *apiextensions.CustomResourceDefinition) field.ErrorList {
nameValidationFn := func(name string, prefix bool) []string { nameValidationFn := func(name string, prefix bool) []string {
ret := genericvalidation.NameIsDNSSubdomain(name, prefix) ret := genericvalidation.NameIsDNSSubdomain(name, prefix)
requiredName := obj.Spec.Names.Plural + "." + obj.Spec.Group requiredName := obj.Spec.Names.Plural + "." + obj.Spec.Group
@@ -57,15 +56,13 @@ func ValidateCustomResourceDefinition(obj *apiextensions.CustomResourceDefinitio
return ret return ret
} }
allowDefaults, rejectDefaultsReason := allowDefaults(requestGV, nil)
opts := validationOptions{ opts := validationOptions{
allowDefaults: allowDefaults, allowDefaults: true,
disallowDefaultsReason: rejectDefaultsReason,
requireRecognizedConversionReviewVersion: true, requireRecognizedConversionReviewVersion: true,
requireImmutableNames: false, requireImmutableNames: false,
requireOpenAPISchema: requireOpenAPISchema(requestGV, nil), requireOpenAPISchema: true,
requireValidPropertyType: requireValidPropertyType(requestGV, nil), requireValidPropertyType: true,
requireStructuralSchema: requireStructuralSchema(requestGV, nil), requireStructuralSchema: true,
requirePrunedDefaults: true, requirePrunedDefaults: true,
requireAtomicSetType: true, requireAtomicSetType: true,
requireMapListKeysMapSetValidation: true, requireMapListKeysMapSetValidation: true,
@@ -75,8 +72,8 @@ func ValidateCustomResourceDefinition(obj *apiextensions.CustomResourceDefinitio
allErrs = append(allErrs, validateCustomResourceDefinitionSpec(&obj.Spec, opts, field.NewPath("spec"))...) allErrs = append(allErrs, validateCustomResourceDefinitionSpec(&obj.Spec, opts, field.NewPath("spec"))...)
allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...) allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...) allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
allErrs = append(allErrs, validateAPIApproval(obj, nil, requestGV)...) allErrs = append(allErrs, validateAPIApproval(obj, nil)...)
allErrs = append(allErrs, validatePreserveUnknownFields(obj, nil, requestGV)...) allErrs = append(allErrs, validatePreserveUnknownFields(obj, nil)...)
return allErrs return allErrs
} }
@@ -107,16 +104,14 @@ type validationOptions struct {
} }
// ValidateCustomResourceDefinitionUpdate statically validates // ValidateCustomResourceDefinitionUpdate statically validates
func ValidateCustomResourceDefinitionUpdate(obj, oldObj *apiextensions.CustomResourceDefinition, requestGV schema.GroupVersion) field.ErrorList { func ValidateCustomResourceDefinitionUpdate(obj, oldObj *apiextensions.CustomResourceDefinition) field.ErrorList {
allowDefaults, rejectDefaultsReason := allowDefaults(requestGV, &oldObj.Spec)
opts := validationOptions{ opts := validationOptions{
allowDefaults: allowDefaults, allowDefaults: true,
disallowDefaultsReason: rejectDefaultsReason,
requireRecognizedConversionReviewVersion: oldObj.Spec.Conversion == nil || hasValidConversionReviewVersionOrEmpty(oldObj.Spec.Conversion.ConversionReviewVersions), requireRecognizedConversionReviewVersion: oldObj.Spec.Conversion == nil || hasValidConversionReviewVersionOrEmpty(oldObj.Spec.Conversion.ConversionReviewVersions),
requireImmutableNames: apiextensions.IsCRDConditionTrue(oldObj, apiextensions.Established), requireImmutableNames: apiextensions.IsCRDConditionTrue(oldObj, apiextensions.Established),
requireOpenAPISchema: requireOpenAPISchema(requestGV, &oldObj.Spec), requireOpenAPISchema: requireOpenAPISchema(&oldObj.Spec),
requireValidPropertyType: requireValidPropertyType(requestGV, &oldObj.Spec), requireValidPropertyType: requireValidPropertyType(&oldObj.Spec),
requireStructuralSchema: requireStructuralSchema(requestGV, &oldObj.Spec), requireStructuralSchema: requireStructuralSchema(&oldObj.Spec),
requirePrunedDefaults: requirePrunedDefaults(&oldObj.Spec), requirePrunedDefaults: requirePrunedDefaults(&oldObj.Spec),
requireAtomicSetType: requireAtomicSetType(&oldObj.Spec), requireAtomicSetType: requireAtomicSetType(&oldObj.Spec),
requireMapListKeysMapSetValidation: requireMapListKeysMapSetValidation(&oldObj.Spec), requireMapListKeysMapSetValidation: requireMapListKeysMapSetValidation(&oldObj.Spec),
@@ -126,8 +121,8 @@ func ValidateCustomResourceDefinitionUpdate(obj, oldObj *apiextensions.CustomRes
allErrs = append(allErrs, validateCustomResourceDefinitionSpecUpdate(&obj.Spec, &oldObj.Spec, opts, field.NewPath("spec"))...) allErrs = append(allErrs, validateCustomResourceDefinitionSpecUpdate(&obj.Spec, &oldObj.Spec, opts, field.NewPath("spec"))...)
allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...) allErrs = append(allErrs, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...) allErrs = append(allErrs, ValidateCustomResourceDefinitionStoredVersions(obj.Status.StoredVersions, obj.Spec.Versions, field.NewPath("status").Child("storedVersions"))...)
allErrs = append(allErrs, validateAPIApproval(obj, oldObj, requestGV)...) allErrs = append(allErrs, validateAPIApproval(obj, oldObj)...)
allErrs = append(allErrs, validatePreserveUnknownFields(obj, oldObj, requestGV)...) allErrs = append(allErrs, validatePreserveUnknownFields(obj, oldObj)...)
return allErrs return allErrs
} }
@@ -1129,11 +1124,7 @@ func allowedAtRootSchema(field string) bool {
} }
// requireOpenAPISchema returns true if the request group version requires a schema // requireOpenAPISchema returns true if the request group version requires a schema
func requireOpenAPISchema(requestGV schema.GroupVersion, oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool { func requireOpenAPISchema(oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool {
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// for backwards compatibility
return false
}
if oldCRDSpec != nil && !allVersionsSpecifyOpenAPISchema(oldCRDSpec) { if oldCRDSpec != nil && !allVersionsSpecifyOpenAPISchema(oldCRDSpec) {
// don't tighten validation on existing persisted data // don't tighten validation on existing persisted data
return false return false
@@ -1152,19 +1143,6 @@ func allVersionsSpecifyOpenAPISchema(spec *apiextensions.CustomResourceDefinitio
return true return true
} }
// allowDefaults returns true if the defaulting feature is enabled and the request group version allows adding defaults,
// or false and a reason for the user if defaults are not allowed.
func allowDefaults(requestGV schema.GroupVersion, oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) (bool, string) {
if oldCRDSpec != nil && specHasDefaults(oldCRDSpec) {
// don't tighten validation on existing persisted data
return true, ""
}
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
return false, "(cannot set default values in apiextensions.k8s.io/v1beta1 CRDs, must use apiextensions.k8s.io/v1)"
}
return true, ""
}
func specHasDefaults(spec *apiextensions.CustomResourceDefinitionSpec) bool { func specHasDefaults(spec *apiextensions.CustomResourceDefinitionSpec) bool {
return hasSchemaWith(spec, schemaHasDefaults) return hasSchemaWith(spec, schemaHasDefaults)
} }
@@ -1277,11 +1255,7 @@ func schemaHasKubernetesExtensions(s *apiextensions.JSONSchemaProps) bool {
} }
// requireStructuralSchema returns true if schemas specified must be structural // requireStructuralSchema returns true if schemas specified must be structural
func requireStructuralSchema(requestGV schema.GroupVersion, oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool { func requireStructuralSchema(oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool {
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// for compatibility
return false
}
if oldCRDSpec != nil && specHasNonStructuralSchema(oldCRDSpec) { if oldCRDSpec != nil && specHasNonStructuralSchema(oldCRDSpec) {
// don't tighten validation on existing persisted data // don't tighten validation on existing persisted data
return false return false
@@ -1380,11 +1354,7 @@ func hasInvalidMapListKeysMapSet(schema *apiextensions.JSONSchemaProps) bool {
} }
// requireValidPropertyType returns true if valid openapi v3 types should be required for the given API version // requireValidPropertyType returns true if valid openapi v3 types should be required for the given API version
func requireValidPropertyType(requestGV schema.GroupVersion, oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool { func requireValidPropertyType(oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool {
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// for compatibility
return false
}
if oldCRDSpec != nil && specHasInvalidTypes(oldCRDSpec) { if oldCRDSpec != nil && specHasInvalidTypes(oldCRDSpec) {
// don't tighten validation on existing persisted data // don't tighten validation on existing persisted data
return false return false
@@ -1393,13 +1363,8 @@ func requireValidPropertyType(requestGV schema.GroupVersion, oldCRDSpec *apiexte
} }
// validateAPIApproval returns a list of errors if the API approval annotation isn't valid // validateAPIApproval returns a list of errors if the API approval annotation isn't valid
func validateAPIApproval(newCRD, oldCRD *apiextensions.CustomResourceDefinition, requestGV schema.GroupVersion) field.ErrorList { func validateAPIApproval(newCRD, oldCRD *apiextensions.CustomResourceDefinition) field.ErrorList {
// check to see if we need confirm API approval for kube group. // check to see if we need confirm API approval for kube group.
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// no-op for compatibility with v1beta1
return nil
}
if !apihelpers.IsProtectedCommunityGroup(newCRD.Spec.Group) { if !apihelpers.IsProtectedCommunityGroup(newCRD.Spec.Group) {
// no-op for non-protected groups // no-op for non-protected groups
return nil return nil
@@ -1433,12 +1398,7 @@ func validateAPIApproval(newCRD, oldCRD *apiextensions.CustomResourceDefinition,
} }
} }
func validatePreserveUnknownFields(crd, oldCRD *apiextensions.CustomResourceDefinition, requestGV schema.GroupVersion) field.ErrorList { func validatePreserveUnknownFields(crd, oldCRD *apiextensions.CustomResourceDefinition) field.ErrorList {
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// no-op for compatibility with v1beta1
return nil
}
if oldCRD != nil && oldCRD.Spec.PreserveUnknownFields != nil && *oldCRD.Spec.PreserveUnknownFields { if oldCRD != nil && oldCRD.Spec.PreserveUnknownFields != nil && *oldCRD.Spec.PreserveUnknownFields {
// no-op for compatibility with existing data // no-op for compatibility with existing data
return nil return nil

View File

@@ -137,26 +137,8 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
GenericAPIServer: genericServer, GenericAPIServer: genericServer,
} }
// used later to filter the served resource by those that have expired.
resourceExpirationEvaluator, err := genericapiserver.NewResourceExpirationEvaluator(*c.GenericConfig.Version)
if err != nil {
return nil, err
}
apiResourceConfig := c.GenericConfig.MergedResourceConfig apiResourceConfig := c.GenericConfig.MergedResourceConfig
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, Scheme, metav1.ParameterCodec, Codecs) apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiextensions.GroupName, Scheme, metav1.ParameterCodec, Codecs)
if resourceExpirationEvaluator.ShouldServeForVersion(1, 22) && apiResourceConfig.VersionEnabled(v1beta1.SchemeGroupVersion) {
storage := map[string]rest.Storage{}
// customresourcedefinitions
customResourceDefinitionStorage, err := customresourcedefinition.NewREST(Scheme, c.GenericConfig.RESTOptionsGetter)
if err != nil {
return nil, err
}
storage["customresourcedefinitions"] = customResourceDefinitionStorage
storage["customresourcedefinitions/status"] = customresourcedefinition.NewStatusREST(Scheme, customResourceDefinitionStorage)
apiGroupInfo.VersionedResourcesStorageMap[v1beta1.SchemeGroupVersion.Version] = storage
}
if apiResourceConfig.VersionEnabled(v1.SchemeGroupVersion) { if apiResourceConfig.VersionEnabled(v1.SchemeGroupVersion) {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
// customresourcedefinitions // customresourcedefinitions

View File

@@ -27,9 +27,7 @@ import (
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names" "k8s.io/apiserver/pkg/storage/names"
@@ -111,12 +109,7 @@ func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
// Validate validates a new CustomResourceDefinition. // Validate validates a new CustomResourceDefinition.
func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
var groupVersion schema.GroupVersion return validation.ValidateCustomResourceDefinition(obj.(*apiextensions.CustomResourceDefinition))
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
return validation.ValidateCustomResourceDefinition(obj.(*apiextensions.CustomResourceDefinition), groupVersion)
} }
// WarningsOnCreate returns warnings for the creation of the given object. // WarningsOnCreate returns warnings for the creation of the given object.
@@ -139,12 +132,7 @@ func (strategy) Canonicalize(obj runtime.Object) {
// ValidateUpdate is the default update validation for an end user updating status. // ValidateUpdate is the default update validation for an end user updating status.
func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
var groupVersion schema.GroupVersion return validation.ValidateCustomResourceDefinitionUpdate(obj.(*apiextensions.CustomResourceDefinition), old.(*apiextensions.CustomResourceDefinition))
if requestInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
groupVersion = schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
}
return validation.ValidateCustomResourceDefinitionUpdate(obj.(*apiextensions.CustomResourceDefinition), old.(*apiextensions.CustomResourceDefinition), groupVersion)
} }
// WarningsOnUpdate returns warnings for the given update. // WarningsOnUpdate returns warnings for the given update.

View File

@@ -23,7 +23,6 @@ import (
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
) )
@@ -43,29 +42,19 @@ func TestValidateAPIApproval(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
version string
group string group string
annotationValue string annotationValue string
oldAnnotationValue *string oldAnnotationValue *string
validateError func(t *testing.T, errors field.ErrorList) validateError func(t *testing.T, errors field.ErrorList)
}{ }{
{
name: "ignore v1beta1",
version: "v1beta1",
group: "sigs.k8s.io",
annotationValue: "invalid",
validateError: okFn,
},
{ {
name: "ignore non-k8s group", name: "ignore non-k8s group",
version: "v1",
group: "other.io", group: "other.io",
annotationValue: "invalid", annotationValue: "invalid",
validateError: okFn, validateError: okFn,
}, },
{ {
name: "invalid annotation create", name: "invalid annotation create",
version: "v1",
group: "sigs.k8s.io", group: "sigs.k8s.io",
annotationValue: "invalid", annotationValue: "invalid",
validateError: func(t *testing.T, errors field.ErrorList) { validateError: func(t *testing.T, errors field.ErrorList) {
@@ -80,7 +69,6 @@ func TestValidateAPIApproval(t *testing.T) {
}, },
{ {
name: "invalid annotation update", name: "invalid annotation update",
version: "v1",
group: "sigs.k8s.io", group: "sigs.k8s.io",
annotationValue: "invalid", annotationValue: "invalid",
oldAnnotationValue: strPtr("invalid"), oldAnnotationValue: strPtr("invalid"),
@@ -88,7 +76,6 @@ func TestValidateAPIApproval(t *testing.T) {
}, },
{ {
name: "invalid annotation to missing", name: "invalid annotation to missing",
version: "v1",
group: "sigs.k8s.io", group: "sigs.k8s.io",
annotationValue: "", annotationValue: "",
oldAnnotationValue: strPtr("invalid"), oldAnnotationValue: strPtr("invalid"),
@@ -104,7 +91,6 @@ func TestValidateAPIApproval(t *testing.T) {
}, },
{ {
name: "missing to invalid annotation", name: "missing to invalid annotation",
version: "v1",
group: "sigs.k8s.io", group: "sigs.k8s.io",
annotationValue: "invalid", annotationValue: "invalid",
oldAnnotationValue: strPtr(""), oldAnnotationValue: strPtr(""),
@@ -120,7 +106,6 @@ func TestValidateAPIApproval(t *testing.T) {
}, },
{ {
name: "missing annotation", name: "missing annotation",
version: "v1",
group: "sigs.k8s.io", group: "sigs.k8s.io",
annotationValue: "", annotationValue: "",
validateError: func(t *testing.T, errors field.ErrorList) { validateError: func(t *testing.T, errors field.ErrorList) {
@@ -135,7 +120,6 @@ func TestValidateAPIApproval(t *testing.T) {
}, },
{ {
name: "missing annotation update", name: "missing annotation update",
version: "v1",
group: "sigs.k8s.io", group: "sigs.k8s.io",
annotationValue: "", annotationValue: "",
oldAnnotationValue: strPtr(""), oldAnnotationValue: strPtr(""),
@@ -143,33 +127,16 @@ func TestValidateAPIApproval(t *testing.T) {
}, },
{ {
name: "url", name: "url",
version: "v1",
group: "sigs.k8s.io", group: "sigs.k8s.io",
annotationValue: "https://github.com/kubernetes/kubernetes/pull/79724", annotationValue: "https://github.com/kubernetes/kubernetes/pull/79724",
validateError: okFn, validateError: okFn,
}, },
{ {
name: "unapproved", name: "unapproved",
version: "v1",
group: "sigs.k8s.io", group: "sigs.k8s.io",
annotationValue: "unapproved, other reason", annotationValue: "unapproved, other reason",
validateError: okFn, validateError: okFn,
}, },
{
name: "next version validates",
version: "v2",
group: "sigs.k8s.io",
annotationValue: "invalid",
validateError: func(t *testing.T, errors field.ErrorList) {
t.Helper()
if len(errors) == 0 {
t.Fatal("expected errors, got none")
}
if e, a := `metadata.annotations[api-approved.kubernetes.io]: Invalid value: "invalid": protected groups must have approval annotation "api-approved.kubernetes.io" with either a URL or a reason starting with "unapproved", see https://github.com/kubernetes/enhancements/pull/1111`, errors.ToAggregate().Error(); e != a {
t.Fatal(errors)
}
},
},
} }
for _, test := range tests { for _, test := range tests {
@@ -212,9 +179,9 @@ func TestValidateAPIApproval(t *testing.T) {
var actual field.ErrorList var actual field.ErrorList
if oldCRD == nil { if oldCRD == nil {
actual = validation.ValidateCustomResourceDefinition(crd, schema.GroupVersion{Group: "apiextensions.k8s.io", Version: test.version}) actual = validation.ValidateCustomResourceDefinition(crd)
} else { } else {
actual = validation.ValidateCustomResourceDefinitionUpdate(crd, oldCRD, schema.GroupVersion{Group: "apiextensions.k8s.io", Version: test.version}) actual = validation.ValidateCustomResourceDefinitionUpdate(crd, oldCRD)
} }
test.validateError(t, actual) test.validateError(t, actual)
}) })

View File

@@ -25,7 +25,6 @@ import (
"k8s.io/kube-aggregator/pkg/apis/apiregistration" "k8s.io/kube-aggregator/pkg/apis/apiregistration"
v1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" v1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
"k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1"
aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme" aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
apiservicestorage "k8s.io/kube-aggregator/pkg/registry/apiservice/etcd" apiservicestorage "k8s.io/kube-aggregator/pkg/registry/apiservice/etcd"
) )
@@ -34,14 +33,6 @@ import (
func NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter, shouldServeBeta bool) genericapiserver.APIGroupInfo { func NewRESTStorage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter, shouldServeBeta bool) genericapiserver.APIGroupInfo {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName, aggregatorscheme.Scheme, metav1.ParameterCodec, aggregatorscheme.Codecs) apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apiregistration.GroupName, aggregatorscheme.Scheme, metav1.ParameterCodec, aggregatorscheme.Codecs)
if shouldServeBeta && apiResourceConfigSource.VersionEnabled(v1beta1.SchemeGroupVersion) {
storage := map[string]rest.Storage{}
apiServiceREST := apiservicestorage.NewREST(aggregatorscheme.Scheme, restOptionsGetter)
storage["apiservices"] = apiServiceREST
storage["apiservices/status"] = apiservicestorage.NewStatusREST(aggregatorscheme.Scheme, apiServiceREST)
apiGroupInfo.VersionedResourcesStorageMap["v1beta1"] = storage
}
if apiResourceConfigSource.VersionEnabled(v1.SchemeGroupVersion) { if apiResourceConfigSource.VersionEnabled(v1.SchemeGroupVersion) {
storage := map[string]rest.Storage{} storage := map[string]rest.Storage{}
apiServiceREST := apiservicestorage.NewREST(aggregatorscheme.Scheme, restOptionsGetter) apiServiceREST := apiservicestorage.NewREST(aggregatorscheme.Scheme, restOptionsGetter)

View File

@@ -166,14 +166,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}, },
// -- // --
// k8s.io/kubernetes/pkg/apis/certificates/v1beta1
gvr("certificates.k8s.io", "v1beta1", "certificatesigningrequests"): {
Stub: `{"metadata": {"name": "csr1"}, "spec": {"request": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQnlqQ0NBVE1DQVFBd2dZa3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFOYjNWdWRHRnBiaUJXYVdWM01STXdFUVlEVlFRS0V3cEhiMjluYkdVZ1NXNWpNUjh3CkhRWURWUVFMRXhaSmJtWnZjbTFoZEdsdmJpQlVaV05vYm05c2IyZDVNUmN3RlFZRFZRUURFdzUzZDNjdVoyOXYKWjJ4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBcFp0WUpDSEo0VnBWWEhmVgpJbHN0UVRsTzRxQzAzaGpYK1prUHl2ZFlkMVE0K3FiQWVUd1htQ1VLWUhUaFZSZDVhWFNxbFB6eUlCd2llTVpyCldGbFJRZGRaMUl6WEFsVlJEV3dBbzYwS2VjcWVBWG5uVUsrNWZYb1RJL1VnV3NocmU4dEoreC9UTUhhUUtSL0oKY0lXUGhxYVFoc0p1elpidkFkR0E4MEJMeGRNQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQkJRVUFBNEdCQUlobAo0UHZGcStlN2lwQVJnSTVaTStHWng2bXBDejQ0RFRvMEprd2ZSRGYrQnRyc2FDMHE2OGVUZjJYaFlPc3E0ZmtIClEwdUEwYVZvZzNmNWlKeENhM0hwNWd4YkpRNnpWNmtKMFRFc3VhYU9oRWtvOXNkcENvUE9uUkJtMmkvWFJEMkQKNmlOaDhmOHowU2hHc0ZxakRnRkh5RjNvK2xVeWorVUM2SDFRVzdibgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0="}}`,
ExpectedEtcdPath: "/registry/certificatesigningrequests/csr1",
ExpectedGVK: gvkP("certificates.k8s.io", "v1", "CertificateSigningRequest"),
},
// --
// k8s.io/kubernetes/pkg/apis/certificates/v1 // k8s.io/kubernetes/pkg/apis/certificates/v1
gvr("certificates.k8s.io", "v1", "certificatesigningrequests"): { gvr("certificates.k8s.io", "v1", "certificatesigningrequests"): {
Stub: `{"metadata": {"name": "csr2"}, "spec": {"signerName":"example.com/signer", "usages":["any"], "request": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQnlqQ0NBVE1DQVFBd2dZa3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFOYjNWdWRHRnBiaUJXYVdWM01STXdFUVlEVlFRS0V3cEhiMjluYkdVZ1NXNWpNUjh3CkhRWURWUVFMRXhaSmJtWnZjbTFoZEdsdmJpQlVaV05vYm05c2IyZDVNUmN3RlFZRFZRUURFdzUzZDNjdVoyOXYKWjJ4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBcFp0WUpDSEo0VnBWWEhmVgpJbHN0UVRsTzRxQzAzaGpYK1prUHl2ZFlkMVE0K3FiQWVUd1htQ1VLWUhUaFZSZDVhWFNxbFB6eUlCd2llTVpyCldGbFJRZGRaMUl6WEFsVlJEV3dBbzYwS2VjcWVBWG5uVUsrNWZYb1RJL1VnV3NocmU4dEoreC9UTUhhUUtSL0oKY0lXUGhxYVFoc0p1elpidkFkR0E4MEJMeGRNQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQkJRVUFBNEdCQUlobAo0UHZGcStlN2lwQVJnSTVaTStHWng2bXBDejQ0RFRvMEprd2ZSRGYrQnRyc2FDMHE2OGVUZjJYaFlPc3E0ZmtIClEwdUEwYVZvZzNmNWlKeENhM0hwNWd4YkpRNnpWNmtKMFRFc3VhYU9oRWtvOXNkcENvUE9uUkJtMmkvWFJEMkQKNmlOaDhmOHowU2hHc0ZxakRnRkh5RjNvK2xVeWorVUM2SDFRVzdibgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0="}}`, Stub: `{"metadata": {"name": "csr2"}, "spec": {"signerName":"example.com/signer", "usages":["any"], "request": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQnlqQ0NBVE1DQVFBd2dZa3hDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJRXdwRFlXeHBabTl5Ym1saApNUll3RkFZRFZRUUhFdzFOYjNWdWRHRnBiaUJXYVdWM01STXdFUVlEVlFRS0V3cEhiMjluYkdVZ1NXNWpNUjh3CkhRWURWUVFMRXhaSmJtWnZjbTFoZEdsdmJpQlVaV05vYm05c2IyZDVNUmN3RlFZRFZRUURFdzUzZDNjdVoyOXYKWjJ4bExtTnZiVENCbnpBTkJna3Foa2lHOXcwQkFRRUZBQU9CalFBd2dZa0NnWUVBcFp0WUpDSEo0VnBWWEhmVgpJbHN0UVRsTzRxQzAzaGpYK1prUHl2ZFlkMVE0K3FiQWVUd1htQ1VLWUhUaFZSZDVhWFNxbFB6eUlCd2llTVpyCldGbFJRZGRaMUl6WEFsVlJEV3dBbzYwS2VjcWVBWG5uVUsrNWZYb1RJL1VnV3NocmU4dEoreC9UTUhhUUtSL0oKY0lXUGhxYVFoc0p1elpidkFkR0E4MEJMeGRNQ0F3RUFBYUFBTUEwR0NTcUdTSWIzRFFFQkJRVUFBNEdCQUlobAo0UHZGcStlN2lwQVJnSTVaTStHWng2bXBDejQ0RFRvMEprd2ZSRGYrQnRyc2FDMHE2OGVUZjJYaFlPc3E0ZmtIClEwdUEwYVZvZzNmNWlKeENhM0hwNWd4YkpRNnpWNmtKMFRFc3VhYU9oRWtvOXNkcENvUE9uUkJtMmkvWFJEMkQKNmlOaDhmOHowU2hHc0ZxakRnRkh5RjNvK2xVeWorVUM2SDFRVzdibgotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0="}}`,
@@ -188,14 +180,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}, },
// -- // --
// k8s.io/kubernetes/pkg/apis/coordination/v1beta1
gvr("coordination.k8s.io", "v1beta1", "leases"): {
Stub: `{"metadata": {"name": "leasev1beta1"}, "spec": {"holderIdentity": "holder", "leaseDurationSeconds": 5}}`,
ExpectedEtcdPath: "/registry/leases/" + namespace + "/leasev1beta1",
ExpectedGVK: gvkP("coordination.k8s.io", "v1", "Lease"),
},
// --
// k8s.io/kubernetes/pkg/apis/discovery/v1 // k8s.io/kubernetes/pkg/apis/discovery/v1
gvr("discovery.k8s.io", "v1", "endpointslices"): { gvr("discovery.k8s.io", "v1", "endpointslices"): {
Stub: `{"metadata": {"name": "slicev1"}, "addressType": "IPv4", "protocol": "TCP", "ports": [], "endpoints": []}`, Stub: `{"metadata": {"name": "slicev1"}, "addressType": "IPv4", "protocol": "TCP", "ports": [], "endpoints": []}`,
@@ -227,27 +211,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}, },
// -- // --
// k8s.io/kubernetes/pkg/apis/extensions/v1beta1
gvr("extensions", "v1beta1", "ingresses"): {
Stub: `{"metadata": {"name": "ingress1"}, "spec": {"backend": {"serviceName": "service", "servicePort": 5000}}}`,
ExpectedEtcdPath: "/registry/ingress/" + namespace + "/ingress1",
ExpectedGVK: gvkP("networking.k8s.io", "v1", "Ingress"),
},
// --
// k8s.io/kubernetes/pkg/apis/networking/v1beta1
gvr("networking.k8s.io", "v1beta1", "ingresses"): {
Stub: `{"metadata": {"name": "ingress2"}, "spec": {"backend": {"serviceName": "service", "servicePort": 5000}}}`,
ExpectedEtcdPath: "/registry/ingress/" + namespace + "/ingress2",
ExpectedGVK: gvkP("networking.k8s.io", "v1", "Ingress"),
},
gvr("networking.k8s.io", "v1beta1", "ingressclasses"): {
Stub: `{"metadata": {"name": "ingressclass2"}, "spec": {"controller": "example.com/controller"}}`,
ExpectedEtcdPath: "/registry/ingressclasses/ingressclass2",
ExpectedGVK: gvkP("networking.k8s.io", "v1", "IngressClass"),
},
// --
// k8s.io/kubernetes/pkg/apis/networking/v1 // k8s.io/kubernetes/pkg/apis/networking/v1
gvr("networking.k8s.io", "v1", "ingresses"): { gvr("networking.k8s.io", "v1", "ingresses"): {
Stub: `{"metadata": {"name": "ingress3"}, "spec": {"defaultBackend": {"service":{"name":"service", "port":{"number": 5000}}}}}`, Stub: `{"metadata": {"name": "ingress3"}, "spec": {"defaultBackend": {"service":{"name":"service", "port":{"number": 5000}}}}}`,
@@ -282,14 +245,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}, },
// -- // --
// k8s.io/kubernetes/pkg/apis/storage/v1alpha1
gvr("storage.k8s.io", "v1alpha1", "volumeattachments"): {
Stub: `{"metadata": {"name": "va1"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv1"}}}`,
ExpectedEtcdPath: "/registry/volumeattachments/va1",
ExpectedGVK: gvkP("storage.k8s.io", "v1", "VolumeAttachment"),
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1alpha1 // k8s.io/kubernetes/pkg/apis/storage/v1alpha1
gvr("storage.k8s.io", "v1alpha1", "csistoragecapacities"): { gvr("storage.k8s.io", "v1alpha1", "csistoragecapacities"): {
Stub: `{"metadata": {"name": "csc-12345-1"}, "storageClassName": "sc1"}`, Stub: `{"metadata": {"name": "csc-12345-1"}, "storageClassName": "sc1"}`,
@@ -328,14 +283,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}, },
// -- // --
// k8s.io/kubernetes/pkg/apis/storage/v1beta1
gvr("storage.k8s.io", "v1beta1", "volumeattachments"): {
Stub: `{"metadata": {"name": "va2"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv2"}}}`,
ExpectedEtcdPath: "/registry/volumeattachments/va2",
ExpectedGVK: gvkP("storage.k8s.io", "v1", "VolumeAttachment"),
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1 // k8s.io/kubernetes/pkg/apis/storage/v1
gvr("storage.k8s.io", "v1", "volumeattachments"): { gvr("storage.k8s.io", "v1", "volumeattachments"): {
Stub: `{"metadata": {"name": "va3"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv3"}}}`, Stub: `{"metadata": {"name": "va3"}, "spec": {"attacher": "gce", "nodeName": "localhost", "source": {"persistentVolumeName": "pv3"}}}`,
@@ -344,13 +291,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
// -- // --
// k8s.io/kubernetes/pkg/apis/storage/v1beta1 // k8s.io/kubernetes/pkg/apis/storage/v1beta1
gvr("storage.k8s.io", "v1beta1", "storageclasses"): {
Stub: `{"metadata": {"name": "sc1"}, "provisioner": "aws"}`,
ExpectedEtcdPath: "/registry/storageclasses/sc1",
ExpectedGVK: gvkP("storage.k8s.io", "v1", "StorageClass"),
},
// --
// k8s.io/kubernetes/pkg/apis/storage/v1beta1 // k8s.io/kubernetes/pkg/apis/storage/v1beta1
gvr("storage.k8s.io", "v1beta1", "csistoragecapacities"): { gvr("storage.k8s.io", "v1beta1", "csistoragecapacities"): {
Stub: `{"metadata": {"name": "csc-12345-2"}, "storageClassName": "sc1"}`, Stub: `{"metadata": {"name": "csc-12345-2"}, "storageClassName": "sc1"}`,
@@ -365,52 +305,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}, },
// -- // --
// k8s.io/kubernetes/pkg/apis/rbac/v1alpha1
gvr("rbac.authorization.k8s.io", "v1alpha1", "roles"): {
Stub: `{"metadata": {"name": "role1"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`,
ExpectedEtcdPath: "/registry/roles/" + namespace + "/role1",
ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "Role"),
},
gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterroles"): {
Stub: `{"metadata": {"name": "crole1"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`,
ExpectedEtcdPath: "/registry/clusterroles/crole1",
ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRole"),
},
gvr("rbac.authorization.k8s.io", "v1alpha1", "rolebindings"): {
Stub: `{"metadata": {"name": "roleb1"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`,
ExpectedEtcdPath: "/registry/rolebindings/" + namespace + "/roleb1",
ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "RoleBinding"),
},
gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterrolebindings"): {
Stub: `{"metadata": {"name": "croleb1"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`,
ExpectedEtcdPath: "/registry/clusterrolebindings/croleb1",
ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"),
},
// --
// k8s.io/kubernetes/pkg/apis/rbac/v1beta1
gvr("rbac.authorization.k8s.io", "v1beta1", "roles"): {
Stub: `{"metadata": {"name": "role2"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`,
ExpectedEtcdPath: "/registry/roles/" + namespace + "/role2",
ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "Role"),
},
gvr("rbac.authorization.k8s.io", "v1beta1", "clusterroles"): {
Stub: `{"metadata": {"name": "crole2"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`,
ExpectedEtcdPath: "/registry/clusterroles/crole2",
ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRole"),
},
gvr("rbac.authorization.k8s.io", "v1beta1", "rolebindings"): {
Stub: `{"metadata": {"name": "roleb2"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`,
ExpectedEtcdPath: "/registry/rolebindings/" + namespace + "/roleb2",
ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "RoleBinding"),
},
gvr("rbac.authorization.k8s.io", "v1beta1", "clusterrolebindings"): {
Stub: `{"metadata": {"name": "croleb2"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`,
ExpectedEtcdPath: "/registry/clusterrolebindings/croleb2",
ExpectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"),
},
// --
// k8s.io/kubernetes/pkg/apis/rbac/v1 // k8s.io/kubernetes/pkg/apis/rbac/v1
gvr("rbac.authorization.k8s.io", "v1", "roles"): { gvr("rbac.authorization.k8s.io", "v1", "roles"): {
Stub: `{"metadata": {"name": "role3"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`, Stub: `{"metadata": {"name": "role3"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`,
@@ -441,35 +335,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}, },
// -- // --
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1
gvr("admissionregistration.k8s.io", "v1beta1", "validatingwebhookconfigurations"): {
Stub: `{"metadata":{"name":"hook1","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`,
ExpectedEtcdPath: "/registry/validatingwebhookconfigurations/hook1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1", "ValidatingWebhookConfiguration"),
},
gvr("admissionregistration.k8s.io", "v1beta1", "mutatingwebhookconfigurations"): {
Stub: `{"metadata":{"name":"hook1","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`,
ExpectedEtcdPath: "/registry/mutatingwebhookconfigurations/hook1",
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1", "MutatingWebhookConfiguration"),
},
// --
// k8s.io/kubernetes/pkg/apis/scheduling/v1alpha1
gvr("scheduling.k8s.io", "v1alpha1", "priorityclasses"): {
Stub: `{"metadata":{"name":"pc1"},"Value":1000}`,
ExpectedEtcdPath: "/registry/priorityclasses/pc1",
ExpectedGVK: gvkP("scheduling.k8s.io", "v1", "PriorityClass"),
},
// --
// k8s.io/kubernetes/pkg/apis/scheduling/v1beta1
gvr("scheduling.k8s.io", "v1beta1", "priorityclasses"): {
Stub: `{"metadata":{"name":"pc2"},"Value":1000}`,
ExpectedEtcdPath: "/registry/priorityclasses/pc2",
ExpectedGVK: gvkP("scheduling.k8s.io", "v1", "PriorityClass"),
},
// --
// k8s.io/kubernetes/pkg/apis/scheduling/v1 // k8s.io/kubernetes/pkg/apis/scheduling/v1
gvr("scheduling.k8s.io", "v1", "priorityclasses"): { gvr("scheduling.k8s.io", "v1", "priorityclasses"): {
Stub: `{"metadata":{"name":"pc3"},"Value":1000}`, Stub: `{"metadata":{"name":"pc3"},"Value":1000}`,
@@ -477,15 +342,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}, },
// -- // --
// k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1
// depends on aggregator using the same ungrouped RESTOptionsGetter as the kube apiserver, not SimpleRestOptionsFactory in aggregator.go
gvr("apiregistration.k8s.io", "v1beta1", "apiservices"): {
Stub: `{"metadata": {"name": "as1.foo.com"}, "spec": {"group": "foo.com", "version": "as1", "groupPriorityMinimum":100, "versionPriority":10}}`,
ExpectedEtcdPath: "/registry/apiregistration.k8s.io/apiservices/as1.foo.com",
ExpectedGVK: gvkP("apiregistration.k8s.io", "v1", "APIService"),
},
// --
// k8s.io/kube-aggregator/pkg/apis/apiregistration/v1 // k8s.io/kube-aggregator/pkg/apis/apiregistration/v1
// depends on aggregator using the same ungrouped RESTOptionsGetter as the kube apiserver, not SimpleRestOptionsFactory in aggregator.go // depends on aggregator using the same ungrouped RESTOptionsGetter as the kube apiserver, not SimpleRestOptionsFactory in aggregator.go
gvr("apiregistration.k8s.io", "v1", "apiservices"): { gvr("apiregistration.k8s.io", "v1", "apiservices"): {
@@ -503,11 +359,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
ExpectedEtcdPath: "/registry/apiextensions.k8s.io/customresourcedefinitions/openshiftwebconsoleconfigs.webconsole2.operator.openshift.io", ExpectedEtcdPath: "/registry/apiextensions.k8s.io/customresourcedefinitions/openshiftwebconsoleconfigs.webconsole2.operator.openshift.io",
ExpectedGVK: gvkP("apiextensions.k8s.io", "v1beta1", "CustomResourceDefinition"), ExpectedGVK: gvkP("apiextensions.k8s.io", "v1beta1", "CustomResourceDefinition"),
}, },
// k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1
gvr("apiextensions.k8s.io", "v1beta1", "customresourcedefinitions"): {
Stub: `{"metadata": {"name": "openshiftwebconsoleconfigs.webconsole.operator.openshift.io"},"spec": {"scope": "Cluster","group": "webconsole.operator.openshift.io","version": "v1alpha1","names": {"kind": "OpenShiftWebConsoleConfig","plural": "openshiftwebconsoleconfigs","singular": "openshiftwebconsoleconfig"}}}`,
ExpectedEtcdPath: "/registry/apiextensions.k8s.io/customresourcedefinitions/openshiftwebconsoleconfigs.webconsole.operator.openshift.io",
},
gvr("cr.bar.com", "v1", "foos"): { gvr("cr.bar.com", "v1", "foos"): {
Stub: `{"kind": "Foo", "apiVersion": "cr.bar.com/v1", "metadata": {"name": "cr1foo"}, "color": "blue"}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper Stub: `{"kind": "Foo", "apiVersion": "cr.bar.com/v1", "metadata": {"name": "cr1foo"}, "color": "blue"}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper
ExpectedEtcdPath: "/registry/cr.bar.com/foos/" + namespace + "/cr1foo", ExpectedEtcdPath: "/registry/cr.bar.com/foos/" + namespace + "/cr1foo",
@@ -563,13 +414,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
} }
// add csinodes // add csinodes
// k8s.io/kubernetes/pkg/apis/storage/v1beta1
etcdStorageData[gvr("storage.k8s.io", "v1beta1", "csinodes")] = StorageData{
Stub: `{"metadata": {"name": "csini1"}, "spec": {"drivers": [{"name": "test-driver", "nodeID": "localhost", "topologyKeys": ["company.com/zone1", "company.com/zone2"]}]}}`,
ExpectedEtcdPath: "/registry/csinodes/csini1",
ExpectedGVK: gvkP("storage.k8s.io", "v1", "CSINode"),
}
// k8s.io/kubernetes/pkg/apis/storage/v1 // k8s.io/kubernetes/pkg/apis/storage/v1
etcdStorageData[gvr("storage.k8s.io", "v1", "csinodes")] = StorageData{ etcdStorageData[gvr("storage.k8s.io", "v1", "csinodes")] = StorageData{
Stub: `{"metadata": {"name": "csini2"}, "spec": {"drivers": [{"name": "test-driver", "nodeID": "localhost", "topologyKeys": ["company.com/zone1", "company.com/zone2"]}]}}`, Stub: `{"metadata": {"name": "csini2"}, "spec": {"drivers": [{"name": "test-driver", "nodeID": "localhost", "topologyKeys": ["company.com/zone1", "company.com/zone2"]}]}}`,
@@ -577,13 +421,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
} }
// add csidrivers // add csidrivers
// k8s.io/kubernetes/pkg/apis/storage/v1beta1
etcdStorageData[gvr("storage.k8s.io", "v1beta1", "csidrivers")] = StorageData{
Stub: `{"metadata": {"name": "csid1"}, "spec": {"attachRequired": true, "podInfoOnMount": true}}`,
ExpectedEtcdPath: "/registry/csidrivers/csid1",
ExpectedGVK: gvkP("storage.k8s.io", "v1", "CSIDriver"),
}
// k8s.io/kubernetes/pkg/apis/storage/v1 // k8s.io/kubernetes/pkg/apis/storage/v1
etcdStorageData[gvr("storage.k8s.io", "v1", "csidrivers")] = StorageData{ etcdStorageData[gvr("storage.k8s.io", "v1", "csidrivers")] = StorageData{
Stub: `{"metadata": {"name": "csid2"}, "spec": {"attachRequired": true, "podInfoOnMount": true}}`, Stub: `{"metadata": {"name": "csid2"}, "spec": {"attachRequired": true, "podInfoOnMount": true}}`,