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: "v1beta1"}: {group: 17750, version: 5},
{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: "v1beta1"}: {group: 17600, version: 9},
{Group: "autoscaling", Version: "v1"}: {group: 17500, version: 15},
{Group: "autoscaling", Version: "v2beta1"}: {group: 17500, version: 9},
{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: "v2alpha1"}: {group: 17400, version: 9},
{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: "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: "v1beta1"}: {group: 17100, version: 9},
{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: "v1beta1"}: {group: 16800, version: 9},
{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: "v1beta1"}: {group: 16700, version: 9},
{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: "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: "v1beta1"}: {group: 16500, version: 9},
{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: "v1beta1"}: {group: 16300, version: 9},

View File

@@ -22,7 +22,6 @@ import (
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
@@ -201,11 +200,11 @@ func validateAdmissionReviewVersions(versions []string, requireRecognizedAdmissi
}
// 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{
requireNoSideEffects: requireNoSideEffects(requestGV),
requireNoSideEffects: true,
requireRecognizedAdmissionReviewVersion: true,
requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV),
requireUniqueWebhookNames: true,
})
}
@@ -226,11 +225,11 @@ func validateValidatingWebhookConfiguration(e *admissionregistration.ValidatingW
}
// 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{
requireNoSideEffects: requireNoSideEffects(requestGV),
requireNoSideEffects: true,
requireRecognizedAdmissionReviewVersion: true,
requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV),
requireUniqueWebhookNames: true,
})
}
@@ -497,29 +496,19 @@ func validatingHasNoSideEffects(webhooks []admissionregistration.ValidatingWebho
}
// 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{
requireNoSideEffects: requireNoSideEffects(requestGV) && validatingHasNoSideEffects(oldC.Webhooks),
requireNoSideEffects: validatingHasNoSideEffects(oldC.Webhooks),
requireRecognizedAdmissionReviewVersion: validatingHasAcceptedAdmissionReviewVersions(oldC.Webhooks),
requireUniqueWebhookNames: requireUniqueWebhookNames(requestGV) && validatingHasUniqueWebhookNames(oldC.Webhooks),
requireUniqueWebhookNames: validatingHasUniqueWebhookNames(oldC.Webhooks),
})
}
// 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{
requireNoSideEffects: requireNoSideEffects(requestGV) && mutatingHasNoSideEffects(oldC.Webhooks),
requireNoSideEffects: mutatingHasNoSideEffects(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"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
)
@@ -46,6 +45,7 @@ func newValidatingWebhookConfiguration(hooks []admissionregistration.ValidatingW
}
func TestValidateValidatingWebhookConfiguration(t *testing.T) {
noSideEffect := admissionregistration.SideEffectClassNone
unknownSideEffect := admissionregistration.SideEffectClassUnknown
validClientConfig := admissionregistration.WebhookClientConfig{
URL: strPtr("https://example.com"),
@@ -53,7 +53,6 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
tests := []struct {
name string
config *admissionregistration.ValidatingWebhookConfiguration
gv schema.GroupVersion
expectedError string
}{
{
@@ -83,11 +82,10 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
AdmissionReviewVersions: []string{"v1beta1"},
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{
@@ -96,11 +94,10 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
AdmissionReviewVersions: []string{"v1beta1", "invalid-version"},
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{
@@ -131,24 +128,23 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
{
Name: "k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
{
Name: "",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
}, 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`,
},
{
name: "Webhooks must have unique names when not created via v1beta1",
name: "Webhooks must have unique names when created",
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
{
Name: "webhook.k8s.io",
@@ -161,26 +157,8 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
SideEffects: &unknownSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
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",
config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
@@ -271,7 +249,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
Rules: []admissionregistration.RuleWithOperations{
{
Operations: []admissionregistration.OperationType{"CREATE"},
@@ -284,7 +262,6 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
},
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
},
{
name: `resource "*" cannot mix with resources that don't have subresources`,
@@ -334,7 +311,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
Rules: []admissionregistration.RuleWithOperations{
{
Operations: []admissionregistration.OperationType{"CREATE"},
@@ -347,7 +324,6 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
},
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
},
{
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`,
},
{
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{
{
Name: "webhook.k8s.io",
@@ -440,22 +416,6 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
}(),
},
}, 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"`,
},
{
@@ -599,10 +559,9 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
Port: 443,
},
},
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{
@@ -618,10 +577,9 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
Port: 443,
},
},
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{
@@ -637,7 +595,7 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
Port: 443,
},
},
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
}, true),
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",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(1),
},
{
Name: "webhook2.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(15),
},
{
Name: "webhook3.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(30),
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateValidatingWebhookConfiguration(test.config, test.gv)
errs := ValidateValidatingWebhookConfiguration(test.config)
err := errs.ToAggregate()
if err != nil {
if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
@@ -821,7 +778,6 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
name string
oldconfig *admissionregistration.ValidatingWebhookConfiguration
config *admissionregistration.ValidatingWebhookConfiguration
gv schema.GroupVersion
expectedError string
}{
{
@@ -903,7 +859,7 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
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{
{
Name: "webhook.k8s.io",
@@ -923,7 +879,6 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &unknownSideEffect,
},
}, false),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
},
{
@@ -952,37 +907,12 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &unknownSideEffect,
},
}, 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: ``,
},
}
for _, test := range tests {
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()
if err != nil {
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) {
noSideEffect := admissionregistration.SideEffectClassNone
unknownSideEffect := admissionregistration.SideEffectClassUnknown
validClientConfig := admissionregistration.WebhookClientConfig{
URL: strPtr("https://example.com"),
@@ -1022,7 +953,6 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
tests := []struct {
name string
config *admissionregistration.MutatingWebhookConfiguration
gv schema.GroupVersion
expectedError string
}{
{
@@ -1052,11 +982,10 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
AdmissionReviewVersions: []string{"v1beta1"},
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{
@@ -1065,11 +994,10 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
AdmissionReviewVersions: []string{"v1beta1", "invalid-version"},
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{
@@ -1100,24 +1028,23 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
{
Name: "k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
{
Name: "",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
}, 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`,
},
{
name: "Webhooks must have unique names when not created via v1beta1",
name: "Webhooks must have unique names when created",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{
Name: "webhook.k8s.io",
@@ -1130,26 +1057,8 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
SideEffects: &unknownSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
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",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
@@ -1240,7 +1149,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
Rules: []admissionregistration.RuleWithOperations{
{
Operations: []admissionregistration.OperationType{"CREATE"},
@@ -1253,7 +1162,6 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
},
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
},
{
name: `resource "*" cannot mix with resources that don't have subresources`,
@@ -1303,7 +1211,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
{
Name: "webhook.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
Rules: []admissionregistration.RuleWithOperations{
{
Operations: []admissionregistration.OperationType{"CREATE"},
@@ -1316,7 +1224,6 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
},
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
},
{
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`,
},
{
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{
{
Name: "webhook.k8s.io",
@@ -1409,22 +1316,6 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
}(),
},
}, 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"`,
},
{
@@ -1568,10 +1459,9 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
Port: 443,
},
},
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{
@@ -1587,10 +1477,9 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
Port: 443,
},
},
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
expectedError: ``,
},
{
@@ -1606,7 +1495,7 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
Port: 443,
},
},
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
},
}, true),
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",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(1),
},
{
Name: "webhook2.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(15),
},
{
Name: "webhook3.k8s.io",
ClientConfig: validClientConfig,
SideEffects: &unknownSideEffect,
SideEffects: &noSideEffect,
TimeoutSeconds: int32Ptr(30),
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1beta1"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateMutatingWebhookConfiguration(test.config, test.gv)
errs := ValidateMutatingWebhookConfiguration(test.config)
err := errs.ToAggregate()
if err != nil {
if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" {
@@ -1791,7 +1679,6 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) {
name string
oldconfig *admissionregistration.MutatingWebhookConfiguration
config *admissionregistration.MutatingWebhookConfiguration
gv schema.GroupVersion
expectedError string
}{
{
@@ -1917,35 +1804,10 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &unknownSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "foo", Version: "bar"},
expectedError: ``,
},
{
name: "Webhooks can have duplicate names when updated via v1beta1",
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",
name: "Webhooks can't have side effects when old config has no side effects",
config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
{
Name: "webhook.k8s.io",
@@ -1960,7 +1822,6 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &noSideEffect,
},
}, true),
gv: schema.GroupVersion{Group: "admissionregistration.k8s.io", Version: "v1"},
expectedError: `Unsupported value: "Unknown": supported values: "None", "NoneOnDryRun"`,
},
{
@@ -1979,32 +1840,12 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) {
SideEffects: &unknownSideEffect,
},
}, 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: ``,
},
}
for _, test := range tests {
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()
if err != nil {
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"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
utilcert "k8s.io/client-go/util/cert"
"k8s.io/kubernetes/pkg/apis/certificates"
certificatesv1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
)
@@ -139,8 +137,8 @@ func ValidateCertificateRequestName(name string, prefix bool) []string {
return nil
}
func ValidateCertificateSigningRequestCreate(csr *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList {
opts := getValidationOptions(version, csr, nil)
func ValidateCertificateSigningRequestCreate(csr *certificates.CertificateSigningRequest) field.ErrorList {
opts := getValidationOptions(csr, nil)
return validateCertificateSigningRequest(csr, opts)
}
@@ -347,19 +345,19 @@ func ValidateCertificateSigningRequestSignerName(fldPath *field.Path, signerName
return el
}
func ValidateCertificateSigningRequestUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList {
opts := getValidationOptions(version, newCSR, oldCSR)
func ValidateCertificateSigningRequestUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest) field.ErrorList {
opts := getValidationOptions(newCSR, oldCSR)
return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts)
}
func ValidateCertificateSigningRequestStatusUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList {
opts := getValidationOptions(version, newCSR, oldCSR)
func ValidateCertificateSigningRequestStatusUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest) field.ErrorList {
opts := getValidationOptions(newCSR, oldCSR)
opts.allowSettingCertificate = true
return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts)
}
func ValidateCertificateSigningRequestApprovalUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest, version schema.GroupVersion) field.ErrorList {
opts := getValidationOptions(version, newCSR, oldCSR)
func ValidateCertificateSigningRequestApprovalUpdate(newCSR, oldCSR *certificates.CertificateSigningRequest) field.ErrorList {
opts := getValidationOptions(newCSR, oldCSR)
opts.allowSettingApprovalConditions = true
return validateCertificateSigningRequestUpdate(newCSR, oldCSR, opts)
}
@@ -420,24 +418,19 @@ func findConditions(csr *certificates.CertificateSigningRequest, conditionType c
// compatible with the specified version and existing CSR.
// oldCSR may be nil if this is a create request.
// 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{
allowResettingCertificate: allowResettingCertificate(version),
allowResettingCertificate: false,
allowBothApprovedAndDenied: allowBothApprovedAndDenied(oldCSR),
allowLegacySignerName: allowLegacySignerName(version, oldCSR),
allowDuplicateConditionTypes: allowDuplicateConditionTypes(version, oldCSR),
allowEmptyConditionType: allowEmptyConditionType(version, oldCSR),
allowArbitraryCertificate: allowArbitraryCertificate(version, newCSR, oldCSR),
allowDuplicateUsages: allowDuplicateUsages(version, oldCSR),
allowUnknownUsages: allowUnknownUsages(version, oldCSR),
allowLegacySignerName: allowLegacySignerName(oldCSR),
allowDuplicateConditionTypes: allowDuplicateConditionTypes(oldCSR),
allowEmptyConditionType: allowEmptyConditionType(oldCSR),
allowArbitraryCertificate: allowArbitraryCertificate(newCSR, oldCSR),
allowDuplicateUsages: allowDuplicateUsages(oldCSR),
allowUnknownUsages: allowUnknownUsages(oldCSR),
}
}
func allowResettingCertificate(version schema.GroupVersion) bool {
// compatibility with v1beta1
return version == certificatesv1beta1.SchemeGroupVersion
}
func allowBothApprovedAndDenied(oldCSR *certificates.CertificateSigningRequest) bool {
if oldCSR == nil {
return false
@@ -455,10 +448,8 @@ func allowBothApprovedAndDenied(oldCSR *certificates.CertificateSigningRequest)
return approved && denied
}
func allowLegacySignerName(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool {
func allowLegacySignerName(oldCSR *certificates.CertificateSigningRequest) bool {
switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && oldCSR.Spec.SignerName == certificates.LegacyUnknownSignerName:
return true // compatibility with existing data
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 {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && hasDuplicateConditionTypes(oldCSR):
return true // compatibility with existing data
default:
@@ -487,10 +476,8 @@ func hasDuplicateConditionTypes(csr *certificates.CertificateSigningRequest) boo
return false
}
func allowEmptyConditionType(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool {
func allowEmptyConditionType(oldCSR *certificates.CertificateSigningRequest) bool {
switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && hasEmptyConditionType(oldCSR):
return true // compatibility with existing data
default:
@@ -506,10 +493,8 @@ func hasEmptyConditionType(csr *certificates.CertificateSigningRequest) bool {
return false
}
func allowArbitraryCertificate(version schema.GroupVersion, newCSR, oldCSR *certificates.CertificateSigningRequest) bool {
func allowArbitraryCertificate(newCSR, oldCSR *certificates.CertificateSigningRequest) bool {
switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case newCSR != nil && oldCSR != nil && bytes.Equal(newCSR.Status.Certificate, oldCSR.Status.Certificate):
return true // tolerate updates that don't touch status.certificate
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 {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && hasUnknownUsage(oldCSR.Spec.Usages):
return true // compatibility with existing data
default:
@@ -539,10 +522,8 @@ func hasUnknownUsage(usages []certificates.KeyUsage) bool {
return false
}
func allowDuplicateUsages(version schema.GroupVersion, oldCSR *certificates.CertificateSigningRequest) bool {
func allowDuplicateUsages(oldCSR *certificates.CertificateSigningRequest) bool {
switch {
case version == certificatesv1beta1.SchemeGroupVersion:
return true // compatibility with v1beta1
case oldCSR != nil && hasDuplicateUsage(oldCSR.Spec.Usages):
return true // compatibility with existing data
default:

View File

@@ -29,12 +29,10 @@ import (
"time"
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/validation/field"
"k8s.io/client-go/util/certificate/csr"
capi "k8s.io/kubernetes/pkg/apis/certificates"
capiv1beta1 "k8s.io/kubernetes/pkg/apis/certificates/v1beta1"
"k8s.io/kubernetes/pkg/apis/core"
"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))
tests := map[string]struct {
csr capi.CertificateSigningRequest
gv schema.GroupVersion
errs field.ErrorList
}{
"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"),
},
},
"unknown and duplicate usages - v1beta1": {
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"},
"unknown and duplicate usages": {
csr: capi.CertificateSigningRequest{
ObjectMeta: validObjectMeta,
Spec: capi.CertificateSigningRequestSpec{
@@ -372,7 +357,7 @@ func TestValidateCertificateSigningRequestCreate(t *testing.T) {
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
el := ValidateCertificateSigningRequestCreate(&test.csr, test.gv)
el := ValidateCertificateSigningRequestCreate(&test.csr)
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())
}
@@ -421,58 +406,22 @@ func newCSRPEM(t *testing.T) []byte {
func Test_getValidationOptions(t *testing.T) {
tests := []struct {
name string
version schema.GroupVersion
newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest
want certificateValidationOptions
}{
{
name: "v1beta1 compatible create",
version: capiv1beta1.SchemeGroupVersion,
oldCSR: nil,
want: certificateValidationOptions{
allowResettingCertificate: true,
allowBothApprovedAndDenied: false,
allowLegacySignerName: true,
allowDuplicateConditionTypes: true,
allowEmptyConditionType: true,
allowArbitraryCertificate: true,
allowUnknownUsages: true,
allowDuplicateUsages: true,
},
},
{
name: "v1 strict create",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
name: "strict create",
oldCSR: nil,
want: certificateValidationOptions{},
},
{
name: "v1beta1 compatible update",
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"},
name: "strict update",
oldCSR: &capi.CertificateSigningRequest{},
want: certificateValidationOptions{},
},
{
name: "v1 compatible update, approved+denied",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
name: "compatible update, approved+denied",
oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
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",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
name: "compatible update, legacy signerName",
oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{SignerName: capi.LegacyUnknownSignerName}},
want: certificateValidationOptions{
allowLegacySignerName: true,
},
},
{
name: "v1 compatible update, duplicate condition types",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
name: "compatible update, duplicate condition types",
oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
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",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
name: "compatible update, empty condition types",
oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{}},
}},
@@ -509,8 +455,7 @@ func Test_getValidationOptions(t *testing.T) {
},
},
{
name: "v1 compatible update, no diff to certificate",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
name: "compatible update, no diff to certificate",
newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
Certificate: validCertificate,
}},
@@ -522,8 +467,7 @@ func Test_getValidationOptions(t *testing.T) {
},
},
{
name: "v1 compatible update, existing invalid certificate",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
name: "compatible update, existing invalid certificate",
newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
Certificate: []byte(`new - no PEM blocks`),
}},
@@ -535,16 +479,14 @@ func Test_getValidationOptions(t *testing.T) {
},
},
{
name: "v1 compatible update, existing unknown usages",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
name: "compatible update, existing unknown usages",
oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"unknown"}}},
want: certificateValidationOptions{
allowUnknownUsages: true,
},
},
{
name: "v1 compatible update, existing duplicate usages",
version: schema.GroupVersion{Group: "certificates.k8s.io", Version: "v1"},
name: "compatible update, existing duplicate usages",
oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"any", "any"}}},
want: certificateValidationOptions{
allowDuplicateUsages: true,
@@ -553,7 +495,7 @@ func Test_getValidationOptions(t *testing.T) {
}
for _, tt := range tests {
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)
}
})
@@ -577,7 +519,7 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
name string
newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest
versionErrs map[string][]string
errs []string
}{
{
name: "no-op",
@@ -595,9 +537,8 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`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{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`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}},
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`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{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
}},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
},
},
{
@@ -639,7 +577,7 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{},
errs: []string{},
},
{
name: "remove Failed condition",
@@ -647,9 +585,8 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
},
},
{
@@ -658,21 +595,19 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
Certificate: validCertificate,
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{
"v1": {`status.certificate: Forbidden: updates may not set certificate content`},
"v1beta1": {`status.certificate: Forbidden: updates may not set certificate content`},
errs: []string{
`status.certificate: Forbidden: updates may not set certificate content`,
},
},
}
for _, tt := range tests {
for _, version := range []string{"v1", "v1beta1"} {
t.Run(tt.name+"_"+version, func(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
gotErrs := sets.NewString()
for _, err := range ValidateCertificateSigningRequestUpdate(tt.newCSR, tt.oldCSR, schema.GroupVersion{Group: capi.GroupName, Version: version}) {
for _, err := range ValidateCertificateSigningRequestUpdate(tt.newCSR, tt.oldCSR) {
gotErrs.Insert(err.Error())
}
wantErrs := sets.NewString(tt.versionErrs[version]...)
wantErrs := sets.NewString(tt.errs...)
for _, missing := range wantErrs.Difference(gotErrs).List() {
t.Errorf("missing expected error: %s", missing)
}
@@ -682,7 +617,6 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
})
}
}
}
func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
validUpdateMeta := validObjectMeta
@@ -701,7 +635,7 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
name string
newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest
versionErrs map[string][]string
errs []string
}{
{
name: "no-op",
@@ -732,9 +666,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`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{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`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}},
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`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{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
}},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
},
},
{
@@ -776,7 +706,7 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{},
errs: []string{},
},
{
name: "remove Failed condition",
@@ -784,9 +714,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
},
},
{
@@ -795,7 +724,7 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
Certificate: validCertificate,
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{},
errs: []string{},
},
{
name: "set invalid certificate",
@@ -803,8 +732,8 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
Certificate: invalidCertificateNoPEM,
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{
"v1": {`status.certificate: Invalid value: "<certificate data>": must contain at least one CERTIFICATE PEM block`},
errs: []string{
`status.certificate: Invalid value: "<certificate data>": must contain at least one CERTIFICATE PEM block`,
},
},
{
@@ -815,20 +744,19 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Certificate: invalidCertificateNoPEM,
}},
versionErrs: map[string][]string{
"v1": {`status.certificate: Forbidden: updates may not modify existing certificate content`},
errs: []string{
`status.certificate: Forbidden: updates may not modify existing certificate content`,
},
},
}
for _, tt := range tests {
for _, version := range []string{"v1", "v1beta1"} {
t.Run(tt.name+"_"+version, func(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
gotErrs := sets.NewString()
for _, err := range ValidateCertificateSigningRequestStatusUpdate(tt.newCSR, tt.oldCSR, schema.GroupVersion{Group: capi.GroupName, Version: version}) {
for _, err := range ValidateCertificateSigningRequestStatusUpdate(tt.newCSR, tt.oldCSR) {
gotErrs.Insert(err.Error())
}
wantErrs := sets.NewString(tt.versionErrs[version]...)
wantErrs := sets.NewString(tt.errs...)
for _, missing := range wantErrs.Difference(gotErrs).List() {
t.Errorf("missing expected error: %s", missing)
}
@@ -838,7 +766,6 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
})
}
}
}
func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
validUpdateMeta := validObjectMeta
@@ -857,7 +784,7 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
name string
newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest
versionErrs map[string][]string
errs []string
}{
{
name: "no-op",
@@ -882,9 +809,8 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
}},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`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{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
}},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
},
},
{
@@ -911,7 +836,7 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{},
errs: []string{},
},
{
name: "remove Failed condition",
@@ -919,9 +844,8 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
}},
versionErrs: map[string][]string{
"v1": {`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"`},
errs: []string{
`status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
},
},
{
@@ -930,21 +854,19 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
Certificate: validCertificate,
}},
oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
versionErrs: map[string][]string{
"v1": {`status.certificate: Forbidden: updates may not set certificate content`},
"v1beta1": {`status.certificate: Forbidden: updates may not set certificate content`},
errs: []string{
`status.certificate: Forbidden: updates may not set certificate content`,
},
},
}
for _, tt := range tests {
for _, version := range []string{"v1", "v1beta1"} {
t.Run(tt.name+"_"+version, func(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
gotErrs := sets.NewString()
for _, err := range ValidateCertificateSigningRequestApprovalUpdate(tt.newCSR, tt.oldCSR, schema.GroupVersion{Group: capi.GroupName, Version: version}) {
for _, err := range ValidateCertificateSigningRequestApprovalUpdate(tt.newCSR, tt.oldCSR) {
gotErrs.Insert(err.Error())
}
wantErrs := sets.NewString(tt.versionErrs[version]...)
wantErrs := sets.NewString(tt.errs...)
for _, missing := range wantErrs.Difference(gotErrs).List() {
t.Errorf("missing expected error: %s", missing)
}
@@ -954,7 +876,6 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
})
}
}
}
// Test_validateCertificateSigningRequestOptions verifies validation options are effective in tolerating specific aspects of CSRs
func Test_validateCertificateSigningRequestOptions(t *testing.T) {

View File

@@ -21,12 +21,9 @@ import (
"net"
"strings"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
pathvalidation "k8s.io/apimachinery/pkg/api/validation/path"
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/sets"
"k8s.io/apimachinery/pkg/util/validation"
@@ -230,20 +227,20 @@ type IngressValidationOptions struct {
}
// 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 = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"), opts, requestGV)...)
allErrs = append(allErrs, ValidateIngressSpec(&ingress.Spec, field.NewPath("spec"), opts)...)
return allErrs
}
// 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{}
opts := IngressValidationOptions{
AllowInvalidSecretName: allowInvalidSecretName(requestGV, nil),
AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(requestGV, nil),
AllowInvalidSecretName: false,
AllowInvalidWildcardHostRule: false,
}
allErrs = append(allErrs, validateIngress(ingress, opts, requestGV)...)
allErrs = append(allErrs, validateIngress(ingress, opts)...)
annotationVal, annotationIsSet := ingress.Annotations[annotationIngressClass]
if annotationIsSet && ingress.Spec.IngressClassName != nil {
annotationPath := field.NewPath("annotations").Child(annotationIngressClass)
@@ -253,14 +250,14 @@ func ValidateIngressCreate(ingress *networking.Ingress, requestGV schema.GroupVe
}
// 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"))
opts := IngressValidationOptions{
AllowInvalidSecretName: allowInvalidSecretName(requestGV, oldIngress),
AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(requestGV, oldIngress),
AllowInvalidSecretName: allowInvalidSecretName(oldIngress),
AllowInvalidWildcardHostRule: allowInvalidWildcardHostRule(oldIngress),
}
allErrs = append(allErrs, validateIngress(ingress, opts, requestGV)...)
allErrs = append(allErrs, validateIngress(ingress, opts)...)
return allErrs
}
@@ -291,29 +288,18 @@ func validateIngressTLS(spec *networking.IngressSpec, fldPath *field.Path, opts
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.
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{}
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))
}
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 {
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 {
allErrs = append(allErrs, validateIngressTLS(spec, fldPath.Child("tls"), opts)...)
@@ -333,7 +319,7 @@ func ValidateIngressStatusUpdate(ingress, oldIngress *networking.Ingress) field.
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{}
if len(ingressRules) == 0 {
return append(allErrs, field.Required(fldPath, ""))
@@ -359,32 +345,32 @@ func validateIngressRules(ingressRules []networking.IngressRule, fldPath *field.
}
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
}
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{}
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
}
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{}
if len(httpIngressRuleValue.Paths) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("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
}
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{}
if path.PathType == nil {
@@ -418,45 +404,12 @@ func validateHTTPIngressPath(path *networking.HTTPIngressPath, fldPath *field.Pa
default:
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
}
// 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.
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{}
hasResourceBackend := backend.Resource != nil
@@ -470,10 +423,10 @@ func validateIngressBackend(backend *networking.IngressBackend, fldPath *field.P
case hasServiceBackend:
if len(backend.Service.Name) == 0 {
allErrs = append(allErrs, field.Required(serviceNameFieldPath(fldPath, requestGV), ""))
allErrs = append(allErrs, field.Required(fldPath.Child("service", "name"), ""))
} else {
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"))
} else if hasPortName {
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 {
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 {
allErrs = append(allErrs, field.Required(fldPath, "port name or number is required"))
@@ -616,11 +569,7 @@ func validateIngressClassParametersReference(params *networking.IngressClassPara
return allErrs
}
func allowInvalidSecretName(gv schema.GroupVersion, oldIngress *networking.Ingress) bool {
if gv == networkingv1beta1.SchemeGroupVersion || gv == extensionsv1beta1.SchemeGroupVersion {
// backwards compatibility with released API versions that allowed invalid names
return true
}
func allowInvalidSecretName(oldIngress *networking.Ingress) bool {
if oldIngress != nil {
for _, tls := range oldIngress.Spec.TLS {
if len(validateTLSSecretName(tls.SecretName)) > 0 {
@@ -639,14 +588,10 @@ func validateTLSSecretName(name string) []string {
return apivalidation.ValidateSecretName(name, false)
}
func allowInvalidWildcardHostRule(gv schema.GroupVersion, oldIngress *networking.Ingress) bool {
if gv == networkingv1beta1.SchemeGroupVersion || gv == extensionsv1beta1.SchemeGroupVersion {
// backwards compatibility with released API versions that allowed invalid rules
return true
}
func allowInvalidWildcardHostRule(oldIngress *networking.Ingress) bool {
if oldIngress != nil {
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
return true
}

View File

@@ -21,11 +21,8 @@ import (
"strings"
"testing"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
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/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
@@ -514,7 +511,6 @@ func TestValidateIngress(t *testing.T) {
}
testCases := map[string]struct {
groupVersion *schema.GroupVersion
tweakIngress func(ing *networking.Ingress)
expectErrsOnFields []string
}{
@@ -531,13 +527,12 @@ func TestValidateIngress(t *testing.T) {
expectErrsOnFields: []string{},
},
// invalid use cases
"backend (v1beta1) with no service": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
"backend with no service": {
tweakIngress: func(ing *networking.Ingress) {
ing.Spec.DefaultBackend.Service.Name = ""
},
expectErrsOnFields: []string{
"spec.backend.serviceName",
"spec.defaultBackend.service.name",
},
},
"invalid path type": {
@@ -654,7 +649,6 @@ func TestValidateIngress(t *testing.T) {
},
},
"spec.backend resource and service name are not allowed together": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ing *networking.Ingress) {
ing.Spec.DefaultBackend = &networking.IngressBackend{
Service: serviceBackend,
@@ -666,11 +660,10 @@ func TestValidateIngress(t *testing.T) {
}
},
expectErrsOnFields: []string{
"spec.backend",
"spec.defaultBackend",
},
},
"spec.backend resource and service port are not allowed together": {
groupVersion: &networkingv1beta1.SchemeGroupVersion,
tweakIngress: func(ing *networking.Ingress) {
ing.Spec.DefaultBackend = &networking.IngressBackend{
Service: serviceBackend,
@@ -682,7 +675,7 @@ func TestValidateIngress(t *testing.T) {
}
},
expectErrsOnFields: []string{
"spec.backend",
"spec.defaultBackend",
},
},
}
@@ -691,11 +684,7 @@ func TestValidateIngress(t *testing.T) {
t.Run(name, func(t *testing.T) {
ingress := baseIngress.DeepCopy()
testCase.tweakIngress(ingress)
gv := testCase.groupVersion
if gv == nil {
gv = &networkingv1.SchemeGroupVersion
}
errs := validateIngress(ingress, IngressValidationOptions{}, *gv)
errs := validateIngress(ingress, IngressValidationOptions{})
if len(testCase.expectErrsOnFields) != len(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")
testCases := map[string]struct {
groupVersion *schema.GroupVersion
pathType networking.PathType
path string
expectedErrs field.ErrorList
@@ -820,11 +808,7 @@ func TestValidateIngressRuleValue(t *testing.T) {
},
},
}
gv := testCase.groupVersion
if gv == nil {
gv = &networkingv1.SchemeGroupVersion
}
errs := validateIngressRuleValue(irv, field.NewPath("testing"), IngressValidationOptions{}, *gv)
errs := validateIngressRuleValue(irv, field.NewPath("testing"), IngressValidationOptions{})
if len(errs) != len(testCase.expectedErrs) {
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 {
groupVersion *schema.GroupVersion
tweakIngress func(ingress *networking.Ingress)
expectedErrs field.ErrorList
}{
@@ -950,33 +933,18 @@ func TestValidateIngressCreate(t *testing.T) {
},
expectedErrs: field.ErrorList{},
},
"v1: valid secret": {
groupVersion: &networkingv1.SchemeGroupVersion,
"valid secret": {
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
},
},
"v1: invalid secret": {
groupVersion: &networkingv1.SchemeGroupVersion,
"invalid secret": {
tweakIngress: func(ingress *networking.Ingress) {
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])?)*')`)},
},
"v1beta1: valid secret": {
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,
"valid rules with wildcard host": {
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
ingress.Spec.Rules = []networking.IngressRule{{
@@ -993,8 +961,7 @@ func TestValidateIngressCreate(t *testing.T) {
}}
},
},
"v1: invalid rules with wildcard host": {
groupVersion: &networkingv1.SchemeGroupVersion,
"invalid rules with wildcard host": {
tweakIngress: func(ingress *networking.Ingress) {
ingress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
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`)},
},
"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 {
t.Run(name, func(t *testing.T) {
newIngress := baseIngress.DeepCopy()
testCase.tweakIngress(newIngress)
gv := testCase.groupVersion
if gv == nil {
gv = &networkingv1.SchemeGroupVersion
}
errs := ValidateIngressCreate(newIngress, *gv)
errs := ValidateIngressCreate(newIngress)
if len(errs) != len(testCase.expectedErrs) {
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 {
gv schema.GroupVersion
tweakIngresses func(newIngress, oldIngress *networking.Ingress)
expectedErrs field.ErrorList
}{
@@ -1385,37 +1311,20 @@ func TestValidateIngressUpdate(t *testing.T) {
},
expectedErrs: field.ErrorList{},
},
"v1: change valid secret -> invalid secret": {
gv: networkingv1.SchemeGroupVersion,
"change valid secret -> invalid secret": {
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "valid"}}
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])?)*')`)},
},
"v1: change invalid secret -> invalid secret": {
gv: networkingv1.SchemeGroupVersion,
"change invalid secret -> invalid secret": {
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 1"}}
newIngress.Spec.TLS = []networking.IngressTLS{{SecretName: "invalid name 2"}}
},
},
"v1beta1: change valid secret -> invalid secret": {
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,
"change valid rules with wildcard host -> invalid rules": {
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
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`)},
},
"v1: 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,
"change invalid rules with wildcard host -> invalid rules": {
tweakIngresses: func(newIngress, oldIngress *networking.Ingress) {
oldIngress.Spec.TLS = []networking.IngressTLS{{Hosts: []string{"*.bar.com"}}}
oldIngress.Spec.Rules = []networking.IngressRule{{
@@ -1547,11 +1393,7 @@ func TestValidateIngressUpdate(t *testing.T) {
oldIngress := baseIngress.DeepCopy()
testCase.tweakIngresses(newIngress, oldIngress)
gv := testCase.gv
if gv.Empty() {
gv = networkingv1beta1.SchemeGroupVersion
}
errs := ValidateIngressUpdate(newIngress, oldIngress, gv)
errs := ValidateIngressUpdate(newIngress, oldIngress)
if len(errs) != len(testCase.expectedErrs) {
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
for k, v := range errorCases {
errs := validateIngress(&v, IngressValidationOptions{}, networkingv1beta1.SchemeGroupVersion)
errs := validateIngress(&v, IngressValidationOptions{})
if len(errs) == 0 {
t.Errorf("expected failure for %q", k)
} else {
@@ -1885,7 +1727,7 @@ func TestValidateIngressTLS(t *testing.T) {
}
validCases[fmt.Sprintf("spec.tls[0].hosts: Valid value: '%v'", wildHost)] = goodWildcardTLS
for k, v := range validCases {
errs := validateIngress(&v, IngressValidationOptions{}, networkingv1beta1.SchemeGroupVersion)
errs := validateIngress(&v, IngressValidationOptions{})
if len(errs) != 0 {
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
for k, v := range validCases {
errs := validateIngress(&v, IngressValidationOptions{}, networkingv1beta1.SchemeGroupVersion)
errs := validateIngress(&v, IngressValidationOptions{})
if len(errs) != 0 {
t.Errorf("expected success for %q", k)
}

View File

@@ -26,42 +26,31 @@ import (
"time"
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
apiserverinternalv1alpha1 "k8s.io/api/apiserverinternal/v1alpha1"
appsv1 "k8s.io/api/apps/v1"
authenticationv1 "k8s.io/api/authentication/v1"
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
authorizationapiv1 "k8s.io/api/authorization/v1"
authorizationapiv1beta1 "k8s.io/api/authorization/v1beta1"
autoscalingapiv1 "k8s.io/api/autoscaling/v1"
autoscalingapiv2beta1 "k8s.io/api/autoscaling/v2beta1"
autoscalingapiv2beta2 "k8s.io/api/autoscaling/v2beta2"
batchapiv1 "k8s.io/api/batch/v1"
batchapiv1beta1 "k8s.io/api/batch/v1beta1"
certificatesapiv1 "k8s.io/api/certificates/v1"
certificatesapiv1beta1 "k8s.io/api/certificates/v1beta1"
coordinationapiv1 "k8s.io/api/coordination/v1"
coordinationapiv1beta1 "k8s.io/api/coordination/v1beta1"
apiv1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
eventsv1 "k8s.io/api/events/v1"
eventsv1beta1 "k8s.io/api/events/v1beta1"
extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1"
flowcontrolv1alpha1 "k8s.io/api/flowcontrol/v1alpha1"
networkingapiv1 "k8s.io/api/networking/v1"
networkingapiv1beta1 "k8s.io/api/networking/v1beta1"
nodev1 "k8s.io/api/node/v1"
nodev1alpha1 "k8s.io/api/node/v1alpha1"
nodev1beta1 "k8s.io/api/node/v1beta1"
policyapiv1 "k8s.io/api/policy/v1"
policyapiv1beta1 "k8s.io/api/policy/v1beta1"
rbacv1 "k8s.io/api/rbac/v1"
rbacv1alpha1 "k8s.io/api/rbac/v1alpha1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
schedulingapiv1 "k8s.io/api/scheduling/v1"
schedulingv1alpha1 "k8s.io/api/scheduling/v1alpha1"
schedulingapiv1beta1 "k8s.io/api/scheduling/v1beta1"
storageapiv1 "k8s.io/api/storage/v1"
storageapiv1alpha1 "k8s.io/api/storage/v1alpha1"
storageapiv1beta1 "k8s.io/api/storage/v1beta1"
@@ -108,7 +97,6 @@ import (
corerest "k8s.io/kubernetes/pkg/registry/core/rest"
discoveryrest "k8s.io/kubernetes/pkg/registry/discovery/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"
networkingrest "k8s.io/kubernetes/pkg/registry/networking/rest"
noderest "k8s.io/kubernetes/pkg/registry/node/rest"
@@ -428,7 +416,6 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
certificatesrest.RESTStorageProvider{},
coordinationrest.RESTStorageProvider{},
discoveryrest.StorageProvider{},
extensionsrest.RESTStorageProvider{},
networkingrest.RESTStorageProvider{},
noderest.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.
ret.EnableVersions(
admissionregistrationv1.SchemeGroupVersion,
admissionregistrationv1beta1.SchemeGroupVersion,
apiv1.SchemeGroupVersion,
appsv1.SchemeGroupVersion,
authenticationv1.SchemeGroupVersion,
authenticationv1beta1.SchemeGroupVersion,
authorizationapiv1.SchemeGroupVersion,
authorizationapiv1beta1.SchemeGroupVersion,
autoscalingapiv1.SchemeGroupVersion,
autoscalingapiv2beta1.SchemeGroupVersion,
autoscalingapiv2beta2.SchemeGroupVersion,
batchapiv1.SchemeGroupVersion,
batchapiv1beta1.SchemeGroupVersion,
certificatesapiv1.SchemeGroupVersion,
certificatesapiv1beta1.SchemeGroupVersion,
coordinationapiv1.SchemeGroupVersion,
coordinationapiv1beta1.SchemeGroupVersion,
discoveryv1.SchemeGroupVersion,
discoveryv1beta1.SchemeGroupVersion,
eventsv1.SchemeGroupVersion,
eventsv1beta1.SchemeGroupVersion,
extensionsapiv1beta1.SchemeGroupVersion,
networkingapiv1.SchemeGroupVersion,
networkingapiv1beta1.SchemeGroupVersion,
nodev1.SchemeGroupVersion,
nodev1beta1.SchemeGroupVersion,
policyapiv1.SchemeGroupVersion,
policyapiv1beta1.SchemeGroupVersion,
rbacv1.SchemeGroupVersion,
rbacv1beta1.SchemeGroupVersion,
storageapiv1.SchemeGroupVersion,
storageapiv1beta1.SchemeGroupVersion,
schedulingapiv1beta1.SchemeGroupVersion,
schedulingapiv1.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
ret.DisableVersions(
apiserverinternalv1alpha1.SchemeGroupVersion,
nodev1alpha1.SchemeGroupVersion,
rbacv1alpha1.SchemeGroupVersion,
schedulingv1alpha1.SchemeGroupVersion,
storageapiv1alpha1.SchemeGroupVersion,
flowcontrolv1alpha1.SchemeGroupVersion,
)

View File

@@ -31,12 +31,6 @@ var NoStorageVersionHash = sets.NewString(
"authorization.k8s.io/v1/selfsubjectaccessreviews",
"authorization.k8s.io/v1/selfsubjectrulesreviews",
"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
@@ -64,15 +58,10 @@ var GVRToStorageVersionHash = map[string]string{
"batch/v1/cronjobs": "h/JlFAZkyyY=",
"batch/v1beta1/cronjobs": "h/JlFAZkyyY=",
"certificates.k8s.io/v1/certificatesigningrequests": "95fRKMXA+00=",
"certificates.k8s.io/v1beta1/certificatesigningrequests": "95fRKMXA+00=",
"coordination.k8s.io/v1beta1/leases": "gqkMMb/YqFM=",
"coordination.k8s.io/v1/leases": "gqkMMb/YqFM=",
"discovery.k8s.io/v1/endpointslices": "Nx3SIv6I0mE=",
"discovery.k8s.io/v1beta1/endpointslices": "Nx3SIv6I0mE=",
"extensions/v1beta1/ingresses": "ZOAfGflaKd0=",
"networking.k8s.io/v1/networkpolicies": "YpfwF18m1G8=",
"networking.k8s.io/v1beta1/ingresses": "ZOAfGflaKd0=",
"networking.k8s.io/v1beta1/ingressclasses": "l/iqIbDgFyQ=",
"networking.k8s.io/v1/ingresses": "ZOAfGflaKd0=",
"networking.k8s.io/v1/ingressclasses": "l/iqIbDgFyQ=",
"node.k8s.io/v1/runtimeclasses": "WQTu1GL3T2Q=",
@@ -84,28 +73,17 @@ var GVRToStorageVersionHash = map[string]string{
"rbac.authorization.k8s.io/v1/clusterroles": "bYE5ZWDrJ44=",
"rbac.authorization.k8s.io/v1/rolebindings": "eGsCzGH6b1g=",
"rbac.authorization.k8s.io/v1/roles": "7FuwZcIIItM=",
"rbac.authorization.k8s.io/v1beta1/clusterrolebindings": "48tpQ8gZHFc=",
"rbac.authorization.k8s.io/v1beta1/clusterroles": "bYE5ZWDrJ44=",
"rbac.authorization.k8s.io/v1beta1/rolebindings": "eGsCzGH6b1g=",
"rbac.authorization.k8s.io/v1beta1/roles": "7FuwZcIIItM=",
"scheduling.k8s.io/v1beta1/priorityclasses": "1QwjyaZjj3Y=",
"scheduling.k8s.io/v1/priorityclasses": "1QwjyaZjj3Y=",
"storage.k8s.io/v1/csidrivers": "hL6j/rwBV5w=",
"storage.k8s.io/v1/csinodes": "Pe62DkZtjuo=",
"storage.k8s.io/v1/storageclasses": "K+m6uJwbjGY=",
"storage.k8s.io/v1/volumeattachments": "vQAqD28V4AY=",
"storage.k8s.io/v1beta1/csidrivers": "hL6j/rwBV5w=",
"storage.k8s.io/v1beta1/csinodes": "Pe62DkZtjuo=",
"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=",

View File

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

View File

@@ -18,7 +18,6 @@ package rest
import (
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
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.
// 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 storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
@@ -53,25 +45,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
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) {
storage := map[string]rest.Storage{}
// validatingwebhookconfigurations

View File

@@ -21,9 +21,7 @@ import (
"reflect"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/admissionregistration"
@@ -65,12 +63,7 @@ func (validatingWebhookConfigurationStrategy) PrepareForUpdate(ctx context.Conte
// Validate validates a new validatingWebhookConfiguration.
func (validatingWebhookConfigurationStrategy) 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}
}
return validation.ValidateValidatingWebhookConfiguration(obj.(*admissionregistration.ValidatingWebhookConfiguration), groupVersion)
return validation.ValidateValidatingWebhookConfiguration(obj.(*admissionregistration.ValidatingWebhookConfiguration))
}
// 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.
func (validatingWebhookConfigurationStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
var groupVersion schema.GroupVersion
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)
return validation.ValidateValidatingWebhookConfigurationUpdate(obj.(*admissionregistration.ValidatingWebhookConfiguration), old.(*admissionregistration.ValidatingWebhookConfiguration))
}
// WarningsOnUpdate returns warnings for the given update.

View File

@@ -19,7 +19,6 @@ package daemonset
import (
"context"
appsv1beta2 "k8s.io/api/apps/v1beta2"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apivalidation "k8s.io/apimachinery/pkg/api/validation"
@@ -48,21 +47,13 @@ type daemonSetStrategy struct {
// Strategy is the default logic that applies when creating and updating DaemonSet objects.
var Strategy = daemonSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns OrphanDependents for extensions/v1beta1 and apps/v1beta2 for backwards compatibility,
// and DeleteDependents for all other versions.
// Make sure we correctly implement the interface.
var _ = rest.GarbageCollectionDeleteStrategy(Strategy)
// DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions.
func (daemonSetStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
var groupVersion schema.GroupVersion
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.
func (daemonSetStrategy) NamespaceScoped() bool {

View File

@@ -25,7 +25,6 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/kubernetes/pkg/apis/apps"
@@ -40,60 +39,6 @@ const (
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) {
tests := []struct {
requestInfo genericapirequest.RequestInfo

View File

@@ -20,7 +20,6 @@ import (
"context"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apivalidation "k8s.io/apimachinery/pkg/api/validation"
@@ -47,21 +46,13 @@ type deploymentStrategy struct {
// objects via the REST API.
var Strategy = deploymentStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns OrphanDependents for extensions/v1beta1, apps/v1beta1, and apps/v1beta2 for backwards compatibility,
// and DeleteDependents for all other versions.
// Make sure we correctly implement the interface.
var _ = rest.GarbageCollectionDeleteStrategy(Strategy)
// DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions.
func (deploymentStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
var groupVersion schema.GroupVersion
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.
func (deploymentStrategy) NamespaceScoped() bool {

View File

@@ -26,7 +26,6 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/apps"
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 {
return &apps.Deployment{
ObjectMeta: metav1.ObjectMeta{

View File

@@ -23,7 +23,6 @@ import (
"fmt"
"strconv"
appsv1beta2 "k8s.io/api/apps/v1beta2"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
apivalidation "k8s.io/apimachinery/pkg/api/validation"
@@ -53,21 +52,13 @@ type rsStrategy struct {
// Strategy is the default logic that applies when creating and updating ReplicaSet objects.
var Strategy = rsStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns OrphanDependents for extensions/v1beta1 and apps/v1beta2 for backwards compatibility,
// and DeleteDependents for all other versions.
// Make sure we correctly implement the interface.
var _ = rest.GarbageCollectionDeleteStrategy(Strategy)
// DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions.
func (rsStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
var groupVersion schema.GroupVersion
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.
func (rsStrategy) NamespaceScoped() bool {

View File

@@ -23,7 +23,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/apps"
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"
"k8s.io/kubernetes/pkg/features"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
@@ -46,21 +42,13 @@ type statefulSetStrategy struct {
// Strategy is the default logic that applies when creating and updating Replication StatefulSet objects.
var Strategy = statefulSetStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// DefaultGarbageCollectionPolicy returns OrphanDependents for apps/v1beta1 and apps/v1beta2 for backwards compatibility,
// and DeleteDependents for all other versions.
// Make sure we correctly implement the interface.
var _ = rest.GarbageCollectionDeleteStrategy(Strategy)
// DefaultGarbageCollectionPolicy returns DeleteDependents for all currently served versions.
func (statefulSetStrategy) DefaultGarbageCollectionPolicy(ctx context.Context) rest.GarbageCollectionPolicy {
var groupVersion schema.GroupVersion
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.
func (statefulSetStrategy) NamespaceScoped() bool {

View File

@@ -23,7 +23,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"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) {
ctx := genericapirequest.NewDefaultContext()
if !StatusStrategy.NamespaceScoped() {

View File

@@ -18,7 +18,6 @@ package rest
import (
authenticationv1 "k8s.io/api/authentication/v1"
authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/registry/generic"
"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.
// 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) {
apiGroupInfo.VersionedResourcesStorageMap[authenticationv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
}
@@ -54,15 +50,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
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 {
storage := map[string]rest.Storage{}
// tokenreviews

View File

@@ -18,7 +18,6 @@ package rest
import (
authorizationv1 "k8s.io/api/authorization/v1"
authorizationv1beta1 "k8s.io/api/authorization/v1beta1"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/generic"
"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.
// 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) {
apiGroupInfo.VersionedResourcesStorageMap[authorizationv1.SchemeGroupVersion.Version] = p.v1Storage(apiResourceConfigSource, restOptionsGetter)
}
@@ -57,20 +52,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
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 {
storage := map[string]rest.Storage{}
// subjectaccessreviews

View File

@@ -20,12 +20,10 @@ import (
"context"
"fmt"
certificatesv1beta1 "k8s.io/api/certificates/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"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.
func (csrStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
csr := obj.(*certificates.CertificateSigningRequest)
return validation.ValidateCertificateSigningRequestCreate(csr, requestGroupVersion(ctx))
return validation.ValidateCertificateSigningRequestCreate(csr)
}
// 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 {
oldCSR := old.(*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.
@@ -181,20 +179,11 @@ func (csrStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.
// Updating /status should not modify spec
newCSR.Spec = oldCSR.Spec
switch requestGroupVersion(ctx) {
case certificatesv1beta1.SchemeGroupVersion:
// Specifically preserve existing Approved/Denied conditions.
// If we cannot (if the status update attempted to add/remove Approved/Denied conditions), revert to old conditions for backwards compatibility.
if !preserveConditionInstances(newCSR, oldCSR, certificates.CertificateApproved) || !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)
}
@@ -255,7 +244,7 @@ func populateConditionTimestamps(newCSR, oldCSR *certificates.CertificateSigning
}
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.
@@ -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 {
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.
@@ -332,11 +321,3 @@ func SelectableFields(obj *certificates.CertificateSigningRequest) fields.Set {
}
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

@@ -177,16 +177,13 @@ func TestStatusUpdate(t *testing.T) {
name string
newObj *certapi.CertificateSigningRequest
oldObj *certapi.CertificateSigningRequest
expectedObjs map[string]*certapi.CertificateSigningRequest
expectedObj *certapi.CertificateSigningRequest
}{
{
name: "no-op",
newObj: &certapi.CertificateSigningRequest{},
oldObj: &certapi.CertificateSigningRequest{},
expectedObjs: map[string]*certapi.CertificateSigningRequest{
"v1": {},
"v1beta1": {},
},
expectedObj: &certapi.CertificateSigningRequest{},
},
{
name: "adding failed condition to existing approved/denied conditions",
@@ -211,9 +208,8 @@ func TestStatusUpdate(t *testing.T) {
},
},
},
expectedObjs: map[string]*certapi.CertificateSigningRequest{
expectedObj: &certapi.CertificateSigningRequest{
// preserve existing Approved/Denied conditions
"v1": {
Status: certapi.CertificateSigningRequestStatus{
Conditions: []certapi.CertificateSigningRequestCondition{
{Type: certapi.CertificateFailed, LastUpdateTime: now, LastTransitionTime: now},
@@ -224,19 +220,6 @@ func TestStatusUpdate(t *testing.T) {
},
},
},
// 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"},
},
},
},
},
},
{
name: "add approved condition",
@@ -248,33 +231,24 @@ func TestStatusUpdate(t *testing.T) {
},
},
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)
"v1": {
Status: certapi.CertificateSigningRequestStatus{
Conditions: []certapi.CertificateSigningRequestCondition{
{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 _, version := range []string{"v1", "v1beta1"} {
t.Run(tt.name+"_"+version, func(t *testing.T) {
ctx := genericapirequest.WithRequestInfo(context.TODO(), &genericapirequest.RequestInfo{APIGroup: "certificates.k8s.io", APIVersion: version})
t.Run(tt.name, func(t *testing.T) {
obj := tt.newObj.DeepCopy()
StatusStrategy.PrepareForUpdate(ctx, obj, tt.oldObj.DeepCopy())
if !reflect.DeepEqual(obj, tt.expectedObjs[version]) {
t.Errorf("object diff: %s", diff.ObjectDiff(obj, tt.expectedObjs[version]))
StatusStrategy.PrepareForUpdate(context.TODO(), obj, tt.oldObj.DeepCopy())
if !reflect.DeepEqual(obj, tt.expectedObj) {
t.Errorf("object diff: %s", diff.ObjectDiff(obj, tt.expectedObj))
}
})
}
}
}

View File

@@ -18,7 +18,6 @@ package rest
import (
certificatesapiv1 "k8s.io/api/certificates/v1"
certificatesapiv1beta1 "k8s.io/api/certificates/v1beta1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
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.
// 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) {
storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter)
if err != nil {
@@ -54,20 +45,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
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) {
storage := map[string]rest.Storage{}
// certificatesigningrequests

View File

@@ -18,7 +18,6 @@ package rest
import (
coordinationv1 "k8s.io/api/coordination/v1"
coordinationv1beta1 "k8s.io/api/coordination/v1beta1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
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.
// 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 storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
@@ -52,18 +44,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
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) {
storage := map[string]rest.Storage{}
// 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"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"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.
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)
return validation.ValidateIngressCreate(ingress, requestGV)
return validation.ValidateIngressCreate(ingress)
}
// WarningsOnCreate returns warnings for the creation of the given object.
@@ -112,11 +106,7 @@ func (ingressStrategy) AllowCreateOnUpdate() bool {
// ValidateUpdate validates ingresses on update.
func (ingressStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
var requestGV schema.GroupVersion
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)
return validation.ValidateIngressUpdate(obj.(*networking.Ingress), old.(*networking.Ingress))
}
// WarningsOnUpdate returns warnings for the given update.

View File

@@ -18,7 +18,6 @@ package rest
import (
networkingapiv1 "k8s.io/api/networking/v1"
networkingapiv1beta1 "k8s.io/api/networking/v1beta1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
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
}
@@ -83,26 +74,6 @@ func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.API
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 {
return networking.GroupName
}

View File

@@ -24,8 +24,6 @@ import (
"k8s.io/klog/v2"
rbacapiv1 "k8s.io/api/rbac/v1"
rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1"
rbacapiv1beta1 "k8s.io/api/rbac/v1beta1"
"k8s.io/apimachinery/pkg/api/errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
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.
// 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 storageMap, err := p.storage(rbacapiv1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err

View File

@@ -35,8 +35,6 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/scheduling"
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"
)
@@ -49,20 +47,6 @@ var _ genericapiserver.PostStartHookProvider = RESTStorageProvider{}
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)
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 storage, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, false, err
@@ -73,29 +57,6 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
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) {
storage := map[string]rest.Storage{}
// 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) {
storage := map[string]rest.Storage{}
// volumeattachments
volumeAttachmentStorage, err := volumeattachmentstore.NewStorage(restOptionsGetter)
if err != nil {
return storage, err
}
storage["volumeattachments"] = volumeAttachmentStorage.VolumeAttachment
// register csistoragecapacities
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) {
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
csiStorageStorage, err := csistoragecapacitystore.NewStorage(restOptionsGetter)

View File

@@ -19,12 +19,9 @@ package volumeattachment
import (
"context"
storageapiv1beta1 "k8s.io/api/storage/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"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.
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)
switch groupVersion {
case storageapiv1beta1.SchemeGroupVersion:
// allow modification of status for v1beta1
default:
volumeAttachment.Status = storage.VolumeAttachmentStatus{}
}
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) {
volumeAttachment.Spec.Source.InlineVolumeSpec = nil
@@ -88,19 +73,8 @@ func (volumeAttachmentStrategy) Validate(ctx context.Context, obj runtime.Object
errs := validation.ValidateVolumeAttachment(volumeAttachment)
var groupVersion schema.GroupVersion
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
}
@@ -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
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)
oldVolumeAttachment := old.(*storage.VolumeAttachment)
switch groupVersion {
case storageapiv1beta1.SchemeGroupVersion:
// 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 {
newVolumeAttachment.Spec.Source.InlineVolumeSpec = nil

View File

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

View File

@@ -27,7 +27,6 @@ import (
structuraldefaulting "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting"
apiequality "k8s.io/apimachinery/pkg/api/equality"
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
@@ -47,7 +46,7 @@ var (
)
// 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 {
ret := genericvalidation.NameIsDNSSubdomain(name, prefix)
requiredName := obj.Spec.Names.Plural + "." + obj.Spec.Group
@@ -57,15 +56,13 @@ func ValidateCustomResourceDefinition(obj *apiextensions.CustomResourceDefinitio
return ret
}
allowDefaults, rejectDefaultsReason := allowDefaults(requestGV, nil)
opts := validationOptions{
allowDefaults: allowDefaults,
disallowDefaultsReason: rejectDefaultsReason,
allowDefaults: true,
requireRecognizedConversionReviewVersion: true,
requireImmutableNames: false,
requireOpenAPISchema: requireOpenAPISchema(requestGV, nil),
requireValidPropertyType: requireValidPropertyType(requestGV, nil),
requireStructuralSchema: requireStructuralSchema(requestGV, nil),
requireOpenAPISchema: true,
requireValidPropertyType: true,
requireStructuralSchema: true,
requirePrunedDefaults: true,
requireAtomicSetType: 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, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
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, validatePreserveUnknownFields(obj, nil, requestGV)...)
allErrs = append(allErrs, validateAPIApproval(obj, nil)...)
allErrs = append(allErrs, validatePreserveUnknownFields(obj, nil)...)
return allErrs
}
@@ -107,16 +104,14 @@ type validationOptions struct {
}
// ValidateCustomResourceDefinitionUpdate statically validates
func ValidateCustomResourceDefinitionUpdate(obj, oldObj *apiextensions.CustomResourceDefinition, requestGV schema.GroupVersion) field.ErrorList {
allowDefaults, rejectDefaultsReason := allowDefaults(requestGV, &oldObj.Spec)
func ValidateCustomResourceDefinitionUpdate(obj, oldObj *apiextensions.CustomResourceDefinition) field.ErrorList {
opts := validationOptions{
allowDefaults: allowDefaults,
disallowDefaultsReason: rejectDefaultsReason,
allowDefaults: true,
requireRecognizedConversionReviewVersion: oldObj.Spec.Conversion == nil || hasValidConversionReviewVersionOrEmpty(oldObj.Spec.Conversion.ConversionReviewVersions),
requireImmutableNames: apiextensions.IsCRDConditionTrue(oldObj, apiextensions.Established),
requireOpenAPISchema: requireOpenAPISchema(requestGV, &oldObj.Spec),
requireValidPropertyType: requireValidPropertyType(requestGV, &oldObj.Spec),
requireStructuralSchema: requireStructuralSchema(requestGV, &oldObj.Spec),
requireOpenAPISchema: requireOpenAPISchema(&oldObj.Spec),
requireValidPropertyType: requireValidPropertyType(&oldObj.Spec),
requireStructuralSchema: requireStructuralSchema(&oldObj.Spec),
requirePrunedDefaults: requirePrunedDefaults(&oldObj.Spec),
requireAtomicSetType: requireAtomicSetType(&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, ValidateCustomResourceDefinitionStatus(&obj.Status, field.NewPath("status"))...)
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, validatePreserveUnknownFields(obj, oldObj, requestGV)...)
allErrs = append(allErrs, validateAPIApproval(obj, oldObj)...)
allErrs = append(allErrs, validatePreserveUnknownFields(obj, oldObj)...)
return allErrs
}
@@ -1129,11 +1124,7 @@ func allowedAtRootSchema(field string) bool {
}
// requireOpenAPISchema returns true if the request group version requires a schema
func requireOpenAPISchema(requestGV schema.GroupVersion, oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool {
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// for backwards compatibility
return false
}
func requireOpenAPISchema(oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool {
if oldCRDSpec != nil && !allVersionsSpecifyOpenAPISchema(oldCRDSpec) {
// don't tighten validation on existing persisted data
return false
@@ -1152,19 +1143,6 @@ func allVersionsSpecifyOpenAPISchema(spec *apiextensions.CustomResourceDefinitio
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 {
return hasSchemaWith(spec, schemaHasDefaults)
}
@@ -1277,11 +1255,7 @@ func schemaHasKubernetesExtensions(s *apiextensions.JSONSchemaProps) bool {
}
// requireStructuralSchema returns true if schemas specified must be structural
func requireStructuralSchema(requestGV schema.GroupVersion, oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool {
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// for compatibility
return false
}
func requireStructuralSchema(oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool {
if oldCRDSpec != nil && specHasNonStructuralSchema(oldCRDSpec) {
// don't tighten validation on existing persisted data
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
func requireValidPropertyType(requestGV schema.GroupVersion, oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool {
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// for compatibility
return false
}
func requireValidPropertyType(oldCRDSpec *apiextensions.CustomResourceDefinitionSpec) bool {
if oldCRDSpec != nil && specHasInvalidTypes(oldCRDSpec) {
// don't tighten validation on existing persisted data
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
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.
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// no-op for compatibility with v1beta1
return nil
}
if !apihelpers.IsProtectedCommunityGroup(newCRD.Spec.Group) {
// no-op for non-protected groups
return nil
@@ -1433,12 +1398,7 @@ func validateAPIApproval(newCRD, oldCRD *apiextensions.CustomResourceDefinition,
}
}
func validatePreserveUnknownFields(crd, oldCRD *apiextensions.CustomResourceDefinition, requestGV schema.GroupVersion) field.ErrorList {
if requestGV == apiextensionsv1beta1.SchemeGroupVersion {
// no-op for compatibility with v1beta1
return nil
}
func validatePreserveUnknownFields(crd, oldCRD *apiextensions.CustomResourceDefinition) field.ErrorList {
if oldCRD != nil && oldCRD.Spec.PreserveUnknownFields != nil && *oldCRD.Spec.PreserveUnknownFields {
// no-op for compatibility with existing data
return nil

View File

@@ -137,26 +137,8 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
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
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) {
storage := map[string]rest.Storage{}
// customresourcedefinitions

View File

@@ -27,9 +27,7 @@ import (
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage"
"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.
func (strategy) 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}
}
return validation.ValidateCustomResourceDefinition(obj.(*apiextensions.CustomResourceDefinition), groupVersion)
return validation.ValidateCustomResourceDefinition(obj.(*apiextensions.CustomResourceDefinition))
}
// 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.
func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
var groupVersion schema.GroupVersion
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)
return validation.ValidateCustomResourceDefinitionUpdate(obj.(*apiextensions.CustomResourceDefinition), old.(*apiextensions.CustomResourceDefinition))
}
// 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/validation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/utils/pointer"
)
@@ -43,29 +42,19 @@ func TestValidateAPIApproval(t *testing.T) {
tests := []struct {
name string
version string
group string
annotationValue string
oldAnnotationValue *string
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",
version: "v1",
group: "other.io",
annotationValue: "invalid",
validateError: okFn,
},
{
name: "invalid annotation create",
version: "v1",
group: "sigs.k8s.io",
annotationValue: "invalid",
validateError: func(t *testing.T, errors field.ErrorList) {
@@ -80,7 +69,6 @@ func TestValidateAPIApproval(t *testing.T) {
},
{
name: "invalid annotation update",
version: "v1",
group: "sigs.k8s.io",
annotationValue: "invalid",
oldAnnotationValue: strPtr("invalid"),
@@ -88,7 +76,6 @@ func TestValidateAPIApproval(t *testing.T) {
},
{
name: "invalid annotation to missing",
version: "v1",
group: "sigs.k8s.io",
annotationValue: "",
oldAnnotationValue: strPtr("invalid"),
@@ -104,7 +91,6 @@ func TestValidateAPIApproval(t *testing.T) {
},
{
name: "missing to invalid annotation",
version: "v1",
group: "sigs.k8s.io",
annotationValue: "invalid",
oldAnnotationValue: strPtr(""),
@@ -120,7 +106,6 @@ func TestValidateAPIApproval(t *testing.T) {
},
{
name: "missing annotation",
version: "v1",
group: "sigs.k8s.io",
annotationValue: "",
validateError: func(t *testing.T, errors field.ErrorList) {
@@ -135,7 +120,6 @@ func TestValidateAPIApproval(t *testing.T) {
},
{
name: "missing annotation update",
version: "v1",
group: "sigs.k8s.io",
annotationValue: "",
oldAnnotationValue: strPtr(""),
@@ -143,33 +127,16 @@ func TestValidateAPIApproval(t *testing.T) {
},
{
name: "url",
version: "v1",
group: "sigs.k8s.io",
annotationValue: "https://github.com/kubernetes/kubernetes/pull/79724",
validateError: okFn,
},
{
name: "unapproved",
version: "v1",
group: "sigs.k8s.io",
annotationValue: "unapproved, other reason",
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 {
@@ -212,9 +179,9 @@ func TestValidateAPIApproval(t *testing.T) {
var actual field.ErrorList
if oldCRD == nil {
actual = validation.ValidateCustomResourceDefinition(crd, schema.GroupVersion{Group: "apiextensions.k8s.io", Version: test.version})
actual = validation.ValidateCustomResourceDefinition(crd)
} else {
actual = validation.ValidateCustomResourceDefinitionUpdate(crd, oldCRD, schema.GroupVersion{Group: "apiextensions.k8s.io", Version: test.version})
actual = validation.ValidateCustomResourceDefinitionUpdate(crd, oldCRD)
}
test.validateError(t, actual)
})

View File

@@ -25,7 +25,6 @@ import (
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
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"
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 {
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) {
storage := map[string]rest.Storage{}
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
gvr("certificates.k8s.io", "v1", "certificatesigningrequests"): {
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
gvr("discovery.k8s.io", "v1", "endpointslices"): {
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
gvr("networking.k8s.io", "v1", "ingresses"): {
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
gvr("storage.k8s.io", "v1alpha1", "csistoragecapacities"): {
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
gvr("storage.k8s.io", "v1", "volumeattachments"): {
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
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
gvr("storage.k8s.io", "v1beta1", "csistoragecapacities"): {
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
gvr("rbac.authorization.k8s.io", "v1", "roles"): {
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
gvr("scheduling.k8s.io", "v1", "priorityclasses"): {
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
// depends on aggregator using the same ungrouped RESTOptionsGetter as the kube apiserver, not SimpleRestOptionsFactory in aggregator.go
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",
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"): {
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",
@@ -563,13 +414,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}
// 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
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"]}]}}`,
@@ -577,13 +421,6 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
}
// 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
etcdStorageData[gvr("storage.k8s.io", "v1", "csidrivers")] = StorageData{
Stub: `{"metadata": {"name": "csid2"}, "spec": {"attachRequired": true, "podInfoOnMount": true}}`,