Merge pull request #104248 from liggitt/drop-beta
Drop dead beta storage and validation code
This commit is contained in:
6038
api/openapi-spec/swagger.json
generated
6038
api/openapi-spec/swagger.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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},
|
||||
|
@@ -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"})
|
||||
}
|
||||
|
@@ -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 == "" {
|
||||
|
@@ -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:
|
||||
|
@@ -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)
|
||||
}
|
||||
@@ -681,7 +616,6 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
|
||||
@@ -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)
|
||||
}
|
||||
@@ -837,7 +765,6 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
|
||||
@@ -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)
|
||||
}
|
||||
@@ -953,7 +875,6 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test_validateCertificateSigningRequestOptions verifies validation options are effective in tolerating specific aspects of CSRs
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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,
|
||||
)
|
||||
|
@@ -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=",
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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,20 +47,12 @@ 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.
|
||||
|
@@ -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
|
||||
|
@@ -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,20 +46,12 @@ 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.
|
||||
|
@@ -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{
|
||||
|
@@ -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,20 +52,12 @@ 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.
|
||||
|
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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,20 +42,12 @@ 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.
|
||||
|
@@ -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() {
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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{}
|
||||
}
|
||||
|
@@ -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))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -1,5 +0,0 @@
|
||||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
reviewers:
|
||||
- deads2k
|
||||
- hongchaodeng
|
@@ -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
|
||||
}
|
@@ -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.
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
@@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -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
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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)
|
||||
})
|
||||
|
@@ -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)
|
||||
|
@@ -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}}`,
|
||||
|
Reference in New Issue
Block a user