diff --git a/pkg/apis/admissionregistration/validation/validation_test.go b/pkg/apis/admissionregistration/validation/validation_test.go
index b73dc988734..68b063ea351 100644
--- a/pkg/apis/admissionregistration/validation/validation_test.go
+++ b/pkg/apis/admissionregistration/validation/validation_test.go
@@ -56,926 +56,759 @@ func TestValidateValidatingWebhookConfiguration(t *testing.T) {
name string
config *admissionregistration.ValidatingWebhookConfiguration
expectedError string
- }{
- {
- name: "AdmissionReviewVersions are required",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, false),
- expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1, v1beta1`,
+ }{{
+ name: "AdmissionReviewVersions are required",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, false),
+ expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1, v1beta1`,
+ }, {
+ name: "should fail on bad AdmissionReviewVersion value",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ AdmissionReviewVersions: []string{"0v"},
+ },
+ }, true),
+ expectedError: `Invalid value: "0v": a DNS-1035 label`,
+ }, {
+ name: "should pass on valid AdmissionReviewVersion",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ AdmissionReviewVersions: []string{"v1beta1"},
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "should pass on mix of accepted and unaccepted AdmissionReviewVersion",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ AdmissionReviewVersions: []string{"v1beta1", "invalid-version"},
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "should fail on invalid AdmissionReviewVersion",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ AdmissionReviewVersions: []string{"invalidVersion"},
+ },
+ }, true),
+ expectedError: `Invalid value: []string{"invalidVersion"}`,
+ }, {
+ name: "should fail on duplicate AdmissionReviewVersion",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ AdmissionReviewVersions: []string{"v1beta1", "v1beta1"},
+ },
+ }, true),
+ expectedError: `Invalid value: "v1beta1": duplicate version`,
+ }, {
+ name: "all Webhooks must have a fully qualified name",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
}, {
- name: "should fail on bad AdmissionReviewVersion value",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- AdmissionReviewVersions: []string{"0v"},
- },
- }, true),
- expectedError: `Invalid value: "0v": a DNS-1035 label`,
- },
- {
- name: "should pass on valid AdmissionReviewVersion",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- AdmissionReviewVersions: []string{"v1beta1"},
- },
- }, true),
- expectedError: ``,
- },
- {
- name: "should pass on mix of accepted and unaccepted AdmissionReviewVersion",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- AdmissionReviewVersions: []string{"v1beta1", "invalid-version"},
- },
- }, true),
- expectedError: ``,
- },
- {
- name: "should fail on invalid AdmissionReviewVersion",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- AdmissionReviewVersions: []string{"invalidVersion"},
- },
- }, true),
- expectedError: `Invalid value: []string{"invalidVersion"}`,
- },
- {
- name: "should fail on duplicate AdmissionReviewVersion",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- AdmissionReviewVersions: []string{"v1beta1", "v1beta1"},
- },
- }, true),
- expectedError: `Invalid value: "v1beta1": duplicate version`,
- },
- {
- name: "all Webhooks must have a fully qualified name",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- },
- {
- Name: "k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- },
- {
- Name: "",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- },
- }, true),
- 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 created",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
- },
- {
- name: "Operations must not be empty or nil",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- {
- Operations: nil,
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].rules[0].operations: Required value, webhooks[0].rules[1].operations: Required value`,
- },
- {
- name: "\"\" is NOT a valid operation",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE", ""},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- }, true),
- expectedError: `Unsupported value: ""`,
- },
- {
- name: "operation must be either create/update/delete/connect",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"PATCH"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- }, true),
- expectedError: `Unsupported value: "PATCH"`,
- },
- {
- name: "wildcard operation cannot be mixed with other strings",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE", "*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- }, true),
- expectedError: `if '*' is present, must not specify other operations`,
- },
- {
- name: `resource "*" can co-exist with resources that have subresources`,
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*", "a/b", "a/*", "*/b"},
- },
- },
- },
- },
- }, true),
- },
- {
- name: `resource "*" cannot mix with resources that don't have subresources`,
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*", "a"},
- },
- },
- },
- },
- }, true),
- expectedError: `if '*' is present, must not specify other resources without subresources`,
- },
- {
- name: "resource a/* cannot mix with a/x",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a/x"},
- },
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`,
- },
- {
- name: "resource a/* can mix with a",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a"},
- },
- },
- },
- },
- }, true),
- },
- {
- name: "resource */a cannot mix with x/a",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/a", "x/a"},
- },
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`,
- },
- {
- name: "resource */* cannot mix with other resources",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/*", "a"},
- },
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
- },
- {
- name: "FailurePolicy can only be \"Ignore\" or \"Fail\"",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("other")
- return &r
- }(),
- },
- }, true),
- expectedError: `webhooks[0].failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`,
- },
- {
- name: "AdmissionReviewVersions are required",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, false),
- expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1, v1beta1`,
- },
- {
- name: "SideEffects are required",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: nil,
- },
- }, true),
- expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`,
- },
- {
- name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" when created",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: func() *admissionregistration.SideEffectClass {
- r := admissionregistration.SideEffectClass("other")
- return &r
- }(),
- },
- }, true),
- expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`,
- },
- {
- name: "both service and URL missing",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{},
- },
- }, true),
- expectedError: `exactly one of`,
- },
- {
- name: "both service and URL provided",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Port: 443,
- },
- URL: strPtr("example.com/k8s/webhook"),
- },
- },
- }, true),
- expectedError: `[0].clientConfig: Required value: exactly one of url or service is required`,
- },
- {
- name: "blank URL",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr(""),
- },
- },
- }, true),
- expectedError: `[0].clientConfig.url: Invalid value: "": host must be specified`,
- },
- {
- name: "wrong scheme",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("http://example.com"),
- },
- },
- }, true),
- expectedError: `https`,
- },
- {
- name: "missing host",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("https:///fancy/webhook"),
- },
- },
- }, true),
- expectedError: `host must be specified`,
- },
- {
- name: "fragment",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("https://example.com/#bookmark"),
- },
- },
- }, true),
- expectedError: `"bookmark": fragments are not permitted`,
- },
- {
- name: "query",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("https://example.com?arg=value"),
- },
- },
- }, true),
- expectedError: `"arg=value": query parameters are not permitted`,
- },
- {
- name: "user",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("https://harry.potter@example.com/"),
- },
- },
- }, true),
- expectedError: `"harry.potter": user information is not permitted`,
- },
- {
- name: "just totally wrong",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("arg#backwards=thisis?html.index/port:host//:https"),
- },
- },
- }, true),
- expectedError: `host must be specified`,
- },
- {
- name: "path must start with slash",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("foo/"),
- Port: 443,
- },
- },
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "foo/": must start with a '/'`,
- },
- {
- name: "path accepts slash",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/"),
- Port: 443,
- },
- },
- SideEffects: &noSideEffect,
- },
- }, true),
- expectedError: ``,
- },
- {
- name: "path accepts no trailing slash",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/foo"),
- Port: 443,
- },
- },
- SideEffects: &noSideEffect,
- },
- }, true),
- expectedError: ``,
- },
- {
- name: "path fails //",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("//"),
- Port: 443,
- },
- },
- SideEffects: &noSideEffect,
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`,
- },
- {
- name: "path no empty step",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/foo//bar/"),
- Port: 443,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`,
+ Name: "k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
}, {
- name: "path no empty step 2",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/foo/bar//"),
- Port: 443,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "/foo/bar//": segment[2] may not be empty`,
+ Name: "",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
},
- {
- name: "path no non-subdomain",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/apis/foo.bar/v1alpha1/--bad"),
- Port: 443,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a lowercase RFC 1123 subdomain`,
+ }, true),
+ 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 created",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ }, {
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
},
- {
- name: "invalid port 0",
- config: newValidatingWebhookConfiguration(
- []admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("https://apis/foo.bar"),
- Port: 0,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `Invalid value: 0: port is not valid: must be between 1 and 65535, inclusive`,
- },
- {
- name: "invalid port >65535",
- config: newValidatingWebhookConfiguration(
- []admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("https://apis/foo.bar"),
- Port: 65536,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `Invalid value: 65536: port is not valid: must be between 1 and 65535, inclusive`,
- },
- {
- name: "timeout seconds cannot be greater than 30",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- TimeoutSeconds: int32Ptr(31),
+ }, true),
+ expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
+ }, {
+ name: "Operations must not be empty or nil",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
- }, true),
- expectedError: `webhooks[0].timeoutSeconds: Invalid value: 31: the timeout value must be between 1 and 30 seconds`,
+ }, {
+ Operations: nil,
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ }},
},
- {
- name: "timeout seconds cannot be smaller than 1",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- TimeoutSeconds: int32Ptr(0),
+ }, true),
+ expectedError: `webhooks[0].rules[0].operations: Required value, webhooks[0].rules[1].operations: Required value`,
+ }, {
+ name: "\"\" is NOT a valid operation",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE", ""},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
- }, true),
- expectedError: `webhooks[0].timeoutSeconds: Invalid value: 0: the timeout value must be between 1 and 30 seconds`,
+ }},
},
- {
- name: "timeout seconds must be positive",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- TimeoutSeconds: int32Ptr(-1),
+ }, true),
+ expectedError: `Unsupported value: ""`,
+ }, {
+ name: "operation must be either create/update/delete/connect",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"PATCH"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
- }, true),
- expectedError: `webhooks[0].timeoutSeconds: Invalid value: -1: the timeout value must be between 1 and 30 seconds`,
+ }},
},
- {
- name: "valid timeout seconds",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- TimeoutSeconds: int32Ptr(1),
+ }, true),
+ expectedError: `Unsupported value: "PATCH"`,
+ }, {
+ name: "wildcard operation cannot be mixed with other strings",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE", "*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
- {
- Name: "webhook2.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- TimeoutSeconds: int32Ptr(15),
- },
- {
- Name: "webhook3.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- TimeoutSeconds: int32Ptr(30),
- },
- }, true),
+ }},
},
- {
- name: "single match condition must have a name",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Expression: "true",
- },
+ }, true),
+ expectedError: `if '*' is present, must not specify other operations`,
+ }, {
+ name: `resource "*" can co-exist with resources that have subresources`,
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a/b", "a/*", "*/b"},
+ },
+ }},
+ },
+ }, true),
+ }, {
+ name: `resource "*" cannot mix with resources that don't have subresources`,
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a"},
+ },
+ }},
+ },
+ }, true),
+ expectedError: `if '*' is present, must not specify other resources without subresources`,
+ }, {
+ name: "resource a/* cannot mix with a/x",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a/x"},
+ },
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`,
+ }, {
+ name: "resource a/* can mix with a",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a"},
+ },
+ }},
+ },
+ }, true),
+ }, {
+ name: "resource */a cannot mix with x/a",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/a", "x/a"},
+ },
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`,
+ }, {
+ name: "resource */* cannot mix with other resources",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/*", "a"},
+ },
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
+ }, {
+ name: "FailurePolicy can only be \"Ignore\" or \"Fail\"",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("other")
+ return &r
+ }(),
+ },
+ }, true),
+ expectedError: `webhooks[0].failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`,
+ }, {
+ name: "AdmissionReviewVersions are required",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, false),
+ expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1, v1beta1`,
+ }, {
+ name: "SideEffects are required",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: nil,
+ },
+ }, true),
+ expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`,
+ }, {
+ name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" when created",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: func() *admissionregistration.SideEffectClass {
+ r := admissionregistration.SideEffectClass("other")
+ return &r
+ }(),
+ },
+ }, true),
+ expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`,
+ }, {
+ name: "both service and URL missing",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{},
+ },
+ }, true),
+ expectedError: `exactly one of`,
+ }, {
+ name: "both service and URL provided",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Port: 443,
+ },
+ URL: strPtr("example.com/k8s/webhook"),
+ },
+ },
+ }, true),
+ expectedError: `[0].clientConfig: Required value: exactly one of url or service is required`,
+ }, {
+ name: "blank URL",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr(""),
+ },
+ },
+ }, true),
+ expectedError: `[0].clientConfig.url: Invalid value: "": host must be specified`,
+ }, {
+ name: "wrong scheme",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("http://example.com"),
+ },
+ },
+ }, true),
+ expectedError: `https`,
+ }, {
+ name: "missing host",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("https:///fancy/webhook"),
+ },
+ },
+ }, true),
+ expectedError: `host must be specified`,
+ }, {
+ name: "fragment",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("https://example.com/#bookmark"),
+ },
+ },
+ }, true),
+ expectedError: `"bookmark": fragments are not permitted`,
+ }, {
+ name: "query",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("https://example.com?arg=value"),
+ },
+ },
+ }, true),
+ expectedError: `"arg=value": query parameters are not permitted`,
+ }, {
+ name: "user",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("https://harry.potter@example.com/"),
+ },
+ },
+ }, true),
+ expectedError: `"harry.potter": user information is not permitted`,
+ }, {
+ name: "just totally wrong",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("arg#backwards=thisis?html.index/port:host//:https"),
+ },
+ },
+ }, true),
+ expectedError: `host must be specified`,
+ }, {
+ name: "path must start with slash",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("foo/"),
+ Port: 443,
+ },
+ },
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "foo/": must start with a '/'`,
+ }, {
+ name: "path accepts slash",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/"),
+ Port: 443,
+ },
+ },
+ SideEffects: &noSideEffect,
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "path accepts no trailing slash",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/foo"),
+ Port: 443,
+ },
+ },
+ SideEffects: &noSideEffect,
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "path fails //",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("//"),
+ Port: 443,
+ },
+ },
+ SideEffects: &noSideEffect,
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`,
+ }, {
+ name: "path no empty step",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/foo//bar/"),
+ Port: 443,
+ },
+ },
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`,
+ }, {
+ name: "path no empty step 2",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/foo/bar//"),
+ Port: 443,
+ },
+ },
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "/foo/bar//": segment[2] may not be empty`,
+ }, {
+ name: "path no non-subdomain",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/apis/foo.bar/v1alpha1/--bad"),
+ Port: 443,
+ },
+ },
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a lowercase RFC 1123 subdomain`,
+ }, {
+ name: "invalid port 0",
+ config: newValidatingWebhookConfiguration(
+ []admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("https://apis/foo.bar"),
+ Port: 0,
},
},
+ SideEffects: &unknownSideEffect,
+ },
}, true),
- expectedError: `webhooks[0].matchConditions[0].name: Required value`,
- },
- {
- name: "all match conditions must have a name",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Expression: "true",
- },
- {
- Expression: "true",
- },
- },
- },
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "",
- Expression: "true",
- },
+ expectedError: `Invalid value: 0: port is not valid: must be between 1 and 65535, inclusive`,
+ }, {
+ name: "invalid port >65535",
+ config: newValidatingWebhookConfiguration(
+ []admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("https://apis/foo.bar"),
+ Port: 65536,
},
},
+ SideEffects: &unknownSideEffect,
+ },
}, true),
- expectedError: `webhooks[0].matchConditions[0].name: Required value, webhooks[0].matchConditions[1].name: Required value, webhooks[1].matchConditions[0].name: Required value`,
+ expectedError: `Invalid value: 65536: port is not valid: must be between 1 and 65535, inclusive`,
+ }, {
+ name: "timeout seconds cannot be greater than 30",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ TimeoutSeconds: int32Ptr(31),
},
- {
- name: "single match condition must have a qualified name",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "-hello",
- Expression: "true",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].name: Invalid value: "-hello": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`,
+ }, true),
+ expectedError: `webhooks[0].timeoutSeconds: Invalid value: 31: the timeout value must be between 1 and 30 seconds`,
+ }, {
+ name: "timeout seconds cannot be smaller than 1",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ TimeoutSeconds: int32Ptr(0),
},
- {
- name: "all match conditions must have qualified names",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: ".io",
- Expression: "true",
- },
- {
- Name: "thing.test.com",
- Expression: "true",
- },
- },
- },
- {
- Name: "webhook2.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "some name",
- Expression: "true",
- },
- },
- },
- }, true),
- expectedError: `[webhooks[0].matchConditions[0].name: Invalid value: ".io": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]'), webhooks[1].matchConditions[0].name: Invalid value: "some name": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')]`,
+ }, true),
+ expectedError: `webhooks[0].timeoutSeconds: Invalid value: 0: the timeout value must be between 1 and 30 seconds`,
+ }, {
+ name: "timeout seconds must be positive",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ TimeoutSeconds: int32Ptr(-1),
},
- {
- name: "expression is required",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].expression: Required value`,
+ }, true),
+ expectedError: `webhooks[0].timeoutSeconds: Invalid value: -1: the timeout value must be between 1 and 30 seconds`,
+ }, {
+ name: "valid timeout seconds",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ TimeoutSeconds: int32Ptr(1),
+ }, {
+ Name: "webhook2.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ TimeoutSeconds: int32Ptr(15),
+ }, {
+ Name: "webhook3.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ TimeoutSeconds: int32Ptr(30),
},
- {
- name: "expression is required to have some value",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].expression: Required value`,
+ }, true),
+ }, {
+ name: "single match condition must have a name",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Expression: "true",
+ }},
},
- {
- name: "invalid expression",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "object.x in [1, 2, ",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].expression: Invalid value: "object.x in [1, 2,": compilation failed: ERROR: :1:19: Syntax error: missing ']' at ''`,
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].name: Required value`,
+ }, {
+ name: "all match conditions must have a name",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Expression: "true",
+ }, {
+ Expression: "true",
+ }},
+ }, {
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "",
+ Expression: "true",
+ }},
},
- {
- name: "unique names same hook",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "true",
- },
- {
- Name: "webhook.k8s.io",
- Expression: "true",
- },
- },
- },
- }, true),
- expectedError: `matchConditions[1].name: Duplicate value: "webhook.k8s.io"`,
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].name: Required value, webhooks[0].matchConditions[1].name: Required value, webhooks[1].matchConditions[0].name: Required value`,
+ }, {
+ name: "single match condition must have a qualified name",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "-hello",
+ Expression: "true",
+ }},
},
- {
- name: "repeat names allowed across different hooks",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "true",
- },
- },
- },
- {
- Name: "webhook2.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "true",
- },
- },
- },
- }, true),
- expectedError: ``,
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].name: Invalid value: "-hello": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`,
+ }, {
+ name: "all match conditions must have qualified names",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: ".io",
+ Expression: "true",
+ }, {
+ Name: "thing.test.com",
+ Expression: "true",
+ }},
+ }, {
+ Name: "webhook2.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "some name",
+ Expression: "true",
+ }},
},
- {
- name: "must evaluate to bool",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "6",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].expression: Invalid value: "6": must evaluate to bool`,
+ }, true),
+ expectedError: `[webhooks[0].matchConditions[0].name: Invalid value: ".io": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]'), webhooks[1].matchConditions[0].name: Invalid value: "some name": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')]`,
+ }, {
+ name: "expression is required",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ }},
},
- {
- name: "max of 64 match conditions",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: get65MatchConditions(),
- },
- }, true),
- expectedError: `webhooks[0].matchConditions: Too many: 65: must have at most 64 items`,
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].expression: Required value`,
+ }, {
+ name: "expression is required to have some value",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "",
+ }},
},
- }
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].expression: Required value`,
+ }, {
+ name: "invalid expression",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "object.x in [1, 2, ",
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].expression: Invalid value: "object.x in [1, 2,": compilation failed: ERROR: :1:19: Syntax error: missing ']' at ''`,
+ }, {
+ name: "unique names same hook",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "true",
+ }, {
+ Name: "webhook.k8s.io",
+ Expression: "true",
+ }},
+ },
+ }, true),
+ expectedError: `matchConditions[1].name: Duplicate value: "webhook.k8s.io"`,
+ }, {
+ name: "repeat names allowed across different hooks",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "true",
+ }},
+ }, {
+ Name: "webhook2.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "true",
+ }},
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "must evaluate to bool",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "6",
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].expression: Invalid value: "6": must evaluate to bool`,
+ }, {
+ name: "max of 64 match conditions",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: get65MatchConditions(),
+ },
+ }, true),
+ expectedError: `webhooks[0].matchConditions: Too many: 65: must have at most 64 items`,
+ }}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateValidatingWebhookConfiguration(test.config)
@@ -1004,137 +837,115 @@ func TestValidateValidatingWebhookConfigurationUpdate(t *testing.T) {
oldconfig *admissionregistration.ValidatingWebhookConfiguration
config *admissionregistration.ValidatingWebhookConfiguration
expectedError string
- }{
- {
- name: "should pass on valid new AdmissionReviewVersion",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"v1beta1"},
- },
- }, true),
- oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: ``,
+ }{{
+ name: "should pass on valid new AdmissionReviewVersion",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"v1beta1"},
},
- {
- name: "should pass on invalid AdmissionReviewVersion with invalid previous versions",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"},
- },
- }, true),
- oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"invalid-v0"},
- },
- }, true),
- expectedError: ``,
+ }, true),
+ oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
},
- {
- name: "should fail on invalid AdmissionReviewVersion with valid previous versions",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"invalid-v1"},
- },
- }, true),
- oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"v1beta1", "invalid-v1"},
- },
- }, true),
- expectedError: `Invalid value: []string{"invalid-v1"}`,
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "should pass on invalid AdmissionReviewVersion with invalid previous versions",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"},
},
- {
- name: "should fail on invalid AdmissionReviewVersion with missing previous versions",
- config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"invalid-v1"},
- },
- }, true),
- oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, false),
- expectedError: `Invalid value: []string{"invalid-v1"}`,
+ }, true),
+ oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"invalid-v0"},
},
- {
- name: "Webhooks must have unique names when old config has unique names",
- 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),
- expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "should fail on invalid AdmissionReviewVersion with valid previous versions",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"invalid-v1"},
},
- {
- name: "Webhooks can have duplicate names when old config has duplicate names",
- 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,
- },
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: ``,
+ }, true),
+ oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"v1beta1", "invalid-v1"},
},
- }
+ }, true),
+ expectedError: `Invalid value: []string{"invalid-v1"}`,
+ }, {
+ name: "should fail on invalid AdmissionReviewVersion with missing previous versions",
+ config: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"invalid-v1"},
+ },
+ }, true),
+ oldconfig: newValidatingWebhookConfiguration([]admissionregistration.ValidatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, false),
+ expectedError: `Invalid value: []string{"invalid-v1"}`,
+ }, {
+ name: "Webhooks must have unique names when old config has unique names",
+ 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),
+ expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
+ }, {
+ name: "Webhooks can have duplicate names when old config has duplicate names",
+ 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,
+ }, {
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ expectedError: ``,
+ }}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateValidatingWebhookConfigurationUpdate(test.config, test.oldconfig)
@@ -1179,926 +990,759 @@ func TestValidateMutatingWebhookConfiguration(t *testing.T) {
name string
config *admissionregistration.MutatingWebhookConfiguration
expectedError string
- }{
- {
- name: "AdmissionReviewVersions are required",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, false),
- expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1, v1beta1`,
+ }{{
+ name: "AdmissionReviewVersions are required",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, false),
+ expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1, v1beta1`,
+ }, {
+ name: "should fail on bad AdmissionReviewVersion value",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ AdmissionReviewVersions: []string{"0v"},
+ },
+ }, true),
+ expectedError: `Invalid value: "0v": a DNS-1035 label`,
+ }, {
+ name: "should pass on valid AdmissionReviewVersion",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ AdmissionReviewVersions: []string{"v1beta1"},
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "should pass on mix of accepted and unaccepted AdmissionReviewVersion",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ AdmissionReviewVersions: []string{"v1beta1", "invalid-version"},
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "should fail on invalid AdmissionReviewVersion",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ AdmissionReviewVersions: []string{"invalidVersion"},
+ },
+ }, true),
+ expectedError: `Invalid value: []string{"invalidVersion"}`,
+ }, {
+ name: "should fail on duplicate AdmissionReviewVersion",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ AdmissionReviewVersions: []string{"v1beta1", "v1beta1"},
+ },
+ }, true),
+ expectedError: `Invalid value: "v1beta1": duplicate version`,
+ }, {
+ name: "all Webhooks must have a fully qualified name",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
}, {
- name: "should fail on bad AdmissionReviewVersion value",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- AdmissionReviewVersions: []string{"0v"},
- },
- }, true),
- expectedError: `Invalid value: "0v": a DNS-1035 label`,
- },
- {
- name: "should pass on valid AdmissionReviewVersion",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- AdmissionReviewVersions: []string{"v1beta1"},
- },
- }, true),
- expectedError: ``,
- },
- {
- name: "should pass on mix of accepted and unaccepted AdmissionReviewVersion",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- AdmissionReviewVersions: []string{"v1beta1", "invalid-version"},
- },
- }, true),
- expectedError: ``,
- },
- {
- name: "should fail on invalid AdmissionReviewVersion",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- AdmissionReviewVersions: []string{"invalidVersion"},
- },
- }, true),
- expectedError: `Invalid value: []string{"invalidVersion"}`,
- },
- {
- name: "should fail on duplicate AdmissionReviewVersion",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- AdmissionReviewVersions: []string{"v1beta1", "v1beta1"},
- },
- }, true),
- expectedError: `Invalid value: "v1beta1": duplicate version`,
- },
- {
- name: "all Webhooks must have a fully qualified name",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- },
- {
- Name: "k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- },
- {
- Name: "",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- },
- }, true),
- 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 created",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
- },
- {
- name: "Operations must not be empty or nil",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- {
- Operations: nil,
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].rules[0].operations: Required value, webhooks[0].rules[1].operations: Required value`,
- },
- {
- name: "\"\" is NOT a valid operation",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE", ""},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- }, true),
- expectedError: `Unsupported value: ""`,
- },
- {
- name: "operation must be either create/update/delete/connect",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"PATCH"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- }, true),
- expectedError: `Unsupported value: "PATCH"`,
- },
- {
- name: "wildcard operation cannot be mixed with other strings",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE", "*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- }, true),
- expectedError: `if '*' is present, must not specify other operations`,
- },
- {
- name: `resource "*" can co-exist with resources that have subresources`,
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*", "a/b", "a/*", "*/b"},
- },
- },
- },
- },
- }, true),
- },
- {
- name: `resource "*" cannot mix with resources that don't have subresources`,
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*", "a"},
- },
- },
- },
- },
- }, true),
- expectedError: `if '*' is present, must not specify other resources without subresources`,
- },
- {
- name: "resource a/* cannot mix with a/x",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a/x"},
- },
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`,
- },
- {
- name: "resource a/* can mix with a",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a"},
- },
- },
- },
- },
- }, true),
- },
- {
- name: "resource */a cannot mix with x/a",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/a", "x/a"},
- },
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`,
- },
- {
- name: "resource */* cannot mix with other resources",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- Rules: []admissionregistration.RuleWithOperations{
- {
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/*", "a"},
- },
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
- },
- {
- name: "FailurePolicy can only be \"Ignore\" or \"Fail\"",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("other")
- return &r
- }(),
- },
- }, true),
- expectedError: `webhooks[0].failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`,
- },
- {
- name: "AdmissionReviewVersions are required",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, false),
- expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1, v1beta1`,
- },
- {
- name: "SideEffects are required",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: nil,
- },
- }, true),
- expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`,
- },
- {
- name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" when created",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: func() *admissionregistration.SideEffectClass {
- r := admissionregistration.SideEffectClass("other")
- return &r
- }(),
- },
- }, true),
- expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`,
- },
- {
- name: "both service and URL missing",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{},
- },
- }, true),
- expectedError: `exactly one of`,
- },
- {
- name: "both service and URL provided",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Port: 443,
- },
- URL: strPtr("example.com/k8s/webhook"),
- },
- },
- }, true),
- expectedError: `[0].clientConfig: Required value: exactly one of url or service is required`,
- },
- {
- name: "blank URL",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr(""),
- },
- },
- }, true),
- expectedError: `[0].clientConfig.url: Invalid value: "": host must be specified`,
- },
- {
- name: "wrong scheme",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("http://example.com"),
- },
- },
- }, true),
- expectedError: `https`,
- },
- {
- name: "missing host",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("https:///fancy/webhook"),
- },
- },
- }, true),
- expectedError: `host must be specified`,
- },
- {
- name: "fragment",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("https://example.com/#bookmark"),
- },
- },
- }, true),
- expectedError: `"bookmark": fragments are not permitted`,
- },
- {
- name: "query",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("https://example.com?arg=value"),
- },
- },
- }, true),
- expectedError: `"arg=value": query parameters are not permitted`,
- },
- {
- name: "user",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("https://harry.potter@example.com/"),
- },
- },
- }, true),
- expectedError: `"harry.potter": user information is not permitted`,
- },
- {
- name: "just totally wrong",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- URL: strPtr("arg#backwards=thisis?html.index/port:host//:https"),
- },
- },
- }, true),
- expectedError: `host must be specified`,
- },
- {
- name: "path must start with slash",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("foo/"),
- Port: 443,
- },
- },
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "foo/": must start with a '/'`,
- },
- {
- name: "path accepts slash",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/"),
- Port: 443,
- },
- },
- SideEffects: &noSideEffect,
- },
- }, true),
- expectedError: ``,
- },
- {
- name: "path accepts no trailing slash",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/foo"),
- Port: 443,
- },
- },
- SideEffects: &noSideEffect,
- },
- }, true),
- expectedError: ``,
- },
- {
- name: "path fails //",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("//"),
- Port: 443,
- },
- },
- SideEffects: &noSideEffect,
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`,
- },
- {
- name: "path no empty step",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/foo//bar/"),
- Port: 443,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`,
+ Name: "k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
}, {
- name: "path no empty step 2",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/foo/bar//"),
- Port: 443,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "/foo/bar//": segment[2] may not be empty`,
+ Name: "",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
},
- {
- name: "path no non-subdomain",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("/apis/foo.bar/v1alpha1/--bad"),
- Port: 443,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a lowercase RFC 1123 subdomain`,
+ }, true),
+ 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 created",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ }, {
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
},
- {
- name: "invalid port 0",
- config: newMutatingWebhookConfiguration(
- []admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("https://apis/foo.bar"),
- Port: 0,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `Invalid value: 0: port is not valid: must be between 1 and 65535, inclusive`,
- },
- {
- name: "invalid port >65535",
- config: newMutatingWebhookConfiguration(
- []admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: admissionregistration.WebhookClientConfig{
- Service: &admissionregistration.ServiceReference{
- Namespace: "ns",
- Name: "n",
- Path: strPtr("https://apis/foo.bar"),
- Port: 65536,
- },
- },
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: `Invalid value: 65536: port is not valid: must be between 1 and 65535, inclusive`,
- },
- {
- name: "timeout seconds cannot be greater than 30",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- TimeoutSeconds: int32Ptr(31),
+ }, true),
+ expectedError: `webhooks[1].name: Duplicate value: "webhook.k8s.io"`,
+ }, {
+ name: "Operations must not be empty or nil",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
- }, true),
- expectedError: `webhooks[0].timeoutSeconds: Invalid value: 31: the timeout value must be between 1 and 30 seconds`,
+ }, {
+ Operations: nil,
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ }},
},
- {
- name: "timeout seconds cannot be smaller than 1",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- TimeoutSeconds: int32Ptr(0),
+ }, true),
+ expectedError: `webhooks[0].rules[0].operations: Required value, webhooks[0].rules[1].operations: Required value`,
+ }, {
+ name: "\"\" is NOT a valid operation",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE", ""},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
- }, true),
- expectedError: `webhooks[0].timeoutSeconds: Invalid value: 0: the timeout value must be between 1 and 30 seconds`,
+ }},
},
- {
- name: "timeout seconds must be positive",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- TimeoutSeconds: int32Ptr(-1),
+ }, true),
+ expectedError: `Unsupported value: ""`,
+ }, {
+ name: "operation must be either create/update/delete/connect",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"PATCH"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
- }, true),
- expectedError: `webhooks[0].timeoutSeconds: Invalid value: -1: the timeout value must be between 1 and 30 seconds`,
+ }},
},
- {
- name: "valid timeout seconds",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- TimeoutSeconds: int32Ptr(1),
+ }, true),
+ expectedError: `Unsupported value: "PATCH"`,
+ }, {
+ name: "wildcard operation cannot be mixed with other strings",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE", "*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
- {
- Name: "webhook2.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- TimeoutSeconds: int32Ptr(15),
- },
- {
- Name: "webhook3.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- TimeoutSeconds: int32Ptr(30),
- },
- }, true),
+ }},
},
- {
- name: "single match condition must have a name",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Expression: "true",
- },
+ }, true),
+ expectedError: `if '*' is present, must not specify other operations`,
+ }, {
+ name: `resource "*" can co-exist with resources that have subresources`,
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a/b", "a/*", "*/b"},
+ },
+ }},
+ },
+ }, true),
+ }, {
+ name: `resource "*" cannot mix with resources that don't have subresources`,
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a"},
+ },
+ }},
+ },
+ }, true),
+ expectedError: `if '*' is present, must not specify other resources without subresources`,
+ }, {
+ name: "resource a/* cannot mix with a/x",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a/x"},
+ },
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`,
+ }, {
+ name: "resource a/* can mix with a",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a"},
+ },
+ }},
+ },
+ }, true),
+ }, {
+ name: "resource */a cannot mix with x/a",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/a", "x/a"},
+ },
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].rules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`,
+ }, {
+ name: "resource */* cannot mix with other resources",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ Rules: []admissionregistration.RuleWithOperations{{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/*", "a"},
+ },
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
+ }, {
+ name: "FailurePolicy can only be \"Ignore\" or \"Fail\"",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("other")
+ return &r
+ }(),
+ },
+ }, true),
+ expectedError: `webhooks[0].failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`,
+ }, {
+ name: "AdmissionReviewVersions are required",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, false),
+ expectedError: `webhooks[0].admissionReviewVersions: Required value: must specify one of v1, v1beta1`,
+ }, {
+ name: "SideEffects are required",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: nil,
+ },
+ }, true),
+ expectedError: `webhooks[0].sideEffects: Required value: must specify one of None, NoneOnDryRun`,
+ }, {
+ name: "SideEffects can only be \"None\" or \"NoneOnDryRun\" when created",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: func() *admissionregistration.SideEffectClass {
+ r := admissionregistration.SideEffectClass("other")
+ return &r
+ }(),
+ },
+ }, true),
+ expectedError: `webhooks[0].sideEffects: Unsupported value: "other": supported values: "None", "NoneOnDryRun"`,
+ }, {
+ name: "both service and URL missing",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{},
+ },
+ }, true),
+ expectedError: `exactly one of`,
+ }, {
+ name: "both service and URL provided",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Port: 443,
+ },
+ URL: strPtr("example.com/k8s/webhook"),
+ },
+ },
+ }, true),
+ expectedError: `[0].clientConfig: Required value: exactly one of url or service is required`,
+ }, {
+ name: "blank URL",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr(""),
+ },
+ },
+ }, true),
+ expectedError: `[0].clientConfig.url: Invalid value: "": host must be specified`,
+ }, {
+ name: "wrong scheme",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("http://example.com"),
+ },
+ },
+ }, true),
+ expectedError: `https`,
+ }, {
+ name: "missing host",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("https:///fancy/webhook"),
+ },
+ },
+ }, true),
+ expectedError: `host must be specified`,
+ }, {
+ name: "fragment",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("https://example.com/#bookmark"),
+ },
+ },
+ }, true),
+ expectedError: `"bookmark": fragments are not permitted`,
+ }, {
+ name: "query",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("https://example.com?arg=value"),
+ },
+ },
+ }, true),
+ expectedError: `"arg=value": query parameters are not permitted`,
+ }, {
+ name: "user",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("https://harry.potter@example.com/"),
+ },
+ },
+ }, true),
+ expectedError: `"harry.potter": user information is not permitted`,
+ }, {
+ name: "just totally wrong",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ URL: strPtr("arg#backwards=thisis?html.index/port:host//:https"),
+ },
+ },
+ }, true),
+ expectedError: `host must be specified`,
+ }, {
+ name: "path must start with slash",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("foo/"),
+ Port: 443,
+ },
+ },
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "foo/": must start with a '/'`,
+ }, {
+ name: "path accepts slash",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/"),
+ Port: 443,
+ },
+ },
+ SideEffects: &noSideEffect,
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "path accepts no trailing slash",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/foo"),
+ Port: 443,
+ },
+ },
+ SideEffects: &noSideEffect,
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "path fails //",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("//"),
+ Port: 443,
+ },
+ },
+ SideEffects: &noSideEffect,
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "//": segment[0] may not be empty`,
+ }, {
+ name: "path no empty step",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/foo//bar/"),
+ Port: 443,
+ },
+ },
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "/foo//bar/": segment[1] may not be empty`,
+ }, {
+ name: "path no empty step 2",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/foo/bar//"),
+ Port: 443,
+ },
+ },
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "/foo/bar//": segment[2] may not be empty`,
+ }, {
+ name: "path no non-subdomain",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("/apis/foo.bar/v1alpha1/--bad"),
+ Port: 443,
+ },
+ },
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ expectedError: `clientConfig.service.path: Invalid value: "/apis/foo.bar/v1alpha1/--bad": segment[3]: a lowercase RFC 1123 subdomain`,
+ }, {
+ name: "invalid port 0",
+ config: newMutatingWebhookConfiguration(
+ []admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("https://apis/foo.bar"),
+ Port: 0,
},
},
+ SideEffects: &unknownSideEffect,
+ },
}, true),
- expectedError: `webhooks[0].matchConditions[0].name: Required value`,
- },
- {
- name: "all match conditions must have a name",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Expression: "true",
- },
- {
- Expression: "true",
- },
- },
- },
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "",
- Expression: "true",
- },
+ expectedError: `Invalid value: 0: port is not valid: must be between 1 and 65535, inclusive`,
+ }, {
+ name: "invalid port >65535",
+ config: newMutatingWebhookConfiguration(
+ []admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: admissionregistration.WebhookClientConfig{
+ Service: &admissionregistration.ServiceReference{
+ Namespace: "ns",
+ Name: "n",
+ Path: strPtr("https://apis/foo.bar"),
+ Port: 65536,
},
},
+ SideEffects: &unknownSideEffect,
+ },
}, true),
- expectedError: `webhooks[0].matchConditions[0].name: Required value, webhooks[0].matchConditions[1].name: Required value, webhooks[1].matchConditions[0].name: Required value`,
+ expectedError: `Invalid value: 65536: port is not valid: must be between 1 and 65535, inclusive`,
+ }, {
+ name: "timeout seconds cannot be greater than 30",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ TimeoutSeconds: int32Ptr(31),
},
- {
- name: "single match condition must have a qualified name",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "-hello",
- Expression: "true",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].name: Invalid value: "-hello": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`,
+ }, true),
+ expectedError: `webhooks[0].timeoutSeconds: Invalid value: 31: the timeout value must be between 1 and 30 seconds`,
+ }, {
+ name: "timeout seconds cannot be smaller than 1",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ TimeoutSeconds: int32Ptr(0),
},
- {
- name: "all match conditions must have qualified names",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: ".io",
- Expression: "true",
- },
- {
- Name: "thing.test.com",
- Expression: "true",
- },
- },
- },
- {
- Name: "webhook2.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "some name",
- Expression: "true",
- },
- },
- },
- }, true),
- expectedError: `[webhooks[0].matchConditions[0].name: Invalid value: ".io": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]'), webhooks[1].matchConditions[0].name: Invalid value: "some name": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')]`,
+ }, true),
+ expectedError: `webhooks[0].timeoutSeconds: Invalid value: 0: the timeout value must be between 1 and 30 seconds`,
+ }, {
+ name: "timeout seconds must be positive",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ TimeoutSeconds: int32Ptr(-1),
},
- {
- name: "expression is required",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].expression: Required value`,
+ }, true),
+ expectedError: `webhooks[0].timeoutSeconds: Invalid value: -1: the timeout value must be between 1 and 30 seconds`,
+ }, {
+ name: "valid timeout seconds",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ TimeoutSeconds: int32Ptr(1),
+ }, {
+ Name: "webhook2.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ TimeoutSeconds: int32Ptr(15),
+ }, {
+ Name: "webhook3.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ TimeoutSeconds: int32Ptr(30),
},
- {
- name: "expression is required to have some value",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].expression: Required value`,
+ }, true),
+ }, {
+ name: "single match condition must have a name",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Expression: "true",
+ }},
},
- {
- name: "invalid expression",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "object.x in [1, 2, ",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].expression: Invalid value: "object.x in [1, 2,": compilation failed: ERROR: :1:19: Syntax error: missing ']' at ''`,
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].name: Required value`,
+ }, {
+ name: "all match conditions must have a name",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Expression: "true",
+ }, {
+ Expression: "true",
+ }},
+ }, {
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "",
+ Expression: "true",
+ }},
},
- {
- name: "unique names same hook",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "true",
- },
- {
- Name: "webhook.k8s.io",
- Expression: "true",
- },
- },
- },
- }, true),
- expectedError: `matchConditions[1].name: Duplicate value: "webhook.k8s.io"`,
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].name: Required value, webhooks[0].matchConditions[1].name: Required value, webhooks[1].matchConditions[0].name: Required value`,
+ }, {
+ name: "single match condition must have a qualified name",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "-hello",
+ Expression: "true",
+ }},
},
- {
- name: "repeat names allowed across different hooks",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "true",
- },
- },
- },
- {
- Name: "webhook2.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "true",
- },
- },
- },
- }, true),
- expectedError: ``,
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].name: Invalid value: "-hello": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`,
+ }, {
+ name: "all match conditions must have qualified names",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: ".io",
+ Expression: "true",
+ }, {
+ Name: "thing.test.com",
+ Expression: "true",
+ }},
+ }, {
+ Name: "webhook2.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "some name",
+ Expression: "true",
+ }},
},
- {
- name: "must evaluate to bool",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "webhook.k8s.io",
- Expression: "6",
- },
- },
- },
- }, true),
- expectedError: `webhooks[0].matchConditions[0].expression: Invalid value: "6": must evaluate to bool`,
+ }, true),
+ expectedError: `[webhooks[0].matchConditions[0].name: Invalid value: ".io": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]'), webhooks[1].matchConditions[0].name: Invalid value: "some name": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')]`,
+ }, {
+ name: "expression is required",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ }},
},
- {
- name: "max of 64 match conditions",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- MatchConditions: get65MatchConditions(),
- },
- }, true),
- expectedError: `webhooks[0].matchConditions: Too many: 65: must have at most 64 items`,
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].expression: Required value`,
+ }, {
+ name: "expression is required to have some value",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "",
+ }},
},
- }
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].expression: Required value`,
+ }, {
+ name: "invalid expression",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "object.x in [1, 2, ",
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].expression: Invalid value: "object.x in [1, 2,": compilation failed: ERROR: :1:19: Syntax error: missing ']' at ''`,
+ }, {
+ name: "unique names same hook",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "true",
+ }, {
+ Name: "webhook.k8s.io",
+ Expression: "true",
+ }},
+ },
+ }, true),
+ expectedError: `matchConditions[1].name: Duplicate value: "webhook.k8s.io"`,
+ }, {
+ name: "repeat names allowed across different hooks",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "true",
+ }},
+ }, {
+ Name: "webhook2.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "true",
+ }},
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "must evaluate to bool",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "webhook.k8s.io",
+ Expression: "6",
+ }},
+ },
+ }, true),
+ expectedError: `webhooks[0].matchConditions[0].expression: Invalid value: "6": must evaluate to bool`,
+ }, {
+ name: "max of 64 match conditions",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ MatchConditions: get65MatchConditions(),
+ },
+ }, true),
+ expectedError: `webhooks[0].matchConditions: Too many: 65: must have at most 64 items`,
+ }}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateMutatingWebhookConfiguration(test.config)
@@ -2128,169 +1772,142 @@ func TestValidateMutatingWebhookConfigurationUpdate(t *testing.T) {
oldconfig *admissionregistration.MutatingWebhookConfiguration
config *admissionregistration.MutatingWebhookConfiguration
expectedError string
- }{
- {
- name: "should pass on valid new AdmissionReviewVersion (v1beta1)",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"v1beta1"},
- },
- }, true),
- oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: ``,
+ }{{
+ name: "should pass on valid new AdmissionReviewVersion (v1beta1)",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"v1beta1"},
},
- {
- name: "should pass on valid new AdmissionReviewVersion (v1)",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"v1"},
- },
- }, true),
- oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: ``,
+ }, true),
+ oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
},
- {
- name: "should pass on invalid AdmissionReviewVersion with invalid previous versions",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"},
- },
- }, true),
- oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"invalid-v0"},
- },
- }, true),
- expectedError: ``,
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "should pass on valid new AdmissionReviewVersion (v1)",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"v1"},
},
- {
- name: "should fail on invalid AdmissionReviewVersion with valid previous versions",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"invalid-v1"},
- },
- }, true),
- oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"v1beta1", "invalid-v1"},
- },
- }, true),
- expectedError: `Invalid value: []string{"invalid-v1"}`,
+ }, true),
+ oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
},
- {
- name: "should fail on invalid AdmissionReviewVersion with missing previous versions",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- AdmissionReviewVersions: []string{"invalid-v1"},
- },
- }, true),
- oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, false),
- expectedError: `Invalid value: []string{"invalid-v1"}`,
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "should pass on invalid AdmissionReviewVersion with invalid previous versions",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"invalid-v1", "invalid-v2"},
},
- {
- name: "Webhooks can have duplicate names when old config has duplicate names",
- 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,
- },
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: ``,
+ }, true),
+ oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"invalid-v0"},
},
- {
- name: "Webhooks can't have side effects when old config has no side effects",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &noSideEffect,
- },
- }, true),
- expectedError: `Unsupported value: "Unknown": supported values: "None", "NoneOnDryRun"`,
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "should fail on invalid AdmissionReviewVersion with valid previous versions",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"invalid-v1"},
},
- {
- name: "Webhooks can have side effects when old config has side effects",
- config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{
- {
- Name: "webhook.k8s.io",
- ClientConfig: validClientConfig,
- SideEffects: &unknownSideEffect,
- },
- }, true),
- expectedError: ``,
+ }, true),
+ oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"v1beta1", "invalid-v1"},
},
- }
+ }, true),
+ expectedError: `Invalid value: []string{"invalid-v1"}`,
+ }, {
+ name: "should fail on invalid AdmissionReviewVersion with missing previous versions",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ AdmissionReviewVersions: []string{"invalid-v1"},
+ },
+ }, true),
+ oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, false),
+ expectedError: `Invalid value: []string{"invalid-v1"}`,
+ }, {
+ name: "Webhooks can have duplicate names when old config has duplicate names",
+ 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,
+ }, {
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ expectedError: ``,
+ }, {
+ name: "Webhooks can't have side effects when old config has no side effects",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &noSideEffect,
+ },
+ }, true),
+ expectedError: `Unsupported value: "Unknown": supported values: "None", "NoneOnDryRun"`,
+ }, {
+ name: "Webhooks can have side effects when old config has side effects",
+ config: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ oldconfig: newMutatingWebhookConfiguration([]admissionregistration.MutatingWebhook{{
+ Name: "webhook.k8s.io",
+ ClientConfig: validClientConfig,
+ SideEffects: &unknownSideEffect,
+ },
+ }, true),
+ expectedError: ``,
+ }}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateMutatingWebhookConfigurationUpdate(test.config, test.oldconfig)
@@ -2314,999 +1931,845 @@ func TestValidateValidatingAdmissionPolicy(t *testing.T) {
name string
config *admissionregistration.ValidatingAdmissionPolicy
expectedError string
- }{
- {
- name: "metadata.name validation",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "!!!!",
- },
+ }{{
+ name: "metadata.name validation",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "!!!!",
},
- expectedError: `metadata.name: Invalid value: "!!!!":`,
},
- {
- name: "failure policy validation",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("other")
- return &r
- }(),
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- },
+ expectedError: `metadata.name: Invalid value: "!!!!":`,
+ }, {
+ name: "failure policy validation",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("other")
+ return &r
+ }(),
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
},
- expectedError: `spec.failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`,
},
- {
- name: "failure policy validation",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("other")
- return &r
- }(),
- },
+ expectedError: `spec.failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`,
+ }, {
+ name: "failure policy validation",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("other")
+ return &r
+ }(),
},
- expectedError: `spec.failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`,
},
- {
- name: "API version is required in ParamKind",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- ParamKind: &admissionregistration.ParamKind{
- Kind: "Example",
- APIVersion: "test.example.com",
- },
+ expectedError: `spec.failurePolicy: Unsupported value: "other": supported values: "Fail", "Ignore"`,
+ }, {
+ name: "API version is required in ParamKind",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ ParamKind: &admissionregistration.ParamKind{
+ Kind: "Example",
+ APIVersion: "test.example.com",
},
},
- expectedError: `spec.paramKind.apiVersion: Invalid value: "test.example.com"`,
},
- {
- name: "API kind is required in ParamKind",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- ParamKind: &admissionregistration.ParamKind{
- APIVersion: "test.example.com/v1",
- },
+ expectedError: `spec.paramKind.apiVersion: Invalid value: "test.example.com"`,
+ }, {
+ name: "API kind is required in ParamKind",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ ParamKind: &admissionregistration.ParamKind{
+ APIVersion: "test.example.com/v1",
},
},
- expectedError: `spec.paramKind.kind: Required value`,
},
- {
- name: "API version format in ParamKind",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- ParamKind: &admissionregistration.ParamKind{
- Kind: "Example",
- APIVersion: "test.example.com/!!!",
- },
+ expectedError: `spec.paramKind.kind: Required value`,
+ }, {
+ name: "API version format in ParamKind",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ ParamKind: &admissionregistration.ParamKind{
+ Kind: "Example",
+ APIVersion: "test.example.com/!!!",
},
},
- expectedError: `pec.paramKind.apiVersion: Invalid value: "!!!":`,
},
- {
- name: "API group format in ParamKind",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- ParamKind: &admissionregistration.ParamKind{
- APIVersion: "!!!/v1",
- Kind: "ReplicaLimit",
- },
+ expectedError: `pec.paramKind.apiVersion: Invalid value: "!!!":`,
+ }, {
+ name: "API group format in ParamKind",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ ParamKind: &admissionregistration.ParamKind{
+ APIVersion: "!!!/v1",
+ Kind: "ReplicaLimit",
},
},
- expectedError: `pec.paramKind.apiVersion: Invalid value: "!!!":`,
},
- {
- name: "Validations is required",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{},
+ expectedError: `pec.paramKind.apiVersion: Invalid value: "!!!":`,
+ }, {
+ name: "Validations is required",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
},
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{},
+ },
- expectedError: `spec.validations: Required value: validations or auditAnnotations must contain at least one item`,
- },
- {
- name: "Invalid Validations Reason",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- Reason: func() *metav1.StatusReason {
- r := metav1.StatusReason("other")
- return &r
- }(),
- },
- },
- },
+ expectedError: `spec.validations: Required value: validations or auditAnnotations must contain at least one item`,
+ }, {
+ name: "Invalid Validations Reason",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
},
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ Reason: func() *metav1.StatusReason {
+ r := metav1.StatusReason("other")
+ return &r
+ }(),
+ }},
+ },
+ },
- expectedError: `spec.validations[0].reason: Unsupported value: "other"`,
- },
- {
- name: "MatchConstraints is required",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- },
+ expectedError: `spec.validations[0].reason: Unsupported value: "other"`,
+ }, {
+ name: "MatchConstraints is required",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
},
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ },
+ },
- expectedError: `spec.matchConstraints: Required value`,
+ expectedError: `spec.matchConstraints: Required value`,
+ }, {
+ name: "matchConstraints.resourceRules is required",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{},
+ },
},
- {
- name: "matchConstraints.resourceRules is required",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
+ expectedError: `spec.matchConstraints.resourceRules: Required value`,
+ }, {
+ name: "matchConstraints.resourceRules has at least one explicit rule",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Rule: admissionregistration.Rule{},
},
- },
- MatchConstraints: &admissionregistration.MatchResources{},
+ ResourceNames: []string{"/./."},
+ }},
},
},
- expectedError: `spec.matchConstraints.resourceRules: Required value`,
},
- {
- name: "matchConstraints.resourceRules has at least one explicit rule",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Rule: admissionregistration.Rule{},
- },
- ResourceNames: []string{"/./."},
- },
- },
- },
- },
+ expectedError: `spec.matchConstraints.resourceRules[0].apiVersions: Required value`,
+ }, {
+ name: "expression is required",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{}},
},
- expectedError: `spec.matchConstraints.resourceRules[0].apiVersions: Required value`,
},
- {
- name: "expression is required",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{{}},
- },
- },
- expectedError: `spec.validations[0].expression: Required value: expression is not specified`,
- },
- {
- name: "matchResources resourceNames check",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- ResourceNames: []string{"/./."},
- },
- },
- },
+ expectedError: `spec.validations[0].expression: Required value: expression is not specified`,
+ }, {
+ name: "matchResources resourceNames check",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ ResourceNames: []string{"/./."},
+ }},
},
},
- expectedError: `spec.matchConstraints.resourceRules[0].resourceNames[0]: Invalid value: "/./."`,
},
- {
- name: "matchResources resourceNames cannot duplicate",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- ResourceNames: []string{"test", "test"},
- },
- },
- },
+ expectedError: `spec.matchConstraints.resourceRules[0].resourceNames[0]: Invalid value: "/./."`,
+ }, {
+ name: "matchResources resourceNames cannot duplicate",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ ResourceNames: []string{"test", "test"},
+ }},
},
},
- expectedError: `spec.matchConstraints.resourceRules[0].resourceNames[1]: Duplicate value: "test"`,
},
- {
- name: "matchResources validation: matchPolicy",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("other")
- return &r
- }(),
- },
- },
+ expectedError: `spec.matchConstraints.resourceRules[0].resourceNames[1]: Duplicate value: "test"`,
+ }, {
+ name: "matchResources validation: matchPolicy",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
},
- expectedError: `spec.matchConstraints.matchPolicy: Unsupported value: "other": supported values: "Equivalent", "Exact"`,
- },
- {
- name: "Operations must not be empty or nil",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("other")
return &r
}(),
- MatchConstraints: &admissionregistration.MatchResources{
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: nil,
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- ExcludeResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: nil,
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- },
},
},
- expectedError: `spec.matchConstraints.resourceRules[0].operations: Required value, spec.matchConstraints.resourceRules[1].operations: Required value, spec.matchConstraints.excludeResourceRules[0].operations: Required value, spec.matchConstraints.excludeResourceRules[1].operations: Required value`,
},
- {
- name: "\"\" is NOT a valid operation",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE", ""},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- },
- },
+ expectedError: `spec.matchConstraints.matchPolicy: Unsupported value: "other": supported values: "Equivalent", "Exact"`,
+ }, {
+ name: "Operations must not be empty or nil",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
},
- expectedError: `Unsupported value: ""`,
- },
- {
- name: "operation must be either create/update/delete/connect",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ MatchConstraints: &admissionregistration.MatchResources{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"PATCH"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- },
- },
- expectedError: `Unsupported value: "PATCH"`,
- },
- {
- name: "wildcard operation cannot be mixed with other strings",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE", "*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- },
- },
- },
- expectedError: `if '*' is present, must not specify other operations`,
- },
- {
- name: `resource "*" can co-exist with resources that have subresources`,
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*", "a/b", "a/*", "*/b"},
- },
- },
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
+ }, {
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: nil,
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
},
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
+ }},
+ ExcludeResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
},
- },
+ }, {
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: nil,
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ },
+ }},
},
},
},
- {
- name: `resource "*" cannot mix with resources that don't have subresources`,
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*", "a"},
- },
- },
+ expectedError: `spec.matchConstraints.resourceRules[0].operations: Required value, spec.matchConstraints.resourceRules[1].operations: Required value, spec.matchConstraints.excludeResourceRules[0].operations: Required value, spec.matchConstraints.excludeResourceRules[1].operations: Required value`,
+ }, {
+ name: "\"\" is NOT a valid operation",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE", ""},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
+ }},
},
},
- expectedError: `if '*' is present, must not specify other resources without subresources`,
},
- {
- name: "resource a/* cannot mix with a/x",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a/x"},
- },
- },
+ expectedError: `Unsupported value: ""`,
+ }, {
+ name: "operation must be either create/update/delete/connect",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"PATCH"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
+ }},
},
},
- expectedError: `spec.matchConstraints.resourceRules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`,
},
- {
- name: "resource a/* can mix with a",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ expectedError: `Unsupported value: "PATCH"`,
+ }, {
+ name: "wildcard operation cannot be mixed with other strings",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE", "*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ },
+ }},
},
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
+ },
+ },
+ expectedError: `if '*' is present, must not specify other operations`,
+ }, {
+ name: `resource "*" can co-exist with resources that have subresources`,
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a"},
- },
- },
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a/b", "a/*", "*/b"},
},
},
+ }},
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
},
},
},
- {
- name: "resource */a cannot mix with x/a",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/a", "x/a"},
- },
- },
+ }, {
+ name: `resource "*" cannot mix with resources that don't have subresources`,
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a"},
},
},
- },
+ }},
},
},
- expectedError: `spec.matchConstraints.resourceRules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`,
},
- {
- name: "resource */* cannot mix with other resources",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/*", "a"},
- },
- },
+ expectedError: `if '*' is present, must not specify other resources without subresources`,
+ }, {
+ name: "resource a/* cannot mix with a/x",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a/x"},
},
},
- },
+ }},
},
},
- expectedError: `spec.matchConstraints.resourceRules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
},
- {
- name: "invalid expression",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x in [1, 2, ",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/*"},
- },
- },
- },
- },
- },
- },
+ expectedError: `spec.matchConstraints.resourceRules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`,
+ }, {
+ name: "resource a/* can mix with a",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
},
- expectedError: `spec.validations[0].expression: Invalid value: "object.x in [1, 2, ": compilation failed: ERROR: :1:20: Syntax error: missing ']' at '`,
- },
- {
- name: "invalid messageExpression",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "true",
- MessageExpression: "object.x in [1, 2, ",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/*"},
- },
- },
- },
- },
- },
- },
- },
- expectedError: `spec.validations[0].messageExpression: Invalid value: "object.x in [1, 2, ": compilation failed: ERROR: :1:20: Syntax error: missing ']' at '`,
- },
- {
- name: "messageExpression of wrong type",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "true",
- MessageExpression: "0 == 0",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/*"},
- },
- },
- },
- },
- },
- },
- },
- expectedError: `spec.validations[0].messageExpression: Invalid value: "0 == 0": must evaluate to string`,
- },
- {
- name: "invalid auditAnnotations key due to key name",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- AuditAnnotations: []admissionregistration.AuditAnnotation{
- {
- Key: "@",
- ValueExpression: "value",
- },
- },
- },
- },
- expectedError: `spec.auditAnnotations[0].key: Invalid value: "config/@": name part must consist of alphanumeric characters`,
- },
- {
- name: "auditAnnotations keys must be unique",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- AuditAnnotations: []admissionregistration.AuditAnnotation{
- {
- Key: "a",
- ValueExpression: "'1'",
- },
- {
- Key: "a",
- ValueExpression: "'2'",
- },
- },
- },
- },
- expectedError: `spec.auditAnnotations[1].key: Duplicate value: "a"`,
- },
- {
- name: "invalid auditAnnotations key due to metadata.name",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "nope!",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- AuditAnnotations: []admissionregistration.AuditAnnotation{
- {
- Key: "key",
- ValueExpression: "'value'",
- },
- },
- },
- },
- expectedError: `spec.auditAnnotations[0].key: Invalid value: "nope!/key": prefix part a lowercase RFC 1123 subdomain`,
- },
- {
- name: "invalid auditAnnotations key due to length",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "this-is-a-long-name-for-a-admission-policy-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- AuditAnnotations: []admissionregistration.AuditAnnotation{
- {
- Key: "this-is-a-long-name-for-an-audit-annotation-key-xxxxxxxxxxxxxxxxxxxxxxxxxx",
- ValueExpression: "'value'",
- },
- },
- },
- },
- expectedError: `spec.auditAnnotations[0].key: Invalid value`,
- },
- {
- name: "invalid auditAnnotations valueExpression type",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- AuditAnnotations: []admissionregistration.AuditAnnotation{
- {
- Key: "something",
- ValueExpression: "true",
- },
- },
- },
- },
- expectedError: `spec.auditAnnotations[0].valueExpression: Invalid value: "true": must evaluate to one of [string null_type]`,
- },
- {
- name: "invalid auditAnnotations valueExpression",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- AuditAnnotations: []admissionregistration.AuditAnnotation{
- {
- Key: "something",
- ValueExpression: "object.x in [1, 2, ",
- },
- },
- },
- },
- expectedError: `spec.auditAnnotations[0].valueExpression: Invalid value: "object.x in [1, 2, ": compilation failed: ERROR: :1:19: Syntax error: missing ']' at '`,
- },
- {
- name: "single match condition must have a name",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Expression: "true",
- },
- },
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- },
- },
- expectedError: `spec.matchConditions[0].name: Required value`,
- },
- {
- name: "match condition with parameters allowed",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- ParamKind: &admissionregistration.ParamKind{
- Kind: "Foo",
- APIVersion: "foobar/v1alpha1",
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- },
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "hasParams",
- Expression: `params.foo == "okay"`,
- },
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- },
- },
- expectedError: "",
- },
- {
- name: "match condition with parameters not allowed if no param kind",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a"},
},
},
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- },
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
- return &r
- }(),
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "hasParams",
- Expression: `params.foo == "okay"`,
- },
- },
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
+ }},
},
},
- expectedError: `undeclared reference to 'params'`,
},
- }
+ }, {
+ name: "resource */a cannot mix with x/a",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/a", "x/a"},
+ },
+ },
+ }},
+ },
+ },
+ },
+ expectedError: `spec.matchConstraints.resourceRules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`,
+ }, {
+ name: "resource */* cannot mix with other resources",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/*", "a"},
+ },
+ },
+ }},
+ },
+ },
+ },
+ expectedError: `spec.matchConstraints.resourceRules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
+ }, {
+ name: "invalid expression",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x in [1, 2, ",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/*"},
+ },
+ },
+ }},
+ },
+ },
+ },
+ expectedError: `spec.validations[0].expression: Invalid value: "object.x in [1, 2, ": compilation failed: ERROR: :1:20: Syntax error: missing ']' at '`,
+ }, {
+ name: "invalid messageExpression",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "true",
+ MessageExpression: "object.x in [1, 2, ",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/*"},
+ },
+ },
+ }},
+ },
+ },
+ },
+ expectedError: `spec.validations[0].messageExpression: Invalid value: "object.x in [1, 2, ": compilation failed: ERROR: :1:20: Syntax error: missing ']' at '`,
+ }, {
+ name: "messageExpression of wrong type",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "true",
+ MessageExpression: "0 == 0",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/*"},
+ },
+ },
+ }},
+ },
+ },
+ },
+ expectedError: `spec.validations[0].messageExpression: Invalid value: "0 == 0": must evaluate to string`,
+ }, {
+ name: "invalid auditAnnotations key due to key name",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ AuditAnnotations: []admissionregistration.AuditAnnotation{{
+ Key: "@",
+ ValueExpression: "value",
+ }},
+ },
+ },
+ expectedError: `spec.auditAnnotations[0].key: Invalid value: "config/@": name part must consist of alphanumeric characters`,
+ }, {
+ name: "auditAnnotations keys must be unique",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ AuditAnnotations: []admissionregistration.AuditAnnotation{{
+ Key: "a",
+ ValueExpression: "'1'",
+ }, {
+ Key: "a",
+ ValueExpression: "'2'",
+ }},
+ },
+ },
+ expectedError: `spec.auditAnnotations[1].key: Duplicate value: "a"`,
+ }, {
+ name: "invalid auditAnnotations key due to metadata.name",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "nope!",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ AuditAnnotations: []admissionregistration.AuditAnnotation{{
+ Key: "key",
+ ValueExpression: "'value'",
+ }},
+ },
+ },
+ expectedError: `spec.auditAnnotations[0].key: Invalid value: "nope!/key": prefix part a lowercase RFC 1123 subdomain`,
+ }, {
+ name: "invalid auditAnnotations key due to length",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "this-is-a-long-name-for-a-admission-policy-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ AuditAnnotations: []admissionregistration.AuditAnnotation{{
+ Key: "this-is-a-long-name-for-an-audit-annotation-key-xxxxxxxxxxxxxxxxxxxxxxxxxx",
+ ValueExpression: "'value'",
+ }},
+ },
+ },
+ expectedError: `spec.auditAnnotations[0].key: Invalid value`,
+ }, {
+ name: "invalid auditAnnotations valueExpression type",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ AuditAnnotations: []admissionregistration.AuditAnnotation{{
+ Key: "something",
+ ValueExpression: "true",
+ }},
+ },
+ },
+ expectedError: `spec.auditAnnotations[0].valueExpression: Invalid value: "true": must evaluate to one of [string null_type]`,
+ }, {
+ name: "invalid auditAnnotations valueExpression",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ AuditAnnotations: []admissionregistration.AuditAnnotation{{
+ Key: "something",
+ ValueExpression: "object.x in [1, 2, ",
+ }},
+ },
+ },
+ expectedError: `spec.auditAnnotations[0].valueExpression: Invalid value: "object.x in [1, 2, ": compilation failed: ERROR: :1:19: Syntax error: missing ']' at '`,
+ }, {
+ name: "single match condition must have a name",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Expression: "true",
+ }},
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ },
+ },
+ expectedError: `spec.matchConditions[0].name: Required value`,
+ }, {
+ name: "match condition with parameters allowed",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ ParamKind: &admissionregistration.ParamKind{
+ Kind: "Foo",
+ APIVersion: "foobar/v1alpha1",
+ },
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ },
+ }},
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ },
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "hasParams",
+ Expression: `params.foo == "okay"`,
+ }},
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ },
+ },
+ expectedError: "",
+ }, {
+ name: "match condition with parameters not allowed if no param kind",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ },
+ }},
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ },
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "hasParams",
+ Expression: `params.foo == "okay"`,
+ }},
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ },
+ },
+ expectedError: `undeclared reference to 'params'`,
+ }}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateValidatingAdmissionPolicy(test.config)
@@ -3330,322 +2793,282 @@ func TestValidateValidatingAdmissionPolicyUpdate(t *testing.T) {
oldconfig *admissionregistration.ValidatingAdmissionPolicy
config *admissionregistration.ValidatingAdmissionPolicy
expectedError string
- }{
- {
- name: "should pass on valid new ValidatingAdmissionPolicy",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
+ }{{
+ name: "should pass on valid new ValidatingAdmissionPolicy",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
- },
- },
- oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- },
+ }},
},
},
},
- {
- name: "should pass on valid new ValidatingAdmissionPolicy with invalid old ValidatingAdmissionPolicy",
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
- return &r
- }(),
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- MatchConstraints: &admissionregistration.MatchResources{
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
+ }},
},
},
- oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "!!!",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{},
- },
},
- {
- name: "match conditions re-checked if paramKind changes",
- oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- ParamKind: &admissionregistration.ParamKind{
- Kind: "Foo",
- APIVersion: "foobar/v1alpha1",
+ }, {
+ name: "should pass on valid new ValidatingAdmissionPolicy with invalid old ValidatingAdmissionPolicy",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ MatchConstraints: &admissionregistration.MatchResources{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
return &r
}(),
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "hasParams",
- Expression: `params.foo == "okay"`,
- },
- },
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- },
- },
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- },
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
- return &r
- }(),
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "hasParams",
- Expression: `params.foo == "okay"`,
- },
- },
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
+ }},
},
},
- expectedError: `undeclared reference to 'params'`,
},
- {
- name: "match conditions not re-checked if no change to paramKind or matchConditions",
- oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- },
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Fail")
- return &r
- }(),
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "hasParams",
- Expression: `params.foo == "okay"`,
- },
- },
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 100",
- },
- },
- },
+ oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "!!!",
},
- config: &admissionregistration.ValidatingAdmissionPolicy{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicySpec{
- MatchConstraints: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- },
- FailurePolicy: func() *admissionregistration.FailurePolicyType {
- r := admissionregistration.FailurePolicyType("Ignore")
- return &r
- }(),
- MatchConditions: []admissionregistration.MatchCondition{
- {
- Name: "hasParams",
- Expression: `params.foo == "okay"`,
- },
- },
- Validations: []admissionregistration.Validation{
- {
- Expression: "object.x < 50",
- },
- },
- },
- },
- expectedError: "",
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{},
},
- // TODO: CustomAuditAnnotations: string valueExpression with {oldObject} is allowed
+ }, {
+ name: "match conditions re-checked if paramKind changes",
+ oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ ParamKind: &admissionregistration.ParamKind{
+ Kind: "Foo",
+ APIVersion: "foobar/v1alpha1",
+ },
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ },
+ }},
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ },
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "hasParams",
+ Expression: `params.foo == "okay"`,
+ }},
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ },
+ },
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ },
+ }},
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ },
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "hasParams",
+ Expression: `params.foo == "okay"`,
+ }},
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ },
+ },
+ expectedError: `undeclared reference to 'params'`,
+ }, {
+ name: "match conditions not re-checked if no change to paramKind or matchConditions",
+ oldconfig: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ },
+ }},
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ },
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Fail")
+ return &r
+ }(),
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "hasParams",
+ Expression: `params.foo == "okay"`,
+ }},
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 100",
+ }},
+ },
+ },
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ MatchConstraints: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ },
+ }},
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ },
+ FailurePolicy: func() *admissionregistration.FailurePolicyType {
+ r := admissionregistration.FailurePolicyType("Ignore")
+ return &r
+ }(),
+ MatchConditions: []admissionregistration.MatchCondition{{
+ Name: "hasParams",
+ Expression: `params.foo == "okay"`,
+ }},
+ Validations: []admissionregistration.Validation{{
+ Expression: "object.x < 50",
+ }},
+ },
+ },
+ expectedError: "",
+ },
+ // TODO: CustomAuditAnnotations: string valueExpression with {oldObject} is allowed
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@@ -3670,425 +3093,385 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
name string
config *admissionregistration.ValidatingAdmissionPolicyBinding
expectedError string
- }{
- {
- name: "metadata.name validation",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "!!!!",
+ }{{
+ name: "metadata.name validation",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "!!!!",
+ },
+ },
+ expectedError: `metadata.name: Invalid value: "!!!!":`,
+ }, {
+ name: "PolicyName is required",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{},
+ },
+ expectedError: `spec.policyName: Required value`,
+ }, {
+ name: "matchResources validation: matchPolicy",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
+ },
+ MatchResources: &admissionregistration.MatchResources{
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("other")
+ return &r
+ }(),
},
},
- expectedError: `metadata.name: Invalid value: "!!!!":`,
},
- {
- name: "PolicyName is required",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{},
+ expectedError: `spec.matchResouces.matchPolicy: Unsupported value: "other": supported values: "Equivalent", "Exact"`,
+ }, {
+ name: "Operations must not be empty or nil",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
},
- expectedError: `spec.policyName: Required value`,
- },
- {
- name: "matchResources validation: matchPolicy",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- MatchResources: &admissionregistration.MatchResources{
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("other")
- return &r
- }(),
- },
- },
- },
- expectedError: `spec.matchResouces.matchPolicy: Unsupported value: "other": supported values: "Equivalent", "Exact"`,
- },
- {
- name: "Operations must not be empty or nil",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- MatchResources: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: nil,
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ MatchResources: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- ExcludeResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: nil,
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ }, {
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: nil,
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
- },
- },
- expectedError: `spec.matchResouces.resourceRules[0].operations: Required value, spec.matchResouces.resourceRules[1].operations: Required value, spec.matchResouces.excludeResourceRules[0].operations: Required value, spec.matchResouces.excludeResourceRules[1].operations: Required value`,
- },
- {
- name: "\"\" is NOT a valid operation",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- }, MatchResources: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE", ""},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ }},
+ ExcludeResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
- },
- },
- expectedError: `Unsupported value: ""`,
- },
- {
- name: "operation must be either create/update/delete/connect",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- }, MatchResources: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"PATCH"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ }, {
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: nil,
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
+ }},
},
},
- expectedError: `Unsupported value: "PATCH"`,
},
- {
- name: "wildcard operation cannot be mixed with other strings",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE", "*"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ expectedError: `spec.matchResouces.resourceRules[0].operations: Required value, spec.matchResouces.resourceRules[1].operations: Required value, spec.matchResouces.excludeResourceRules[0].operations: Required value, spec.matchResouces.excludeResourceRules[1].operations: Required value`,
+ }, {
+ name: "\"\" is NOT a valid operation",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
+ }, MatchResources: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE", ""},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
+ }},
},
},
- expectedError: `if '*' is present, must not specify other operations`,
},
- {
- name: `resource "*" can co-exist with resources that have subresources`,
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*", "a/b", "a/*", "*/b"},
- },
- },
+ expectedError: `Unsupported value: ""`,
+ }, {
+ name: "operation must be either create/update/delete/connect",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
+ }, MatchResources: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"PATCH"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
+ }},
},
},
},
- {
- name: `resource "*" cannot mix with resources that don't have subresources`,
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ expectedError: `Unsupported value: "PATCH"`,
+ }, {
+ name: "wildcard operation cannot be mixed with other strings",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*", "a"},
- },
- },
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE", "*"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
+ }},
},
},
- expectedError: `if '*' is present, must not specify other resources without subresources`,
},
- {
- name: "resource a/* cannot mix with a/x",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ expectedError: `if '*' is present, must not specify other operations`,
+ }, {
+ name: `resource "*" can co-exist with resources that have subresources`,
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a/x"},
- },
- },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a/b", "a/*", "*/b"},
},
},
- },
+ }},
},
},
- expectedError: `spec.matchResouces.resourceRules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`,
},
- {
- name: "resource a/* can mix with a",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ }, {
+ name: `resource "*" cannot mix with resources that don't have subresources`,
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a"},
- },
- },
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a"},
},
},
- },
+ }},
},
},
},
- {
- name: "resource */a cannot mix with x/a",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ expectedError: `if '*' is present, must not specify other resources without subresources`,
+ }, {
+ name: "resource a/* cannot mix with a/x",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/a", "x/a"},
- },
- },
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a/x"},
},
},
- },
+ }},
},
},
- expectedError: `spec.matchResouces.resourceRules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`,
},
- {
- name: "resource */* cannot mix with other resources",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ expectedError: `spec.matchResouces.resourceRules[0].resources[1]: Invalid value: "a/x": if 'a/*' is present, must not specify a/x`,
+ }, {
+ name: "resource a/* can mix with a",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/*", "a"},
- },
- },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a"},
},
},
- },
+ }},
},
},
- expectedError: `spec.matchResouces.resourceRules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
},
- {
- name: "validationActions must be unique",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ }, {
+ name: "resource */a cannot mix with x/a",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny, admissionregistration.Deny},
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/a", "x/a"},
+ },
+ },
+ }},
},
},
- expectedError: `spec.validationActions[1]: Duplicate value: "Deny"`,
},
- {
- name: "validationActions must contain supported values",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ expectedError: `spec.matchResouces.resourceRules[0].resources[1]: Invalid value: "x/a": if '*/a' is present, must not specify x/a`,
+ }, {
+ name: "resource */* cannot mix with other resources",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.ValidationAction("illegal")},
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/*", "a"},
+ },
+ },
+ }},
},
},
- expectedError: `Unsupported value: "illegal": supported values: "Audit", "Deny", "Warn"`,
},
- }
+ expectedError: `spec.matchResouces.resourceRules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
+ }, {
+ name: "validationActions must be unique",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
+ },
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny, admissionregistration.Deny},
+ },
+ },
+ expectedError: `spec.validationActions[1]: Duplicate value: "Deny"`,
+ }, {
+ name: "validationActions must contain supported values",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
+ },
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.ValidationAction("illegal")},
+ },
+ },
+ expectedError: `Unsupported value: "illegal": supported values: "Audit", "Deny", "Warn"`,
+ }}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateValidatingAdmissionPolicyBinding(test.config)
@@ -4113,128 +3496,119 @@ func TestValidateValidatingAdmissionPolicyBindingUpdate(t *testing.T) {
oldconfig *admissionregistration.ValidatingAdmissionPolicyBinding
config *admissionregistration.ValidatingAdmissionPolicyBinding
expectedError string
- }{
- {
- name: "should pass on valid new ValidatingAdmissionPolicyBinding",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
- },
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
- },
- },
- },
- },
+ }{{
+ name: "should pass on valid new ValidatingAdmissionPolicyBinding",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
},
- oldconfig: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
+ }},
},
},
},
- {
- name: "should pass on valid new ValidatingAdmissionPolicyBinding with invalid old ValidatingAdmissionPolicyBinding",
- config: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "config",
+ oldconfig: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
},
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
- PolicyName: "xyzlimit-scale.example.com",
- ParamRef: &admissionregistration.ParamRef{
- Name: "xyzlimit-scale-setting.example.com",
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
},
- ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
- MatchResources: &admissionregistration.MatchResources{
- NamespaceSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- ObjectSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{"a": "b"},
- },
- MatchPolicy: func() *admissionregistration.MatchPolicyType {
- r := admissionregistration.MatchPolicyType("Exact")
- return &r
- }(),
- ResourceRules: []admissionregistration.NamedRuleWithOperations{
- {
- RuleWithOperations: admissionregistration.RuleWithOperations{
- Operations: []admissionregistration.OperationType{"CREATE"},
- Rule: admissionregistration.Rule{
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a"},
- },
- },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
},
},
- },
+ }},
},
},
- oldconfig: &admissionregistration.ValidatingAdmissionPolicyBinding{
- ObjectMeta: metav1.ObjectMeta{
- Name: "!!!",
- },
- Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{},
- },
},
- }
+ }, {
+ name: "should pass on valid new ValidatingAdmissionPolicyBinding with invalid old ValidatingAdmissionPolicyBinding",
+ config: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
+ PolicyName: "xyzlimit-scale.example.com",
+ ParamRef: &admissionregistration.ParamRef{
+ Name: "xyzlimit-scale-setting.example.com",
+ },
+ ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
+ MatchResources: &admissionregistration.MatchResources{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ ObjectSelector: &metav1.LabelSelector{
+ MatchLabels: map[string]string{"a": "b"},
+ },
+ MatchPolicy: func() *admissionregistration.MatchPolicyType {
+ r := admissionregistration.MatchPolicyType("Exact")
+ return &r
+ }(),
+ ResourceRules: []admissionregistration.NamedRuleWithOperations{{
+ RuleWithOperations: admissionregistration.RuleWithOperations{
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a"},
+ },
+ },
+ }},
+ },
+ },
+ },
+ oldconfig: &admissionregistration.ValidatingAdmissionPolicyBinding{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "!!!",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{},
+ },
+ }}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateValidatingAdmissionPolicyBindingUpdate(test.config, test.oldconfig)
@@ -4258,64 +3632,51 @@ func TestValidateValidatingAdmissionPolicyStatus(t *testing.T) {
name string
status *admissionregistration.ValidatingAdmissionPolicyStatus
expectedError string
- }{
- {
- name: "empty",
- status: &admissionregistration.ValidatingAdmissionPolicyStatus{},
- },
- {
- name: "type checking",
- status: &admissionregistration.ValidatingAdmissionPolicyStatus{
- TypeChecking: &admissionregistration.TypeChecking{
- ExpressionWarnings: []admissionregistration.ExpressionWarning{
- {
- FieldRef: "spec.validations[0].expression",
- Warning: "message",
- },
- },
- },
+ }{{
+ name: "empty",
+ status: &admissionregistration.ValidatingAdmissionPolicyStatus{},
+ }, {
+ name: "type checking",
+ status: &admissionregistration.ValidatingAdmissionPolicyStatus{
+ TypeChecking: &admissionregistration.TypeChecking{
+ ExpressionWarnings: []admissionregistration.ExpressionWarning{{
+ FieldRef: "spec.validations[0].expression",
+ Warning: "message",
+ }},
},
},
- {
- name: "type checking bad json path",
- status: &admissionregistration.ValidatingAdmissionPolicyStatus{
- TypeChecking: &admissionregistration.TypeChecking{
- ExpressionWarnings: []admissionregistration.ExpressionWarning{
- {
- FieldRef: "spec[foo]",
- Warning: "message",
- },
- },
- },
+ }, {
+ name: "type checking bad json path",
+ status: &admissionregistration.ValidatingAdmissionPolicyStatus{
+ TypeChecking: &admissionregistration.TypeChecking{
+ ExpressionWarnings: []admissionregistration.ExpressionWarning{{
+ FieldRef: "spec[foo]",
+ Warning: "message",
+ }},
},
- expectedError: "invalid JSONPath: invalid array index foo",
},
- {
- name: "type checking missing warning",
- status: &admissionregistration.ValidatingAdmissionPolicyStatus{
- TypeChecking: &admissionregistration.TypeChecking{
- ExpressionWarnings: []admissionregistration.ExpressionWarning{
- {
- FieldRef: "spec.validations[0].expression",
- },
- },
- },
+ expectedError: "invalid JSONPath: invalid array index foo",
+ }, {
+ name: "type checking missing warning",
+ status: &admissionregistration.ValidatingAdmissionPolicyStatus{
+ TypeChecking: &admissionregistration.TypeChecking{
+ ExpressionWarnings: []admissionregistration.ExpressionWarning{{
+ FieldRef: "spec.validations[0].expression",
+ }},
},
- expectedError: "Required value",
},
- {
- name: "type checking missing fieldRef",
- status: &admissionregistration.ValidatingAdmissionPolicyStatus{
- TypeChecking: &admissionregistration.TypeChecking{
- ExpressionWarnings: []admissionregistration.ExpressionWarning{
- {
- Warning: "message",
- },
- },
- },
+ expectedError: "Required value",
+ }, {
+ name: "type checking missing fieldRef",
+ status: &admissionregistration.ValidatingAdmissionPolicyStatus{
+ TypeChecking: &admissionregistration.TypeChecking{
+ ExpressionWarnings: []admissionregistration.ExpressionWarning{{
+ Warning: "message",
+ }},
},
- expectedError: "Required value",
},
+ expectedError: "Required value",
+ },
} {
t.Run(tc.name, func(t *testing.T) {
errs := validateValidatingAdmissionPolicyStatus(tc.status, field.NewPath("status"))
diff --git a/pkg/apis/apiserverinternal/validation/validation_test.go b/pkg/apis/apiserverinternal/validation/validation_test.go
index 0d0b3bae893..a2bbae659d2 100644
--- a/pkg/apis/apiserverinternal/validation/validation_test.go
+++ b/pkg/apis/apiserverinternal/validation/validation_test.go
@@ -28,96 +28,84 @@ func TestValidateServerStorageVersion(t *testing.T) {
cases := []struct {
ssv apiserverinternal.ServerStorageVersion
expectedErr string
- }{
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "-fea",
- EncodingVersion: "v1alpha1",
- DecodableVersions: []string{"v1alpha1", "v1"},
- },
- expectedErr: "apiServerID: Invalid value",
+ }{{
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "-fea",
+ EncodingVersion: "v1alpha1",
+ DecodableVersions: []string{"v1alpha1", "v1"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "v1alpha1",
- DecodableVersions: []string{"v1beta1", "v1"},
- },
- expectedErr: "decodableVersions must include encodingVersion",
+ expectedErr: "apiServerID: Invalid value",
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "v1alpha1",
+ DecodableVersions: []string{"v1beta1", "v1"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "v1alpha1",
- DecodableVersions: []string{"v1alpha1", "v1", "-fea"},
- },
- expectedErr: "decodableVersions[2]: Invalid value",
+ expectedErr: "decodableVersions must include encodingVersion",
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "v1alpha1",
+ DecodableVersions: []string{"v1alpha1", "v1", "-fea"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "v1alpha1",
- DecodableVersions: []string{"v1alpha1", "v1"},
- },
- expectedErr: "",
+ expectedErr: "decodableVersions[2]: Invalid value",
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "v1alpha1",
+ DecodableVersions: []string{"v1alpha1", "v1"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "mygroup.com/v2",
- DecodableVersions: []string{"v1alpha1", "v1", "mygroup.com/v2"},
- },
- expectedErr: "",
+ expectedErr: "",
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "mygroup.com/v2",
+ DecodableVersions: []string{"v1alpha1", "v1", "mygroup.com/v2"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "mygroup.com/v2",
- DecodableVersions: []string{"mygroup.com/v2", "/v3"},
- },
- expectedErr: `[].decodableVersions[1]: Invalid value: "/v3": group part: must be non-empty`,
+ expectedErr: "",
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "mygroup.com/v2",
+ DecodableVersions: []string{"mygroup.com/v2", "/v3"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "mygroup.com/v2",
- DecodableVersions: []string{"mygroup.com/v2", "mygroup.com/"},
- },
- expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup.com/": version part: must be non-empty`,
+ expectedErr: `[].decodableVersions[1]: Invalid value: "/v3": group part: must be non-empty`,
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "mygroup.com/v2",
+ DecodableVersions: []string{"mygroup.com/v2", "mygroup.com/"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "/v3",
- DecodableVersions: []string{"mygroup.com/v2", "/v3"},
- },
- expectedErr: `[].encodingVersion: Invalid value: "/v3": group part: must be non-empty`,
+ expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup.com/": version part: must be non-empty`,
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "/v3",
+ DecodableVersions: []string{"mygroup.com/v2", "/v3"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "v1",
- DecodableVersions: []string{"v1", "mygroup_com/v2"},
- },
- expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup_com/v2": group part: 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])?)*')`,
+ expectedErr: `[].encodingVersion: Invalid value: "/v3": group part: must be non-empty`,
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "v1",
+ DecodableVersions: []string{"v1", "mygroup_com/v2"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "v1",
- DecodableVersions: []string{"v1", "mygroup.com/v2_"},
- },
- expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup.com/v2_": version part: a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')`,
+ expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup_com/v2": group part: 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])?)*')`,
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "v1",
+ DecodableVersions: []string{"v1", "mygroup.com/v2_"},
},
- {
- ssv: apiserverinternal.ServerStorageVersion{
- APIServerID: "fea",
- EncodingVersion: "v1",
- DecodableVersions: []string{"v1", "mygroup.com/v2/myresource"},
- },
- expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup.com/v2/myresource": an apiVersion is a DNS-1035 label, which must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyVersion')`,
+ expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup.com/v2_": version part: a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?')`,
+ }, {
+ ssv: apiserverinternal.ServerStorageVersion{
+ APIServerID: "fea",
+ EncodingVersion: "v1",
+ DecodableVersions: []string{"v1", "mygroup.com/v2/myresource"},
},
- }
+ expectedErr: `[].decodableVersions[1]: Invalid value: "mygroup.com/v2/myresource": an apiVersion is a DNS-1035 label, which must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character (e.g. 'my-name', or 'abc-123', regex used for validation is '[a-z]([-a-z0-9]*[a-z0-9])?') with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyVersion')`,
+ }}
for _, tc := range cases {
err := validateServerStorageVersion(tc.ssv, field.NewPath("")).ToAggregate()
@@ -142,91 +130,70 @@ func TestValidateCommonVersion(t *testing.T) {
cases := []struct {
status apiserverinternal.StorageVersionStatus
expectedErr string
- }{
- {
- status: apiserverinternal.StorageVersionStatus{
- StorageVersions: []apiserverinternal.ServerStorageVersion{},
- CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
- },
- expectedErr: "should be nil if servers do not agree on the same encoding version, or if there is no server reporting the supported versions yet",
+ }{{
+ status: apiserverinternal.StorageVersionStatus{
+ StorageVersions: []apiserverinternal.ServerStorageVersion{},
+ CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
},
- {
- status: apiserverinternal.StorageVersionStatus{
- StorageVersions: []apiserverinternal.ServerStorageVersion{
- {
- APIServerID: "1",
- EncodingVersion: "v1alpha1",
- },
- {
- APIServerID: "2",
- EncodingVersion: "v1",
- },
- },
- CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
- },
- expectedErr: "should be nil if servers do not agree on the same encoding version, or if there is no server reporting the supported versions yet",
+ expectedErr: "should be nil if servers do not agree on the same encoding version, or if there is no server reporting the supported versions yet",
+ }, {
+ status: apiserverinternal.StorageVersionStatus{
+ StorageVersions: []apiserverinternal.ServerStorageVersion{{
+ APIServerID: "1",
+ EncodingVersion: "v1alpha1",
+ }, {
+ APIServerID: "2",
+ EncodingVersion: "v1",
+ }},
+ CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
},
- {
- status: apiserverinternal.StorageVersionStatus{
- StorageVersions: []apiserverinternal.ServerStorageVersion{
- {
- APIServerID: "1",
- EncodingVersion: "v1alpha1",
- },
- {
- APIServerID: "2",
- EncodingVersion: "v1alpha1",
- },
- },
- CommonEncodingVersion: nil,
- },
- expectedErr: "Invalid value: \"null\": the common encoding version is v1alpha1",
+ expectedErr: "should be nil if servers do not agree on the same encoding version, or if there is no server reporting the supported versions yet",
+ }, {
+ status: apiserverinternal.StorageVersionStatus{
+ StorageVersions: []apiserverinternal.ServerStorageVersion{{
+ APIServerID: "1",
+ EncodingVersion: "v1alpha1",
+ }, {
+ APIServerID: "2",
+ EncodingVersion: "v1alpha1",
+ }},
+ CommonEncodingVersion: nil,
},
- {
- status: apiserverinternal.StorageVersionStatus{
- StorageVersions: []apiserverinternal.ServerStorageVersion{
- {
- APIServerID: "1",
- EncodingVersion: "v1alpha1",
- },
- {
- APIServerID: "2",
- EncodingVersion: "v1alpha1",
- },
- },
- CommonEncodingVersion: func() *string { a := "v1"; return &a }(),
- },
- expectedErr: "Invalid value: \"v1\": the actual common encoding version is v1alpha1",
+ expectedErr: "Invalid value: \"null\": the common encoding version is v1alpha1",
+ }, {
+ status: apiserverinternal.StorageVersionStatus{
+ StorageVersions: []apiserverinternal.ServerStorageVersion{{
+ APIServerID: "1",
+ EncodingVersion: "v1alpha1",
+ }, {
+ APIServerID: "2",
+ EncodingVersion: "v1alpha1",
+ }},
+ CommonEncodingVersion: func() *string { a := "v1"; return &a }(),
},
- {
- status: apiserverinternal.StorageVersionStatus{
- StorageVersions: []apiserverinternal.ServerStorageVersion{
- {
- APIServerID: "1",
- EncodingVersion: "v1alpha1",
- },
- {
- APIServerID: "2",
- EncodingVersion: "v1alpha1",
- },
- },
- CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
- },
- expectedErr: "",
+ expectedErr: "Invalid value: \"v1\": the actual common encoding version is v1alpha1",
+ }, {
+ status: apiserverinternal.StorageVersionStatus{
+ StorageVersions: []apiserverinternal.ServerStorageVersion{{
+ APIServerID: "1",
+ EncodingVersion: "v1alpha1",
+ }, {
+ APIServerID: "2",
+ EncodingVersion: "v1alpha1",
+ }},
+ CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
},
- {
- status: apiserverinternal.StorageVersionStatus{
- StorageVersions: []apiserverinternal.ServerStorageVersion{
- {
- APIServerID: "1",
- EncodingVersion: "v1alpha1",
- },
- },
- CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
- },
- expectedErr: "",
+ expectedErr: "",
+ }, {
+ status: apiserverinternal.StorageVersionStatus{
+ StorageVersions: []apiserverinternal.ServerStorageVersion{{
+ APIServerID: "1",
+ EncodingVersion: "v1alpha1",
+ }},
+ CommonEncodingVersion: func() *string { a := "v1alpha1"; return &a }(),
},
- }
+ expectedErr: "",
+ }}
for _, tc := range cases {
err := validateCommonVersion(tc.status, field.NewPath(""))
if err == nil && len(tc.expectedErr) == 0 {
@@ -250,78 +217,58 @@ func TestValidateStorageVersionCondition(t *testing.T) {
cases := []struct {
conditions []apiserverinternal.StorageVersionCondition
expectedErr string
- }{
- {
- conditions: []apiserverinternal.StorageVersionCondition{
- {
- Type: "-fea",
- Status: "True",
- Reason: "unknown",
- Message: "unknown",
- },
- },
- expectedErr: "type: Invalid value",
- },
- {
- conditions: []apiserverinternal.StorageVersionCondition{
- {
- Type: "fea",
- Status: "-True",
- Reason: "unknown",
- Message: "unknown",
- },
- },
- expectedErr: "status: Invalid value",
- },
- {
- conditions: []apiserverinternal.StorageVersionCondition{
- {
- Type: "fea",
- Status: "True",
- Message: "unknown",
- },
- },
- expectedErr: "Required value: reason cannot be empty",
- },
- {
- conditions: []apiserverinternal.StorageVersionCondition{
- {
- Type: "fea",
- Status: "True",
- Reason: "unknown",
- },
- },
- expectedErr: "Required value: message cannot be empty",
- },
- {
- conditions: []apiserverinternal.StorageVersionCondition{
- {
- Type: "fea",
- Status: "True",
- Reason: "unknown",
- Message: "unknown",
- },
- {
- Type: "fea",
- Status: "True",
- Reason: "unknown",
- Message: "unknown",
- },
- },
- expectedErr: `"fea": the type of the condition is not unique, it also appears in conditions[0]`,
- },
- {
- conditions: []apiserverinternal.StorageVersionCondition{
- {
- Type: "fea",
- Status: "True",
- Reason: "unknown",
- Message: "unknown",
- },
- },
- expectedErr: "",
- },
- }
+ }{{
+ conditions: []apiserverinternal.StorageVersionCondition{{
+ Type: "-fea",
+ Status: "True",
+ Reason: "unknown",
+ Message: "unknown",
+ }},
+ expectedErr: "type: Invalid value",
+ }, {
+ conditions: []apiserverinternal.StorageVersionCondition{{
+ Type: "fea",
+ Status: "-True",
+ Reason: "unknown",
+ Message: "unknown",
+ }},
+ expectedErr: "status: Invalid value",
+ }, {
+ conditions: []apiserverinternal.StorageVersionCondition{{
+ Type: "fea",
+ Status: "True",
+ Message: "unknown",
+ }},
+ expectedErr: "Required value: reason cannot be empty",
+ }, {
+ conditions: []apiserverinternal.StorageVersionCondition{{
+ Type: "fea",
+ Status: "True",
+ Reason: "unknown",
+ }},
+ expectedErr: "Required value: message cannot be empty",
+ }, {
+ conditions: []apiserverinternal.StorageVersionCondition{{
+ Type: "fea",
+ Status: "True",
+ Reason: "unknown",
+ Message: "unknown",
+ }, {
+ Type: "fea",
+ Status: "True",
+ Reason: "unknown",
+ Message: "unknown",
+ }},
+ expectedErr: `"fea": the type of the condition is not unique, it also appears in conditions[0]`,
+ }, {
+ conditions: []apiserverinternal.StorageVersionCondition{{
+ Type: "fea",
+ Status: "True",
+ Reason: "unknown",
+ Message: "unknown",
+ }},
+ expectedErr: "",
+ }}
for _, tc := range cases {
err := validateStorageVersionCondition(tc.conditions, field.NewPath("")).ToAggregate()
if err == nil && len(tc.expectedErr) == 0 {
@@ -345,40 +292,31 @@ func TestValidateStorageVersionName(t *testing.T) {
cases := []struct {
name string
expectedErr string
- }{
- {
- name: "",
- expectedErr: `name must be in the form of .`,
- },
- {
- name: "pods",
- expectedErr: `name must be in the form of .`,
- },
- {
- name: "core.pods",
- expectedErr: "",
- },
- {
- name: "authentication.k8s.io.tokenreviews",
- expectedErr: "",
- },
- {
- name: strings.Repeat("x", 253) + ".tokenreviews",
- expectedErr: "",
- },
- {
- name: strings.Repeat("x", 254) + ".tokenreviews",
- expectedErr: `the group segment must be no more than 253 characters`,
- },
- {
- name: "authentication.k8s.io." + strings.Repeat("x", 63),
- expectedErr: "",
- },
- {
- name: "authentication.k8s.io." + strings.Repeat("x", 64),
- expectedErr: `the resource segment must be no more than 63 characters`,
- },
- }
+ }{{
+ name: "",
+ expectedErr: `name must be in the form of .`,
+ }, {
+ name: "pods",
+ expectedErr: `name must be in the form of .`,
+ }, {
+ name: "core.pods",
+ expectedErr: "",
+ }, {
+ name: "authentication.k8s.io.tokenreviews",
+ expectedErr: "",
+ }, {
+ name: strings.Repeat("x", 253) + ".tokenreviews",
+ expectedErr: "",
+ }, {
+ name: strings.Repeat("x", 254) + ".tokenreviews",
+ expectedErr: `the group segment must be no more than 253 characters`,
+ }, {
+ name: "authentication.k8s.io." + strings.Repeat("x", 63),
+ expectedErr: "",
+ }, {
+ name: "authentication.k8s.io." + strings.Repeat("x", 64),
+ expectedErr: `the resource segment must be no more than 63 characters`,
+ }}
for _, tc := range cases {
errs := ValidateStorageVersionName(tc.name, false)
if errs == nil && len(tc.expectedErr) == 0 {
diff --git a/pkg/apis/apps/validation/validation_test.go b/pkg/apis/apps/validation/validation_test.go
index 7b0ad6aefc2..c79ab04c827 100644
--- a/pkg/apis/apps/validation/validation_test.go
+++ b/pkg/apis/apps/validation/validation_test.go
@@ -248,291 +248,257 @@ func TestValidateStatefulSet(t *testing.T) {
errs field.ErrorList
}
- successCases := []testCase{
- {
- name: "alpha name",
- set: mkStatefulSet(&validPodTemplate, tweakName("abc")),
- },
- {
- name: "alphanumeric name",
- set: mkStatefulSet(&validPodTemplate, tweakName("abc-123")),
- },
- {
- name: "parallel pod management",
- set: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.ParallelPodManagement)),
- },
- {
- name: "ordered ready pod management",
- set: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.OrderedReadyPodManagement)),
- },
- {
- name: "update strategy",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
- tweakRollingUpdatePartition(2),
- ),
- },
- {
- name: "PVC policy " + enableStatefulSetAutoDeletePVC,
- set: mkStatefulSet(&validPodTemplate,
- tweakPVCPolicy(mkPVCPolicy(
- tweakPVCDeletedPolicy(apps.DeletePersistentVolumeClaimRetentionPolicyType),
- tweakPVCScalePolicy(apps.RetainPersistentVolumeClaimRetentionPolicyType),
- )),
- ),
- },
- {
- name: "maxUnavailable with parallel pod management",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
- tweakRollingUpdatePartition(2),
- tweakMaxUnavailable(intstr.FromInt32(2)),
- ),
- },
- {
- name: "ordinals.start positive value",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakOrdinalsStart(2),
- ),
- },
+ successCases := []testCase{{
+ name: "alpha name",
+ set: mkStatefulSet(&validPodTemplate, tweakName("abc")),
+ }, {
+ name: "alphanumeric name",
+ set: mkStatefulSet(&validPodTemplate, tweakName("abc-123")),
+ }, {
+ name: "parallel pod management",
+ set: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.ParallelPodManagement)),
+ }, {
+ name: "ordered ready pod management",
+ set: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.OrderedReadyPodManagement)),
+ }, {
+ name: "update strategy",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
+ tweakRollingUpdatePartition(2),
+ ),
+ }, {
+ name: "PVC policy " + enableStatefulSetAutoDeletePVC,
+ set: mkStatefulSet(&validPodTemplate,
+ tweakPVCPolicy(mkPVCPolicy(
+ tweakPVCDeletedPolicy(apps.DeletePersistentVolumeClaimRetentionPolicyType),
+ tweakPVCScalePolicy(apps.RetainPersistentVolumeClaimRetentionPolicyType),
+ )),
+ ),
+ }, {
+ name: "maxUnavailable with parallel pod management",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
+ tweakRollingUpdatePartition(2),
+ tweakMaxUnavailable(intstr.FromInt32(2)),
+ ),
+ }, {
+ name: "ordinals.start positive value",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakOrdinalsStart(2),
+ ),
+ },
}
- errorCases := []testCase{
- {
- name: "zero-length name",
- set: mkStatefulSet(&validPodTemplate, tweakName("")),
- errs: field.ErrorList{
- field.Required(field.NewPath("metadata", "name"), ""),
- },
+ errorCases := []testCase{{
+ name: "zero-length name",
+ set: mkStatefulSet(&validPodTemplate, tweakName("")),
+ errs: field.ErrorList{
+ field.Required(field.NewPath("metadata", "name"), ""),
},
- {
- name: "name-with-dots",
- set: mkStatefulSet(&validPodTemplate, tweakName("abc.123")),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "name"), "abc.123", ""),
- },
+ }, {
+ name: "name-with-dots",
+ set: mkStatefulSet(&validPodTemplate, tweakName("abc.123")),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "name"), "abc.123", ""),
},
- {
- name: "long name",
- set: mkStatefulSet(&validPodTemplate, tweakName(strings.Repeat("a", 64))),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "name"), strings.Repeat("a", 64), ""),
- },
+ }, {
+ name: "long name",
+ set: mkStatefulSet(&validPodTemplate, tweakName(strings.Repeat("a", 64))),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "name"), strings.Repeat("a", 64), ""),
},
- {
- name: "missing-namespace",
- set: mkStatefulSet(&validPodTemplate, tweakNamespace("")),
- errs: field.ErrorList{
- field.Required(field.NewPath("metadata", "namespace"), ""),
- },
+ }, {
+ name: "missing-namespace",
+ set: mkStatefulSet(&validPodTemplate, tweakNamespace("")),
+ errs: field.ErrorList{
+ field.Required(field.NewPath("metadata", "namespace"), ""),
},
- {
- name: "empty selector",
- set: mkStatefulSet(&validPodTemplate, tweakSelectorLabels(nil)),
- errs: field.ErrorList{
- field.Required(field.NewPath("spec", "selector"), ""),
- field.Invalid(field.NewPath("spec", "template", "metadata", "labels"), nil, ""), // selector is empty, labels are not, so select doesn't match labels
- },
+ }, {
+ name: "empty selector",
+ set: mkStatefulSet(&validPodTemplate, tweakSelectorLabels(nil)),
+ errs: field.ErrorList{
+ field.Required(field.NewPath("spec", "selector"), ""),
+ field.Invalid(field.NewPath("spec", "template", "metadata", "labels"), nil, ""), // selector is empty, labels are not, so select doesn't match labels
},
- {
- name: "selector_doesnt_match",
- set: mkStatefulSet(&validPodTemplate, tweakSelectorLabels(map[string]string{"foo": "bar"})),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "template", "metadata", "labels"), nil, ""),
- },
+ }, {
+ name: "selector_doesnt_match",
+ set: mkStatefulSet(&validPodTemplate, tweakSelectorLabels(map[string]string{"foo": "bar"})),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "template", "metadata", "labels"), nil, ""),
},
- {
- name: "negative_replicas",
- set: mkStatefulSet(&validPodTemplate, tweakReplicas(-1)),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "replicas"), nil, ""),
- },
+ }, {
+ name: "negative_replicas",
+ set: mkStatefulSet(&validPodTemplate, tweakReplicas(-1)),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "replicas"), nil, ""),
},
- {
- name: "invalid_label",
- set: mkStatefulSet(&validPodTemplate,
- tweakLabels("NoUppercaseOrSpecialCharsLike=Equals", "bar"),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "labels"), nil, ""),
- },
+ }, {
+ name: "invalid_label",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakLabels("NoUppercaseOrSpecialCharsLike=Equals", "bar"),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "labels"), nil, ""),
},
- {
- name: "invalid_label 2",
- set: mkStatefulSet(&invalidPodTemplate,
- tweakLabels("NoUppercaseOrSpecialCharsLike=Equals", "bar"),
- tweakSelectorLabels(invalidLabels),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "labels"), nil, ""),
- field.Invalid(field.NewPath("spec", "selector"), nil, ""),
- field.Invalid(field.NewPath("spec", "selector", "matchLabels"), nil, ""),
- },
+ }, {
+ name: "invalid_label 2",
+ set: mkStatefulSet(&invalidPodTemplate,
+ tweakLabels("NoUppercaseOrSpecialCharsLike=Equals", "bar"),
+ tweakSelectorLabels(invalidLabels),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "labels"), nil, ""),
+ field.Invalid(field.NewPath("spec", "selector"), nil, ""),
+ field.Invalid(field.NewPath("spec", "selector", "matchLabels"), nil, ""),
},
- {
- name: "invalid_annotation",
- set: mkStatefulSet(&validPodTemplate,
- tweakAnnotations("NoUppercaseOrSpecialCharsLike=Equals", "bar"),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "annotations"), nil, ""),
- },
+ }, {
+ name: "invalid_annotation",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakAnnotations("NoUppercaseOrSpecialCharsLike=Equals", "bar"),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "annotations"), nil, ""),
},
- {
- name: "invalid restart policy 1",
- set: mkStatefulSet(&validPodTemplate, tweakTemplateRestartPolicy(api.RestartPolicyOnFailure)),
- errs: field.ErrorList{
- field.NotSupported(field.NewPath("spec", "template", "spec", "restartPolicy"), nil, nil),
- },
+ }, {
+ name: "invalid restart policy 1",
+ set: mkStatefulSet(&validPodTemplate, tweakTemplateRestartPolicy(api.RestartPolicyOnFailure)),
+ errs: field.ErrorList{
+ field.NotSupported(field.NewPath("spec", "template", "spec", "restartPolicy"), nil, nil),
},
- {
- name: "invalid restart policy 2",
- set: mkStatefulSet(&validPodTemplate, tweakTemplateRestartPolicy(api.RestartPolicyNever)),
- errs: field.ErrorList{
- field.NotSupported(field.NewPath("spec", "template", "spec", "restartPolicy"), nil, nil),
- },
+ }, {
+ name: "invalid restart policy 2",
+ set: mkStatefulSet(&validPodTemplate, tweakTemplateRestartPolicy(api.RestartPolicyNever)),
+ errs: field.ErrorList{
+ field.NotSupported(field.NewPath("spec", "template", "spec", "restartPolicy"), nil, nil),
},
- {
- name: "empty restart policy",
- set: mkStatefulSet(&validPodTemplate, tweakTemplateRestartPolicy("")),
- errs: field.ErrorList{
- field.NotSupported(field.NewPath("spec", "template", "spec", "restartPolicy"), nil, nil),
- },
+ }, {
+ name: "empty restart policy",
+ set: mkStatefulSet(&validPodTemplate, tweakTemplateRestartPolicy("")),
+ errs: field.ErrorList{
+ field.NotSupported(field.NewPath("spec", "template", "spec", "restartPolicy"), nil, nil),
},
- {
- name: "invalid update strategy",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakUpdateStrategyType("foo"),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "updateStrategy"), nil, ""),
- },
+ }, {
+ name: "invalid update strategy",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakUpdateStrategyType("foo"),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "updateStrategy"), nil, ""),
},
- {
- name: "empty update strategy",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakUpdateStrategyType(""),
- ),
- errs: field.ErrorList{
- field.Required(field.NewPath("spec", "updateStrategy"), ""),
- },
+ }, {
+ name: "empty update strategy",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakUpdateStrategyType(""),
+ ),
+ errs: field.ErrorList{
+ field.Required(field.NewPath("spec", "updateStrategy"), ""),
},
- {
- name: "invalid rolling update",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakUpdateStrategyType(apps.OnDeleteStatefulSetStrategyType),
- tweakRollingUpdatePartition(1),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate"), nil, ""),
- },
+ }, {
+ name: "invalid rolling update",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakUpdateStrategyType(apps.OnDeleteStatefulSetStrategyType),
+ tweakRollingUpdatePartition(1),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate"), nil, ""),
},
- {
- name: "negative parition",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakRollingUpdatePartition(-1),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate", "partition"), nil, ""),
- },
+ }, {
+ name: "negative parition",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakRollingUpdatePartition(-1),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate", "partition"), nil, ""),
},
- {
- name: "empty pod management policy",
- set: mkStatefulSet(&validPodTemplate,
- tweakPodManagementPolicy(""),
- tweakReplicas(3),
- ),
- errs: field.ErrorList{
- field.Required(field.NewPath("spec", "podManagementPolicy"), ""),
- },
+ }, {
+ name: "empty pod management policy",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakPodManagementPolicy(""),
+ tweakReplicas(3),
+ ),
+ errs: field.ErrorList{
+ field.Required(field.NewPath("spec", "podManagementPolicy"), ""),
},
- {
- name: "invalid pod management policy",
- set: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy("foo")),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "podManagementPolicy"), nil, ""),
- },
+ }, {
+ name: "invalid pod management policy",
+ set: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy("foo")),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "podManagementPolicy"), nil, ""),
},
- {
- name: "set active deadline seconds",
- set: mkStatefulSet(&invalidPodTemplate2, tweakReplicas(3)),
- errs: field.ErrorList{
- field.Forbidden(field.NewPath("spec", "template", "spec", "activeDeadlineSeconds"), ""),
- },
+ }, {
+ name: "set active deadline seconds",
+ set: mkStatefulSet(&invalidPodTemplate2, tweakReplicas(3)),
+ errs: field.ErrorList{
+ field.Forbidden(field.NewPath("spec", "template", "spec", "activeDeadlineSeconds"), ""),
},
- {
- name: "empty PersistentVolumeClaimRetentionPolicy " + enableStatefulSetAutoDeletePVC,
- set: mkStatefulSet(&validPodTemplate,
- tweakPVCPolicy(mkPVCPolicy()),
- ),
- errs: field.ErrorList{
- field.NotSupported(field.NewPath("spec", "persistentVolumeClaimRetentionPolicy", "whenDeleted"), nil, nil),
- field.NotSupported(field.NewPath("spec", "persistentVolumeClaimRetentionPolicy", "whenScaled"), nil, nil),
- },
+ }, {
+ name: "empty PersistentVolumeClaimRetentionPolicy " + enableStatefulSetAutoDeletePVC,
+ set: mkStatefulSet(&validPodTemplate,
+ tweakPVCPolicy(mkPVCPolicy()),
+ ),
+ errs: field.ErrorList{
+ field.NotSupported(field.NewPath("spec", "persistentVolumeClaimRetentionPolicy", "whenDeleted"), nil, nil),
+ field.NotSupported(field.NewPath("spec", "persistentVolumeClaimRetentionPolicy", "whenScaled"), nil, nil),
},
- {
- name: "invalid PersistentVolumeClaimRetentionPolicy " + enableStatefulSetAutoDeletePVC,
- set: mkStatefulSet(&validPodTemplate,
- tweakPVCPolicy(mkPVCPolicy(
- tweakPVCDeletedPolicy("invalid-retention-policy"),
- tweakPVCScalePolicy("invalid-retention-policy"),
- )),
- ),
- errs: field.ErrorList{
- field.NotSupported(field.NewPath("spec", "persistentVolumeClaimRetentionPolicy", "whenDeleted"), nil, nil),
- field.NotSupported(field.NewPath("spec", "persistentVolumeClaimRetentionPolicy", "whenScaled"), nil, nil),
- },
+ }, {
+ name: "invalid PersistentVolumeClaimRetentionPolicy " + enableStatefulSetAutoDeletePVC,
+ set: mkStatefulSet(&validPodTemplate,
+ tweakPVCPolicy(mkPVCPolicy(
+ tweakPVCDeletedPolicy("invalid-retention-policy"),
+ tweakPVCScalePolicy("invalid-retention-policy"),
+ )),
+ ),
+ errs: field.ErrorList{
+ field.NotSupported(field.NewPath("spec", "persistentVolumeClaimRetentionPolicy", "whenDeleted"), nil, nil),
+ field.NotSupported(field.NewPath("spec", "persistentVolumeClaimRetentionPolicy", "whenScaled"), nil, nil),
},
- {
- name: "zero maxUnavailable",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
- tweakMaxUnavailable(intstr.FromInt32(0)),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate", "maxUnavailable"), nil, ""),
- },
+ }, {
+ name: "zero maxUnavailable",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
+ tweakMaxUnavailable(intstr.FromInt32(0)),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate", "maxUnavailable"), nil, ""),
},
- {
- name: "zero percent maxUnavailable",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
- tweakMaxUnavailable(intstr.FromString("0%")),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate", "maxUnavailable"), nil, ""),
- },
+ }, {
+ name: "zero percent maxUnavailable",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
+ tweakMaxUnavailable(intstr.FromString("0%")),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate", "maxUnavailable"), nil, ""),
},
- {
- name: "greater than 100 percent maxUnavailable",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
- tweakMaxUnavailable(intstr.FromString("101%")),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate", "maxUnavailable"), nil, ""),
- },
+ }, {
+ name: "greater than 100 percent maxUnavailable",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakUpdateStrategyType(apps.RollingUpdateStatefulSetStrategyType),
+ tweakMaxUnavailable(intstr.FromString("101%")),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "updateStrategy", "rollingUpdate", "maxUnavailable"), nil, ""),
},
- {
- name: "invalid ordinals.start",
- set: mkStatefulSet(&validPodTemplate,
- tweakReplicas(3),
- tweakOrdinalsStart(-2),
- ),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "ordinals.start"), nil, ""),
- },
+ }, {
+ name: "invalid ordinals.start",
+ set: mkStatefulSet(&validPodTemplate,
+ tweakReplicas(3),
+ tweakOrdinalsStart(-2),
+ ),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "ordinals.start"), nil, ""),
},
+ },
}
cmpOpts := []cmp.Option{cmpopts.IgnoreFields(field.Error{}, "BadValue", "Detail"), cmpopts.SortSlices(func(a, b *field.Error) bool { return a.Error() < b.Error() })}
@@ -635,113 +601,100 @@ func TestValidateStatefulSetStatus(t *testing.T) {
observedGeneration *int64
collisionCount *int32
expectedErr bool
- }{
- {
- name: "valid status",
- replicas: 3,
- readyReplicas: 3,
- currentReplicas: 2,
- updatedReplicas: 1,
- expectedErr: false,
- },
- {
- name: "invalid replicas",
- replicas: -1,
- readyReplicas: 3,
- currentReplicas: 2,
- updatedReplicas: 1,
- expectedErr: true,
- },
- {
- name: "invalid readyReplicas",
- replicas: 3,
- readyReplicas: -1,
- currentReplicas: 2,
- updatedReplicas: 1,
- expectedErr: true,
- },
- {
- name: "invalid currentReplicas",
- replicas: 3,
- readyReplicas: 3,
- currentReplicas: -1,
- updatedReplicas: 1,
- expectedErr: true,
- },
- {
- name: "invalid updatedReplicas",
- replicas: 3,
- readyReplicas: 3,
- currentReplicas: 2,
- updatedReplicas: -1,
- expectedErr: true,
- },
- {
- name: "invalid observedGeneration",
- replicas: 3,
- readyReplicas: 3,
- currentReplicas: 2,
- updatedReplicas: 1,
- observedGeneration: &observedGenerationMinusOne,
- expectedErr: true,
- },
- {
- name: "invalid collisionCount",
- replicas: 3,
- readyReplicas: 3,
- currentReplicas: 2,
- updatedReplicas: 1,
- collisionCount: &collisionCountMinusOne,
- expectedErr: true,
- },
- {
- name: "readyReplicas greater than replicas",
- replicas: 3,
- readyReplicas: 4,
- currentReplicas: 2,
- updatedReplicas: 1,
- expectedErr: true,
- },
- {
- name: "currentReplicas greater than replicas",
- replicas: 3,
- readyReplicas: 3,
- currentReplicas: 4,
- updatedReplicas: 1,
- expectedErr: true,
- },
- {
- name: "updatedReplicas greater than replicas",
- replicas: 3,
- readyReplicas: 3,
- currentReplicas: 2,
- updatedReplicas: 4,
- expectedErr: true,
- },
- {
- name: "invalid: number of available replicas",
- replicas: 3,
- readyReplicas: 3,
- currentReplicas: 2,
- availableReplicas: int32(-1),
- expectedErr: true,
- },
- {
- name: "invalid: available replicas greater than replicas",
- replicas: 3,
- readyReplicas: 3,
- currentReplicas: 2,
- availableReplicas: int32(4),
- expectedErr: true,
- },
- {
- name: "invalid: available replicas greater than ready replicas",
- replicas: 3,
- readyReplicas: 2,
- currentReplicas: 2,
- availableReplicas: int32(3),
- expectedErr: true,
- },
+ }{{
+ name: "valid status",
+ replicas: 3,
+ readyReplicas: 3,
+ currentReplicas: 2,
+ updatedReplicas: 1,
+ expectedErr: false,
+ }, {
+ name: "invalid replicas",
+ replicas: -1,
+ readyReplicas: 3,
+ currentReplicas: 2,
+ updatedReplicas: 1,
+ expectedErr: true,
+ }, {
+ name: "invalid readyReplicas",
+ replicas: 3,
+ readyReplicas: -1,
+ currentReplicas: 2,
+ updatedReplicas: 1,
+ expectedErr: true,
+ }, {
+ name: "invalid currentReplicas",
+ replicas: 3,
+ readyReplicas: 3,
+ currentReplicas: -1,
+ updatedReplicas: 1,
+ expectedErr: true,
+ }, {
+ name: "invalid updatedReplicas",
+ replicas: 3,
+ readyReplicas: 3,
+ currentReplicas: 2,
+ updatedReplicas: -1,
+ expectedErr: true,
+ }, {
+ name: "invalid observedGeneration",
+ replicas: 3,
+ readyReplicas: 3,
+ currentReplicas: 2,
+ updatedReplicas: 1,
+ observedGeneration: &observedGenerationMinusOne,
+ expectedErr: true,
+ }, {
+ name: "invalid collisionCount",
+ replicas: 3,
+ readyReplicas: 3,
+ currentReplicas: 2,
+ updatedReplicas: 1,
+ collisionCount: &collisionCountMinusOne,
+ expectedErr: true,
+ }, {
+ name: "readyReplicas greater than replicas",
+ replicas: 3,
+ readyReplicas: 4,
+ currentReplicas: 2,
+ updatedReplicas: 1,
+ expectedErr: true,
+ }, {
+ name: "currentReplicas greater than replicas",
+ replicas: 3,
+ readyReplicas: 3,
+ currentReplicas: 4,
+ updatedReplicas: 1,
+ expectedErr: true,
+ }, {
+ name: "updatedReplicas greater than replicas",
+ replicas: 3,
+ readyReplicas: 3,
+ currentReplicas: 2,
+ updatedReplicas: 4,
+ expectedErr: true,
+ }, {
+ name: "invalid: number of available replicas",
+ replicas: 3,
+ readyReplicas: 3,
+ currentReplicas: 2,
+ availableReplicas: int32(-1),
+ expectedErr: true,
+ }, {
+ name: "invalid: available replicas greater than replicas",
+ replicas: 3,
+ readyReplicas: 3,
+ currentReplicas: 2,
+ availableReplicas: int32(4),
+ expectedErr: true,
+ }, {
+ name: "invalid: available replicas greater than ready replicas",
+ replicas: 3,
+ readyReplicas: 2,
+ currentReplicas: 2,
+ availableReplicas: int32(3),
+ expectedErr: true,
+ },
}
for _, test := range tests {
@@ -842,145 +795,126 @@ func TestValidateStatefulSetUpdate(t *testing.T) {
errs field.ErrorList
}
- successCases := []testCase{
- {
- name: "update replica count",
- old: mkStatefulSet(&validPodTemplate),
- update: mkStatefulSet(&validPodTemplate, tweakReplicas(3)),
- },
- {
- name: "update containers 1",
- old: mkStatefulSet(&validPodTemplate),
- update: mkStatefulSet(addContainersValidTemplate),
- },
- {
- name: "update containers 2",
- old: mkStatefulSet(addContainersValidTemplate),
- update: mkStatefulSet(&validPodTemplate),
- },
- {
- name: "update containers and pvc retention policy 1",
- old: mkStatefulSet(addContainersValidTemplate),
- update: mkStatefulSet(&validPodTemplate,
- tweakPVCPolicy(mkPVCPolicy(
- tweakPVCDeletedPolicy(apps.RetainPersistentVolumeClaimRetentionPolicyType),
- tweakPVCScalePolicy(apps.RetainPersistentVolumeClaimRetentionPolicyType),
- )),
- ),
- },
- {
- name: "update containers and pvc retention policy 2",
- old: mkStatefulSet(&validPodTemplate,
- tweakPVCPolicy(mkPVCPolicy(
- tweakPVCScalePolicy(apps.RetainPersistentVolumeClaimRetentionPolicyType),
- )),
- ),
- update: mkStatefulSet(&validPodTemplate),
- },
- {
- name: "update update strategy",
- old: mkStatefulSet(&validPodTemplate),
- update: mkStatefulSet(&validPodTemplate,
- tweakUpdateStrategyType(apps.OnDeleteStatefulSetStrategyType),
- ),
- },
- {
- name: "update min ready seconds 1",
- old: mkStatefulSet(&validPodTemplate),
- update: mkStatefulSet(&validPodTemplate, tweakMinReadySeconds(10)),
- },
- {
- name: "update min ready seconds 2",
- old: mkStatefulSet(&validPodTemplate, tweakMinReadySeconds(5)),
- update: mkStatefulSet(&validPodTemplate, tweakMinReadySeconds(10)),
- },
- {
- name: "update existing instance with now-invalid name",
- old: mkStatefulSet(&validPodTemplate, tweakFinalizers("final")),
- update: mkStatefulSet(&validPodTemplate, tweakFinalizers()),
- },
- {
- name: "update existing instance with .spec.ordinals.start",
- old: mkStatefulSet(&validPodTemplate),
- update: mkStatefulSet(&validPodTemplate, tweakOrdinalsStart(3)),
- },
+ successCases := []testCase{{
+ name: "update replica count",
+ old: mkStatefulSet(&validPodTemplate),
+ update: mkStatefulSet(&validPodTemplate, tweakReplicas(3)),
+ }, {
+ name: "update containers 1",
+ old: mkStatefulSet(&validPodTemplate),
+ update: mkStatefulSet(addContainersValidTemplate),
+ }, {
+ name: "update containers 2",
+ old: mkStatefulSet(addContainersValidTemplate),
+ update: mkStatefulSet(&validPodTemplate),
+ }, {
+ name: "update containers and pvc retention policy 1",
+ old: mkStatefulSet(addContainersValidTemplate),
+ update: mkStatefulSet(&validPodTemplate,
+ tweakPVCPolicy(mkPVCPolicy(
+ tweakPVCDeletedPolicy(apps.RetainPersistentVolumeClaimRetentionPolicyType),
+ tweakPVCScalePolicy(apps.RetainPersistentVolumeClaimRetentionPolicyType),
+ )),
+ ),
+ }, {
+ name: "update containers and pvc retention policy 2",
+ old: mkStatefulSet(&validPodTemplate,
+ tweakPVCPolicy(mkPVCPolicy(
+ tweakPVCScalePolicy(apps.RetainPersistentVolumeClaimRetentionPolicyType),
+ )),
+ ),
+ update: mkStatefulSet(&validPodTemplate),
+ }, {
+ name: "update update strategy",
+ old: mkStatefulSet(&validPodTemplate),
+ update: mkStatefulSet(&validPodTemplate,
+ tweakUpdateStrategyType(apps.OnDeleteStatefulSetStrategyType),
+ ),
+ }, {
+ name: "update min ready seconds 1",
+ old: mkStatefulSet(&validPodTemplate),
+ update: mkStatefulSet(&validPodTemplate, tweakMinReadySeconds(10)),
+ }, {
+ name: "update min ready seconds 2",
+ old: mkStatefulSet(&validPodTemplate, tweakMinReadySeconds(5)),
+ update: mkStatefulSet(&validPodTemplate, tweakMinReadySeconds(10)),
+ }, {
+ name: "update existing instance with now-invalid name",
+ old: mkStatefulSet(&validPodTemplate, tweakFinalizers("final")),
+ update: mkStatefulSet(&validPodTemplate, tweakFinalizers()),
+ }, {
+ name: "update existing instance with .spec.ordinals.start",
+ old: mkStatefulSet(&validPodTemplate),
+ update: mkStatefulSet(&validPodTemplate, tweakOrdinalsStart(3)),
+ },
}
- errorCases := []testCase{
- {
- name: "update name",
- old: mkStatefulSet(&validPodTemplate, tweakName("abc")),
- update: mkStatefulSet(&validPodTemplate, tweakName("abc2")),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "name"), nil, ""),
- },
+ errorCases := []testCase{{
+ name: "update name",
+ old: mkStatefulSet(&validPodTemplate, tweakName("abc")),
+ update: mkStatefulSet(&validPodTemplate, tweakName("abc2")),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "name"), nil, ""),
},
- {
- name: "update namespace",
- old: mkStatefulSet(&validPodTemplate, tweakNamespace(metav1.NamespaceDefault)),
- update: mkStatefulSet(&validPodTemplate, tweakNamespace(metav1.NamespaceDefault+"1")),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "namespace"), nil, ""),
- },
+ }, {
+ name: "update namespace",
+ old: mkStatefulSet(&validPodTemplate, tweakNamespace(metav1.NamespaceDefault)),
+ update: mkStatefulSet(&validPodTemplate, tweakNamespace(metav1.NamespaceDefault+"1")),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "namespace"), nil, ""),
},
- {
- name: "update selector",
- old: mkStatefulSet(&validPodTemplate, tweakSelectorLabels(validLabels)),
- update: mkStatefulSet(&validPodTemplate2,
- tweakSelectorLabels(validLabels2),
- ),
- errs: field.ErrorList{
- field.Forbidden(field.NewPath("spec"), ""),
- },
+ }, {
+ name: "update selector",
+ old: mkStatefulSet(&validPodTemplate, tweakSelectorLabels(validLabels)),
+ update: mkStatefulSet(&validPodTemplate2,
+ tweakSelectorLabels(validLabels2),
+ ),
+ errs: field.ErrorList{
+ field.Forbidden(field.NewPath("spec"), ""),
},
- {
- name: "update pod management policy 1",
- old: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy("")),
- update: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.OrderedReadyPodManagement)),
- errs: field.ErrorList{
- field.Forbidden(field.NewPath("spec"), ""),
- },
+ }, {
+ name: "update pod management policy 1",
+ old: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy("")),
+ update: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.OrderedReadyPodManagement)),
+ errs: field.ErrorList{
+ field.Forbidden(field.NewPath("spec"), ""),
},
- {
- name: "update pod management policy 2",
- old: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.ParallelPodManagement)),
- update: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.OrderedReadyPodManagement)),
- errs: field.ErrorList{
- field.Forbidden(field.NewPath("spec"), ""),
- },
+ }, {
+ name: "update pod management policy 2",
+ old: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.ParallelPodManagement)),
+ update: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.OrderedReadyPodManagement)),
+ errs: field.ErrorList{
+ field.Forbidden(field.NewPath("spec"), ""),
},
- {
- name: "update to negative replicas",
- old: mkStatefulSet(&validPodTemplate),
- update: mkStatefulSet(&validPodTemplate, tweakReplicas(-1)),
- errs: field.ErrorList{
- field.Invalid(field.NewPath("spec", "replicas"), nil, ""),
- },
+ }, {
+ name: "update to negative replicas",
+ old: mkStatefulSet(&validPodTemplate),
+ update: mkStatefulSet(&validPodTemplate, tweakReplicas(-1)),
+ errs: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "replicas"), nil, ""),
},
- {
- name: "update pvc template size",
- old: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplate)),
- update: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplateChangedSize)),
- errs: field.ErrorList{
- field.Forbidden(field.NewPath("spec"), ""),
- },
+ }, {
+ name: "update pvc template size",
+ old: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplate)),
+ update: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplateChangedSize)),
+ errs: field.ErrorList{
+ field.Forbidden(field.NewPath("spec"), ""),
},
- {
- name: "update pvc template storage class",
- old: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplate)),
- update: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplateChangedClass)),
- errs: field.ErrorList{
- field.Forbidden(field.NewPath("spec"), ""),
- },
+ }, {
+ name: "update pvc template storage class",
+ old: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplate)),
+ update: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplateChangedClass)),
+ errs: field.ErrorList{
+ field.Forbidden(field.NewPath("spec"), ""),
},
- {
- name: "add new pvc template",
- old: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplate)),
- update: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplate, validPVCTemplate2)),
- errs: field.ErrorList{
- field.Forbidden(field.NewPath("spec"), ""),
- },
+ }, {
+ name: "add new pvc template",
+ old: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplate)),
+ update: mkStatefulSet(&validPodTemplate, tweakPVCTemplate(validPVCTemplate, validPVCTemplate2)),
+ errs: field.ErrorList{
+ field.Forbidden(field.NewPath("spec"), ""),
},
+ },
}
cmpOpts := []cmp.Option{
@@ -1112,31 +1046,27 @@ func TestValidateControllerRevisionUpdate(t *testing.T) {
newHistory apps.ControllerRevision
oldHistory apps.ControllerRevision
isValid bool
- }{
- {
- name: "valid",
- newHistory: valid,
- oldHistory: valid,
- isValid: true,
- },
- {
- name: "invalid",
- newHistory: noVersion,
- oldHistory: valid,
- isValid: false,
- },
- {
- name: "changed data",
- newHistory: changedData,
- oldHistory: valid,
- isValid: false,
- },
- {
- name: "changed revision",
- newHistory: changedRevision,
- oldHistory: valid,
- isValid: true,
- },
+ }{{
+ name: "valid",
+ newHistory: valid,
+ oldHistory: valid,
+ isValid: true,
+ }, {
+ name: "invalid",
+ newHistory: noVersion,
+ oldHistory: valid,
+ isValid: false,
+ }, {
+ name: "changed data",
+ newHistory: changedData,
+ oldHistory: valid,
+ isValid: false,
+ }, {
+ name: "changed revision",
+ newHistory: changedRevision,
+ oldHistory: valid,
+ isValid: true,
+ },
}
for _, tc := range cases {
@@ -1158,33 +1088,32 @@ func TestValidateDaemonSetStatusUpdate(t *testing.T) {
update apps.DaemonSet
}
- successCases := []dsUpdateTest{
- {
- old: apps.DaemonSet{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Status: apps.DaemonSetStatus{
- CurrentNumberScheduled: 1,
- NumberMisscheduled: 2,
- DesiredNumberScheduled: 3,
- NumberReady: 1,
- UpdatedNumberScheduled: 1,
- NumberAvailable: 1,
- NumberUnavailable: 2,
- },
- },
- update: apps.DaemonSet{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Status: apps.DaemonSetStatus{
- CurrentNumberScheduled: 1,
- NumberMisscheduled: 1,
- DesiredNumberScheduled: 3,
- NumberReady: 1,
- UpdatedNumberScheduled: 1,
- NumberAvailable: 1,
- NumberUnavailable: 2,
- },
+ successCases := []dsUpdateTest{{
+ old: apps.DaemonSet{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Status: apps.DaemonSetStatus{
+ CurrentNumberScheduled: 1,
+ NumberMisscheduled: 2,
+ DesiredNumberScheduled: 3,
+ NumberReady: 1,
+ UpdatedNumberScheduled: 1,
+ NumberAvailable: 1,
+ NumberUnavailable: 2,
},
},
+ update: apps.DaemonSet{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Status: apps.DaemonSetStatus{
+ CurrentNumberScheduled: 1,
+ NumberMisscheduled: 1,
+ DesiredNumberScheduled: 3,
+ NumberReady: 1,
+ UpdatedNumberScheduled: 1,
+ NumberAvailable: 1,
+ NumberUnavailable: 2,
+ },
+ },
+ },
}
for _, successCase := range successCases {
@@ -2071,27 +2000,25 @@ func TestValidateDaemonSet(t *testing.T) {
},
},
}
- successCases := []apps.DaemonSet{
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: apps.DaemonSetSpec{
- Selector: &metav1.LabelSelector{MatchLabels: validSelector},
- Template: validPodTemplate.Template,
- UpdateStrategy: apps.DaemonSetUpdateStrategy{
- Type: apps.OnDeleteDaemonSetStrategyType,
- },
+ successCases := []apps.DaemonSet{{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: apps.DaemonSetSpec{
+ Selector: &metav1.LabelSelector{MatchLabels: validSelector},
+ Template: validPodTemplate.Template,
+ UpdateStrategy: apps.DaemonSetUpdateStrategy{
+ Type: apps.OnDeleteDaemonSetStrategyType,
},
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
- Spec: apps.DaemonSetSpec{
- Selector: &metav1.LabelSelector{MatchLabels: validSelector},
- Template: validPodTemplate.Template,
- UpdateStrategy: apps.DaemonSetUpdateStrategy{
- Type: apps.OnDeleteDaemonSetStrategyType,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
+ Spec: apps.DaemonSetSpec{
+ Selector: &metav1.LabelSelector{MatchLabels: validSelector},
+ Template: validPodTemplate.Template,
+ UpdateStrategy: apps.DaemonSetUpdateStrategy{
+ Type: apps.OnDeleteDaemonSetStrategyType,
},
},
+ },
}
for _, successCase := range successCases {
if errs := ValidateDaemonSet(&successCase, corevalidation.PodValidationOptions{}); len(errs) != 0 {
@@ -2290,14 +2217,12 @@ func validDeployment() *apps.Deployment {
Spec: api.PodSpec{
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSDefault,
- Containers: []api.Container{
- {
- Name: "nginx",
- Image: "image",
- ImagePullPolicy: api.PullNever,
- TerminationMessagePolicy: api.TerminationMessageReadFile,
- },
- },
+ Containers: []api.Container{{
+ Name: "nginx",
+ Image: "image",
+ ImagePullPolicy: api.PullNever,
+ TerminationMessagePolicy: api.TerminationMessageReadFile,
+ }},
},
},
RollbackTo: &apps.RollbackConfig{
@@ -2427,98 +2352,87 @@ func TestValidateDeploymentStatus(t *testing.T) {
collisionCount *int32
expectedErr bool
- }{
- {
- name: "valid status",
- replicas: 3,
- updatedReplicas: 3,
- readyReplicas: 2,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: false,
- },
- {
- name: "invalid replicas",
- replicas: -1,
- updatedReplicas: 2,
- readyReplicas: 2,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid updatedReplicas",
- replicas: 2,
- updatedReplicas: -1,
- readyReplicas: 2,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid readyReplicas",
- replicas: 3,
- readyReplicas: -1,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid availableReplicas",
- replicas: 3,
- readyReplicas: 3,
- availableReplicas: -1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid observedGeneration",
- replicas: 3,
- readyReplicas: 3,
- availableReplicas: 3,
- observedGeneration: -1,
- expectedErr: true,
- },
- {
- name: "updatedReplicas greater than replicas",
- replicas: 3,
- updatedReplicas: 4,
- readyReplicas: 3,
- availableReplicas: 3,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "readyReplicas greater than replicas",
- replicas: 3,
- readyReplicas: 4,
- availableReplicas: 3,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "availableReplicas greater than replicas",
- replicas: 3,
- readyReplicas: 3,
- availableReplicas: 4,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "availableReplicas greater than readyReplicas",
- replicas: 3,
- readyReplicas: 2,
- availableReplicas: 3,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "invalid collisionCount",
- replicas: 3,
- observedGeneration: 1,
- collisionCount: &collisionCount,
- expectedErr: true,
- },
+ }{{
+ name: "valid status",
+ replicas: 3,
+ updatedReplicas: 3,
+ readyReplicas: 2,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: false,
+ }, {
+ name: "invalid replicas",
+ replicas: -1,
+ updatedReplicas: 2,
+ readyReplicas: 2,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid updatedReplicas",
+ replicas: 2,
+ updatedReplicas: -1,
+ readyReplicas: 2,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid readyReplicas",
+ replicas: 3,
+ readyReplicas: -1,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid availableReplicas",
+ replicas: 3,
+ readyReplicas: 3,
+ availableReplicas: -1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid observedGeneration",
+ replicas: 3,
+ readyReplicas: 3,
+ availableReplicas: 3,
+ observedGeneration: -1,
+ expectedErr: true,
+ }, {
+ name: "updatedReplicas greater than replicas",
+ replicas: 3,
+ updatedReplicas: 4,
+ readyReplicas: 3,
+ availableReplicas: 3,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "readyReplicas greater than replicas",
+ replicas: 3,
+ readyReplicas: 4,
+ availableReplicas: 3,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "availableReplicas greater than replicas",
+ replicas: 3,
+ readyReplicas: 3,
+ availableReplicas: 4,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "availableReplicas greater than readyReplicas",
+ replicas: 3,
+ readyReplicas: 2,
+ availableReplicas: 3,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "invalid collisionCount",
+ replicas: 3,
+ observedGeneration: 1,
+ collisionCount: &collisionCount,
+ expectedErr: true,
+ },
}
for _, test := range tests {
@@ -2548,47 +2462,43 @@ func TestValidateDeploymentStatusUpdate(t *testing.T) {
from, to apps.DeploymentStatus
expectedErr bool
- }{
- {
- name: "increase: valid update",
- from: apps.DeploymentStatus{
- CollisionCount: nil,
- },
- to: apps.DeploymentStatus{
- CollisionCount: &collisionCount,
- },
- expectedErr: false,
+ }{{
+ name: "increase: valid update",
+ from: apps.DeploymentStatus{
+ CollisionCount: nil,
},
- {
- name: "stable: valid update",
- from: apps.DeploymentStatus{
- CollisionCount: &collisionCount,
- },
- to: apps.DeploymentStatus{
- CollisionCount: &collisionCount,
- },
- expectedErr: false,
+ to: apps.DeploymentStatus{
+ CollisionCount: &collisionCount,
},
- {
- name: "unset: invalid update",
- from: apps.DeploymentStatus{
- CollisionCount: &collisionCount,
- },
- to: apps.DeploymentStatus{
- CollisionCount: nil,
- },
- expectedErr: true,
+ expectedErr: false,
+ }, {
+ name: "stable: valid update",
+ from: apps.DeploymentStatus{
+ CollisionCount: &collisionCount,
},
- {
- name: "decrease: invalid update",
- from: apps.DeploymentStatus{
- CollisionCount: &otherCollisionCount,
- },
- to: apps.DeploymentStatus{
- CollisionCount: &collisionCount,
- },
- expectedErr: true,
+ to: apps.DeploymentStatus{
+ CollisionCount: &collisionCount,
},
+ expectedErr: false,
+ }, {
+ name: "unset: invalid update",
+ from: apps.DeploymentStatus{
+ CollisionCount: &collisionCount,
+ },
+ to: apps.DeploymentStatus{
+ CollisionCount: nil,
+ },
+ expectedErr: true,
+ }, {
+ name: "decrease: invalid update",
+ from: apps.DeploymentStatus{
+ CollisionCount: &otherCollisionCount,
+ },
+ to: apps.DeploymentStatus{
+ CollisionCount: &collisionCount,
+ },
+ expectedErr: true,
+ },
}
for _, test := range tests {
@@ -2872,97 +2782,87 @@ func TestValidateReplicaSetStatus(t *testing.T) {
observedGeneration int64
expectedErr bool
- }{
- {
- name: "valid status",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 2,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: false,
- },
- {
- name: "invalid replicas",
- replicas: -1,
- fullyLabeledReplicas: 3,
- readyReplicas: 2,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid fullyLabeledReplicas",
- replicas: 3,
- fullyLabeledReplicas: -1,
- readyReplicas: 2,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid readyReplicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: -1,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid availableReplicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 3,
- availableReplicas: -1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid observedGeneration",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 3,
- availableReplicas: 3,
- observedGeneration: -1,
- expectedErr: true,
- },
- {
- name: "fullyLabeledReplicas greater than replicas",
- replicas: 3,
- fullyLabeledReplicas: 4,
- readyReplicas: 3,
- availableReplicas: 3,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "readyReplicas greater than replicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 4,
- availableReplicas: 3,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "availableReplicas greater than replicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 3,
- availableReplicas: 4,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "availableReplicas greater than readyReplicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 2,
- availableReplicas: 3,
- observedGeneration: 1,
- expectedErr: true,
- },
+ }{{
+ name: "valid status",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 2,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: false,
+ }, {
+ name: "invalid replicas",
+ replicas: -1,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 2,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid fullyLabeledReplicas",
+ replicas: 3,
+ fullyLabeledReplicas: -1,
+ readyReplicas: 2,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid readyReplicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: -1,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid availableReplicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 3,
+ availableReplicas: -1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid observedGeneration",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 3,
+ availableReplicas: 3,
+ observedGeneration: -1,
+ expectedErr: true,
+ }, {
+ name: "fullyLabeledReplicas greater than replicas",
+ replicas: 3,
+ fullyLabeledReplicas: 4,
+ readyReplicas: 3,
+ availableReplicas: 3,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "readyReplicas greater than replicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 4,
+ availableReplicas: 3,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "availableReplicas greater than replicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 3,
+ availableReplicas: 4,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "availableReplicas greater than readyReplicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 2,
+ availableReplicas: 3,
+ observedGeneration: 1,
+ expectedErr: true,
+ },
}
for _, test := range tests {
@@ -2998,30 +2898,29 @@ func TestValidateReplicaSetStatusUpdate(t *testing.T) {
old apps.ReplicaSet
update apps.ReplicaSet
}
- successCases := []rcUpdateTest{
- {
- old: apps.ReplicaSet{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: apps.ReplicaSetSpec{
- Selector: &metav1.LabelSelector{MatchLabels: validLabels},
- Template: validPodTemplate.Template,
- },
- Status: apps.ReplicaSetStatus{
- Replicas: 2,
- },
+ successCases := []rcUpdateTest{{
+ old: apps.ReplicaSet{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: apps.ReplicaSetSpec{
+ Selector: &metav1.LabelSelector{MatchLabels: validLabels},
+ Template: validPodTemplate.Template,
},
- update: apps.ReplicaSet{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: apps.ReplicaSetSpec{
- Replicas: 3,
- Selector: &metav1.LabelSelector{MatchLabels: validLabels},
- Template: validPodTemplate.Template,
- },
- Status: apps.ReplicaSetStatus{
- Replicas: 4,
- },
+ Status: apps.ReplicaSetStatus{
+ Replicas: 2,
},
},
+ update: apps.ReplicaSet{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: apps.ReplicaSetSpec{
+ Replicas: 3,
+ Selector: &metav1.LabelSelector{MatchLabels: validLabels},
+ Template: validPodTemplate.Template,
+ },
+ Status: apps.ReplicaSetStatus{
+ Replicas: 4,
+ },
+ },
+ },
}
for _, successCase := range successCases {
successCase.old.ObjectMeta.ResourceVersion = "1"
@@ -3300,29 +3199,26 @@ func TestValidateReplicaSet(t *testing.T) {
},
},
}
- successCases := []apps.ReplicaSet{
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: apps.ReplicaSetSpec{
- Selector: &metav1.LabelSelector{MatchLabels: validLabels},
- Template: validPodTemplate.Template,
- },
+ successCases := []apps.ReplicaSet{{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: apps.ReplicaSetSpec{
+ Selector: &metav1.LabelSelector{MatchLabels: validLabels},
+ Template: validPodTemplate.Template,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
- Spec: apps.ReplicaSetSpec{
- Selector: &metav1.LabelSelector{MatchLabels: validLabels},
- Template: validPodTemplate.Template,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
+ Spec: apps.ReplicaSetSpec{
+ Selector: &metav1.LabelSelector{MatchLabels: validLabels},
+ Template: validPodTemplate.Template,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
- Spec: apps.ReplicaSetSpec{
- Replicas: 1,
- Selector: &metav1.LabelSelector{MatchLabels: validLabels},
- Template: readWriteVolumePodTemplate.Template,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
+ Spec: apps.ReplicaSetSpec{
+ Replicas: 1,
+ Selector: &metav1.LabelSelector{MatchLabels: validLabels},
+ Template: readWriteVolumePodTemplate.Template,
},
+ },
}
for _, successCase := range successCases {
if errs := ValidateReplicaSet(&successCase, corevalidation.PodValidationOptions{}); len(errs) != 0 {
diff --git a/pkg/apis/authorization/validation/validation_test.go b/pkg/apis/authorization/validation/validation_test.go
index 44168859f0b..36e6d46dbe7 100644
--- a/pkg/apis/authorization/validation/validation_test.go
+++ b/pkg/apis/authorization/validation/validation_test.go
@@ -40,29 +40,25 @@ func TestValidateSARSpec(t *testing.T) {
name string
obj authorizationapi.SubjectAccessReviewSpec
msg string
- }{
- {
- name: "neither request",
- obj: authorizationapi.SubjectAccessReviewSpec{User: "me"},
- msg: "exactly one of nonResourceAttributes or resourceAttributes must be specified",
+ }{{
+ name: "neither request",
+ obj: authorizationapi.SubjectAccessReviewSpec{User: "me"},
+ msg: "exactly one of nonResourceAttributes or resourceAttributes must be specified",
+ }, {
+ name: "both requests",
+ obj: authorizationapi.SubjectAccessReviewSpec{
+ ResourceAttributes: &authorizationapi.ResourceAttributes{},
+ NonResourceAttributes: &authorizationapi.NonResourceAttributes{},
+ User: "me",
},
- {
- name: "both requests",
- obj: authorizationapi.SubjectAccessReviewSpec{
- ResourceAttributes: &authorizationapi.ResourceAttributes{},
- NonResourceAttributes: &authorizationapi.NonResourceAttributes{},
- User: "me",
- },
- msg: "cannot be specified in combination with resourceAttributes",
+ msg: "cannot be specified in combination with resourceAttributes",
+ }, {
+ name: "no subject",
+ obj: authorizationapi.SubjectAccessReviewSpec{
+ ResourceAttributes: &authorizationapi.ResourceAttributes{},
},
- {
- name: "no subject",
- obj: authorizationapi.SubjectAccessReviewSpec{
- ResourceAttributes: &authorizationapi.ResourceAttributes{},
- },
- msg: `spec.user: Invalid value: "": at least one of user or group must be specified`,
- },
- }
+ msg: `spec.user: Invalid value: "": at least one of user or group must be specified`,
+ }}
for _, c := range errorCases {
errs := ValidateSubjectAccessReviewSpec(c.obj, field.NewPath("spec"))
@@ -102,21 +98,18 @@ func TestValidateSelfSAR(t *testing.T) {
name string
obj authorizationapi.SelfSubjectAccessReviewSpec
msg string
- }{
- {
- name: "neither request",
- obj: authorizationapi.SelfSubjectAccessReviewSpec{},
- msg: "exactly one of nonResourceAttributes or resourceAttributes must be specified",
+ }{{
+ name: "neither request",
+ obj: authorizationapi.SelfSubjectAccessReviewSpec{},
+ msg: "exactly one of nonResourceAttributes or resourceAttributes must be specified",
+ }, {
+ name: "both requests",
+ obj: authorizationapi.SelfSubjectAccessReviewSpec{
+ ResourceAttributes: &authorizationapi.ResourceAttributes{},
+ NonResourceAttributes: &authorizationapi.NonResourceAttributes{},
},
- {
- name: "both requests",
- obj: authorizationapi.SelfSubjectAccessReviewSpec{
- ResourceAttributes: &authorizationapi.ResourceAttributes{},
- NonResourceAttributes: &authorizationapi.NonResourceAttributes{},
- },
- msg: "cannot be specified in combination with resourceAttributes",
- },
- }
+ msg: "cannot be specified in combination with resourceAttributes",
+ }}
for _, c := range errorCases {
errs := ValidateSelfSubjectAccessReviewSpec(c.obj, field.NewPath("spec"))
@@ -136,14 +129,12 @@ func TestValidateSelfSAR(t *testing.T) {
}
func TestValidateLocalSAR(t *testing.T) {
- successCases := []authorizationapi.LocalSubjectAccessReview{
- {
- Spec: authorizationapi.SubjectAccessReviewSpec{
- ResourceAttributes: &authorizationapi.ResourceAttributes{},
- User: "user",
- },
+ successCases := []authorizationapi.LocalSubjectAccessReview{{
+ Spec: authorizationapi.SubjectAccessReviewSpec{
+ ResourceAttributes: &authorizationapi.ResourceAttributes{},
+ User: "user",
},
- }
+ }}
for _, successCase := range successCases {
if errs := ValidateLocalSubjectAccessReview(&successCase); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
@@ -154,41 +145,37 @@ func TestValidateLocalSAR(t *testing.T) {
name string
obj *authorizationapi.LocalSubjectAccessReview
msg string
- }{
- {
- name: "name",
- obj: &authorizationapi.LocalSubjectAccessReview{
- ObjectMeta: metav1.ObjectMeta{Name: "a"},
- Spec: authorizationapi.SubjectAccessReviewSpec{
- ResourceAttributes: &authorizationapi.ResourceAttributes{},
- User: "user",
- },
+ }{{
+ name: "name",
+ obj: &authorizationapi.LocalSubjectAccessReview{
+ ObjectMeta: metav1.ObjectMeta{Name: "a"},
+ Spec: authorizationapi.SubjectAccessReviewSpec{
+ ResourceAttributes: &authorizationapi.ResourceAttributes{},
+ User: "user",
},
- msg: "must be empty except for namespace",
},
- {
- name: "namespace conflict",
- obj: &authorizationapi.LocalSubjectAccessReview{
- ObjectMeta: metav1.ObjectMeta{Namespace: "a"},
- Spec: authorizationapi.SubjectAccessReviewSpec{
- ResourceAttributes: &authorizationapi.ResourceAttributes{},
- User: "user",
- },
+ msg: "must be empty except for namespace",
+ }, {
+ name: "namespace conflict",
+ obj: &authorizationapi.LocalSubjectAccessReview{
+ ObjectMeta: metav1.ObjectMeta{Namespace: "a"},
+ Spec: authorizationapi.SubjectAccessReviewSpec{
+ ResourceAttributes: &authorizationapi.ResourceAttributes{},
+ User: "user",
},
- msg: "must match metadata.namespace",
},
- {
- name: "nonresource",
- obj: &authorizationapi.LocalSubjectAccessReview{
- ObjectMeta: metav1.ObjectMeta{Namespace: "a"},
- Spec: authorizationapi.SubjectAccessReviewSpec{
- NonResourceAttributes: &authorizationapi.NonResourceAttributes{},
- User: "user",
- },
+ msg: "must match metadata.namespace",
+ }, {
+ name: "nonresource",
+ obj: &authorizationapi.LocalSubjectAccessReview{
+ ObjectMeta: metav1.ObjectMeta{Namespace: "a"},
+ Spec: authorizationapi.SubjectAccessReviewSpec{
+ NonResourceAttributes: &authorizationapi.NonResourceAttributes{},
+ User: "user",
},
- msg: "disallowed on this kind of request",
},
- }
+ msg: "disallowed on this kind of request",
+ }}
for _, c := range errorCases {
errs := ValidateLocalSubjectAccessReview(c.obj)
diff --git a/pkg/apis/autoscaling/validation/validation_test.go b/pkg/apis/autoscaling/validation/validation_test.go
index 1eb9d896552..f7199db2720 100644
--- a/pkg/apis/autoscaling/validation/validation_test.go
+++ b/pkg/apis/autoscaling/validation/validation_test.go
@@ -31,35 +31,31 @@ import (
)
func TestValidateScale(t *testing.T) {
- successCases := []autoscaling.Scale{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "frontend",
- Namespace: metav1.NamespaceDefault,
- },
- Spec: autoscaling.ScaleSpec{
- Replicas: 1,
- },
+ successCases := []autoscaling.Scale{{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "frontend",
+ Namespace: metav1.NamespaceDefault,
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "frontend",
- Namespace: metav1.NamespaceDefault,
- },
- Spec: autoscaling.ScaleSpec{
- Replicas: 10,
- },
+ Spec: autoscaling.ScaleSpec{
+ Replicas: 1,
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "frontend",
- Namespace: metav1.NamespaceDefault,
- },
- Spec: autoscaling.ScaleSpec{
- Replicas: 0,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "frontend",
+ Namespace: metav1.NamespaceDefault,
},
- }
+ Spec: autoscaling.ScaleSpec{
+ Replicas: 10,
+ },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "frontend",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.ScaleSpec{
+ Replicas: 0,
+ },
+ }}
for _, successCase := range successCases {
if errs := ValidateScale(&successCase); len(errs) != 0 {
@@ -70,20 +66,18 @@ func TestValidateScale(t *testing.T) {
errorCases := []struct {
scale autoscaling.Scale
msg string
- }{
- {
- scale: autoscaling.Scale{
- ObjectMeta: metav1.ObjectMeta{
- Name: "frontend",
- Namespace: metav1.NamespaceDefault,
- },
- Spec: autoscaling.ScaleSpec{
- Replicas: -1,
- },
+ }{{
+ scale: autoscaling.Scale{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "frontend",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.ScaleSpec{
+ Replicas: -1,
},
- msg: "must be greater than or equal to 0",
},
- }
+ msg: "must be greater than or equal to 0",
+ }}
for _, c := range errorCases {
if errs := ValidateScale(&c.scale); len(errs) == 0 {
@@ -99,90 +93,73 @@ func TestValidateBehavior(t *testing.T) {
minPolicy := autoscaling.MinPolicySelect
disabledPolicy := autoscaling.DisabledPolicySelect
incorrectPolicy := autoscaling.ScalingPolicySelect("incorrect")
- simplePoliciesList := []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PercentScalingPolicy,
- Value: 10,
- PeriodSeconds: 1,
+ simplePoliciesList := []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PercentScalingPolicy,
+ Value: 10,
+ PeriodSeconds: 1,
+ }, {
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 1,
+ PeriodSeconds: 1800,
+ }}
+ successCases := []autoscaling.HorizontalPodAutoscalerBehavior{{
+ ScaleUp: nil,
+ ScaleDown: nil,
+ }, {
+ ScaleUp: &autoscaling.HPAScalingRules{
+ StabilizationWindowSeconds: utilpointer.Int32(3600),
+ SelectPolicy: &minPolicy,
+ Policies: simplePoliciesList,
},
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 1,
- PeriodSeconds: 1800,
+ ScaleDown: &autoscaling.HPAScalingRules{
+ StabilizationWindowSeconds: utilpointer.Int32(0),
+ SelectPolicy: &disabledPolicy,
+ Policies: simplePoliciesList,
},
- }
- successCases := []autoscaling.HorizontalPodAutoscalerBehavior{
- {
- ScaleUp: nil,
- ScaleDown: nil,
+ }, {
+ ScaleUp: &autoscaling.HPAScalingRules{
+ StabilizationWindowSeconds: utilpointer.Int32(120),
+ SelectPolicy: &maxPolicy,
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 1,
+ PeriodSeconds: 2,
+ }, {
+ Type: autoscaling.PercentScalingPolicy,
+ Value: 3,
+ PeriodSeconds: 4,
+ }, {
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 5,
+ PeriodSeconds: 6,
+ }, {
+ Type: autoscaling.PercentScalingPolicy,
+ Value: 7,
+ PeriodSeconds: 8,
+ }},
},
- {
- ScaleUp: &autoscaling.HPAScalingRules{
- StabilizationWindowSeconds: utilpointer.Int32(3600),
- SelectPolicy: &minPolicy,
- Policies: simplePoliciesList,
- },
- ScaleDown: &autoscaling.HPAScalingRules{
- StabilizationWindowSeconds: utilpointer.Int32(0),
- SelectPolicy: &disabledPolicy,
- Policies: simplePoliciesList,
- },
+ ScaleDown: &autoscaling.HPAScalingRules{
+ StabilizationWindowSeconds: utilpointer.Int32(120),
+ SelectPolicy: &maxPolicy,
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 1,
+ PeriodSeconds: 2,
+ }, {
+ Type: autoscaling.PercentScalingPolicy,
+ Value: 3,
+ PeriodSeconds: 4,
+ }, {
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 5,
+ PeriodSeconds: 6,
+ }, {
+ Type: autoscaling.PercentScalingPolicy,
+ Value: 7,
+ PeriodSeconds: 8,
+ }},
},
- {
- ScaleUp: &autoscaling.HPAScalingRules{
- StabilizationWindowSeconds: utilpointer.Int32(120),
- SelectPolicy: &maxPolicy,
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 1,
- PeriodSeconds: 2,
- },
- {
- Type: autoscaling.PercentScalingPolicy,
- Value: 3,
- PeriodSeconds: 4,
- },
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 5,
- PeriodSeconds: 6,
- },
- {
- Type: autoscaling.PercentScalingPolicy,
- Value: 7,
- PeriodSeconds: 8,
- },
- },
- },
- ScaleDown: &autoscaling.HPAScalingRules{
- StabilizationWindowSeconds: utilpointer.Int32(120),
- SelectPolicy: &maxPolicy,
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 1,
- PeriodSeconds: 2,
- },
- {
- Type: autoscaling.PercentScalingPolicy,
- Value: 3,
- PeriodSeconds: 4,
- },
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 5,
- PeriodSeconds: 6,
- },
- {
- Type: autoscaling.PercentScalingPolicy,
- Value: 7,
- PeriodSeconds: 8,
- },
- },
- },
- },
- }
+ }}
for _, behavior := range successCases {
hpa := prepareHPAWithBehavior(behavior)
if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) != 0 {
@@ -192,238 +169,191 @@ func TestValidateBehavior(t *testing.T) {
errorCases := []struct {
behavior autoscaling.HorizontalPodAutoscalerBehavior
msg string
- }{
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleUp: &autoscaling.HPAScalingRules{
- SelectPolicy: &minPolicy,
- },
+ }{{
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscaling.HPAScalingRules{
+ SelectPolicy: &minPolicy,
},
- msg: "spec.behavior.scaleUp.policies: Required value: must specify at least one Policy",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleUp: &autoscaling.HPAScalingRules{
- StabilizationWindowSeconds: utilpointer.Int32(3601),
- SelectPolicy: &minPolicy,
- Policies: simplePoliciesList,
- },
+ msg: "spec.behavior.scaleUp.policies: Required value: must specify at least one Policy",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscaling.HPAScalingRules{
+ StabilizationWindowSeconds: utilpointer.Int32(3601),
+ SelectPolicy: &minPolicy,
+ Policies: simplePoliciesList,
},
- msg: "spec.behavior.scaleUp.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleUp: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 7,
- PeriodSeconds: 1801,
- },
- },
- },
+ msg: "spec.behavior.scaleUp.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 7,
+ PeriodSeconds: 1801,
+ }},
},
- msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleUp: &autoscaling.HPAScalingRules{
- SelectPolicy: &incorrectPolicy,
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 7,
- PeriodSeconds: 8,
- },
- },
- },
+ msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscaling.HPAScalingRules{
+ SelectPolicy: &incorrectPolicy,
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 7,
+ PeriodSeconds: 8,
+ }},
},
- msg: `spec.behavior.scaleUp.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`,
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleUp: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.HPAScalingPolicyType("hm"),
- Value: 7,
- PeriodSeconds: 8,
- },
- },
- },
+ msg: `spec.behavior.scaleUp.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`,
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.HPAScalingPolicyType("hm"),
+ Value: 7,
+ PeriodSeconds: 8,
+ }},
},
- msg: `spec.behavior.scaleUp.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`,
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleUp: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 8,
- },
- },
- },
+ msg: `spec.behavior.scaleUp.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`,
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 8,
+ }},
},
- msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 0: must be greater than zero",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleUp: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- PeriodSeconds: 8,
- },
- },
- },
+ msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: 0: must be greater than zero",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ PeriodSeconds: 8,
+ }},
},
- msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: 0: must be greater than zero",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleUp: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- PeriodSeconds: -1,
- Value: 1,
- },
- },
- },
+ msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: 0: must be greater than zero",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ PeriodSeconds: -1,
+ Value: 1,
+ }},
},
- msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: -1: must be greater than zero",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleUp: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- PeriodSeconds: 1,
- Value: -1,
- },
- },
- },
+ msg: "spec.behavior.scaleUp.policies[0].periodSeconds: Invalid value: -1: must be greater than zero",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleUp: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ PeriodSeconds: 1,
+ Value: -1,
+ }},
},
- msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: -1: must be greater than zero",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleDown: &autoscaling.HPAScalingRules{
- SelectPolicy: &minPolicy,
- },
+ msg: "spec.behavior.scaleUp.policies[0].value: Invalid value: -1: must be greater than zero",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleDown: &autoscaling.HPAScalingRules{
+ SelectPolicy: &minPolicy,
},
- msg: "spec.behavior.scaleDown.policies: Required value: must specify at least one Policy",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleDown: &autoscaling.HPAScalingRules{
- StabilizationWindowSeconds: utilpointer.Int32(3601),
- SelectPolicy: &minPolicy,
- Policies: simplePoliciesList,
- },
+ msg: "spec.behavior.scaleDown.policies: Required value: must specify at least one Policy",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleDown: &autoscaling.HPAScalingRules{
+ StabilizationWindowSeconds: utilpointer.Int32(3601),
+ SelectPolicy: &minPolicy,
+ Policies: simplePoliciesList,
},
- msg: "spec.behavior.scaleDown.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleDown: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PercentScalingPolicy,
- Value: 7,
- PeriodSeconds: 1801,
- },
- },
- },
+ msg: "spec.behavior.scaleDown.stabilizationWindowSeconds: Invalid value: 3601: must be less than or equal to 3600",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleDown: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PercentScalingPolicy,
+ Value: 7,
+ PeriodSeconds: 1801,
+ }},
},
- msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleDown: &autoscaling.HPAScalingRules{
- SelectPolicy: &incorrectPolicy,
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 7,
- PeriodSeconds: 8,
- },
- },
- },
+ msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 1801: must be less than or equal to 1800",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleDown: &autoscaling.HPAScalingRules{
+ SelectPolicy: &incorrectPolicy,
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 7,
+ PeriodSeconds: 8,
+ }},
},
- msg: `spec.behavior.scaleDown.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`,
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleDown: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.HPAScalingPolicyType("hm"),
- Value: 7,
- PeriodSeconds: 8,
- },
- },
- },
+ msg: `spec.behavior.scaleDown.selectPolicy: Unsupported value: "incorrect": supported values: "Disabled", "Max", "Min"`,
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleDown: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.HPAScalingPolicyType("hm"),
+ Value: 7,
+ PeriodSeconds: 8,
+ }},
},
- msg: `spec.behavior.scaleDown.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`,
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleDown: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- Value: 8,
- },
- },
- },
+ msg: `spec.behavior.scaleDown.policies[0].type: Unsupported value: "hm": supported values: "Percent", "Pods"`,
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleDown: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ Value: 8,
+ }},
},
- msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 0: must be greater than zero",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleDown: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- PeriodSeconds: 8,
- },
- },
- },
+ msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: 0: must be greater than zero",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleDown: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ PeriodSeconds: 8,
+ }},
},
- msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: 0: must be greater than zero",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleDown: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- PeriodSeconds: -1,
- Value: 1,
- },
- },
- },
+ msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: 0: must be greater than zero",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleDown: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ PeriodSeconds: -1,
+ Value: 1,
+ }},
},
- msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: -1: must be greater than zero",
},
- {
- behavior: autoscaling.HorizontalPodAutoscalerBehavior{
- ScaleDown: &autoscaling.HPAScalingRules{
- Policies: []autoscaling.HPAScalingPolicy{
- {
- Type: autoscaling.PodsScalingPolicy,
- PeriodSeconds: 1,
- Value: -1,
- },
- },
- },
+ msg: "spec.behavior.scaleDown.policies[0].periodSeconds: Invalid value: -1: must be greater than zero",
+ }, {
+ behavior: autoscaling.HorizontalPodAutoscalerBehavior{
+ ScaleDown: &autoscaling.HPAScalingRules{
+ Policies: []autoscaling.HPAScalingPolicy{{
+ Type: autoscaling.PodsScalingPolicy,
+ PeriodSeconds: 1,
+ Value: -1,
+ }},
},
- msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: -1: must be greater than zero",
},
- }
+ msg: "spec.behavior.scaleDown.policies[0].value: Invalid value: -1: must be greater than zero",
+ }}
for _, c := range errorCases {
hpa := prepareHPAWithBehavior(c.behavior)
if errs := ValidateHorizontalPodAutoscaler(&hpa); len(errs) == 0 {
@@ -447,18 +377,16 @@ func prepareHPAWithBehavior(b autoscaling.HorizontalPodAutoscalerBehavior) autos
},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
Behavior: &b,
},
}
@@ -470,246 +398,220 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
t.Errorf("unable to parse label selector: %v", err)
}
- successCases := []autoscaling.HorizontalPodAutoscaler{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
+ successCases := []autoscaling.HorizontalPodAutoscaler{{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
- },
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.AverageValueMetricType,
- AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
+ },
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.AverageValueMetricType,
+ AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.PodsMetricSourceType,
- Pods: &autoscaling.PodsMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.AverageValueMetricType,
- AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.PodsMetricSourceType,
+ Pods: &autoscaling.PodsMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.AverageValueMetricType,
+ AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "test-container",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "test-container",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "test-container",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.AverageValueMetricType,
- AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "test-container",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.AverageValueMetricType,
+ AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ObjectMetricSourceType,
- Object: &autoscaling.ObjectMetricSource{
- DescribedObject: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- Value: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ObjectMetricSourceType,
+ Object: &autoscaling.ObjectMetricSource{
+ DescribedObject: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
+ },
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ Value: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- Value: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ Value: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.AverageValueMetricType,
- AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.AverageValueMetricType,
+ AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- }
+ }}
for _, successCase := range successCases {
if errs := ValidateHorizontalPodAutoscaler(&successCase); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
@@ -719,666 +621,586 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
errorCases := []struct {
horizontalPodAutoscaler autoscaling.HorizontalPodAutoscaler
msg string
- }{
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ }{{
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "scaleTargetRef.kind: Required",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "test-application",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "scaleTargetRef.kind: Required",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "test-application",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "scaleTargetRef.kind: Required",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "..", Name: "myrc"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "scaleTargetRef.kind: Required",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "..", Name: "myrc"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "scaleTargetRef.kind: Invalid",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "..", Name: "myrc"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "test-application",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "scaleTargetRef.kind: Invalid",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "..", Name: "myrc"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "test-application",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "scaleTargetRef.kind: Invalid",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "scaleTargetRef.kind: Invalid",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "scaleTargetRef.name: Required",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "test-application",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "scaleTargetRef.name: Required",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "test-application",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "scaleTargetRef.name: Required",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController", Name: ".."},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "scaleTargetRef.name: Required",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController", Name: ".."},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "scaleTargetRef.name: Invalid",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController", Name: ".."},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "test-application",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "scaleTargetRef.name: Invalid",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Kind: "ReplicationController", Name: ".."},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "test-application",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "scaleTargetRef.name: Invalid",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
- },
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{},
- MinReplicas: utilpointer.Int32(-1),
- MaxReplicas: 5,
- },
+ msg: "scaleTargetRef.name: Invalid",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
},
- msg: "must be greater than or equal to 1",
- },
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
- },
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{},
- MinReplicas: utilpointer.Int32(7),
- MaxReplicas: 5,
- },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{},
+ MinReplicas: utilpointer.Int32(-1),
+ MaxReplicas: 5,
},
- msg: "must be greater than or equal to `minReplicas`",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
- },
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ msg: "must be greater than or equal to 1",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{},
+ MinReplicas: utilpointer.Int32(7),
+ MaxReplicas: 5,
+ },
+ },
+ msg: "must be greater than or equal to `minReplicas`",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
+ AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- msg: "may not set both a target raw value and a target utilization",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
- },
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "test-application",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ msg: "may not set both a target raw value and a target utilization",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "test-application",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
+ AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- msg: "may not set both a target raw value and a target utilization",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "may not set both a target raw value and a target utilization",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "must specify a resource name",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Container: "test-application",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "must specify a resource name",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Container: "test-application",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "must specify a resource name",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: "InvalidResource",
- Container: "test-application",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(70),
- },
- },
+ msg: "must specify a resource name",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: "InvalidResource",
+ Container: "test-application",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(70),
},
},
- },
+ }},
},
- msg: "Invalid value: \"InvalidResource\": must be a standard resource type or fully qualified",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(-10),
- },
- },
+ msg: "Invalid value: \"InvalidResource\": must be a standard resource type or fully qualified",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(-10),
},
},
- },
+ }},
},
- msg: "must be greater than 0",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "test-application",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(-10),
- },
- },
+ msg: "must be greater than 0",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "test-application",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(-10),
},
},
- },
+ }},
},
- msg: "must be greater than 0",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(-10),
- },
- },
+ msg: "must be greater than 0",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(-10),
},
},
- },
+ }},
},
- msg: "must specify a container",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "---***",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.UtilizationMetricType,
- AverageUtilization: utilpointer.Int32(-10),
- },
- },
+ msg: "must specify a container",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "---***",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.UtilizationMetricType,
+ AverageUtilization: utilpointer.Int32(-10),
},
},
- },
+ }},
},
- msg: "Invalid value: \"---***\"",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- },
- },
+ msg: "Invalid value: \"---***\"",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
},
},
- },
+ }},
},
- msg: "must set either a target raw value or a target utilization",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ContainerResourceMetricSourceType,
- ContainerResource: &autoscaling.ContainerResourceMetricSource{
- Name: api.ResourceCPU,
- Container: "test-application",
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- },
- },
+ msg: "must set either a target raw value or a target utilization",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ContainerResourceMetricSourceType,
+ ContainerResource: &autoscaling.ContainerResourceMetricSource{
+ Name: api.ResourceCPU,
+ Container: "test-application",
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
},
},
- },
+ }},
},
- msg: "must set either a target raw value or a target utilization",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.PodsMetricSourceType,
- Pods: &autoscaling.PodsMetricSource{
- Metric: autoscaling.MetricIdentifier{},
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
- },
- },
+ msg: "must set either a target raw value or a target utilization",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.PodsMetricSourceType,
+ Pods: &autoscaling.PodsMetricSource{
+ Metric: autoscaling.MetricIdentifier{},
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
},
},
- },
+ }},
},
- msg: "must specify a metric name",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.PodsMetricSourceType,
- Pods: &autoscaling.PodsMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- },
- },
+ msg: "must specify a metric name",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.PodsMetricSourceType,
+ Pods: &autoscaling.PodsMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
},
},
- },
+ }},
},
- msg: "must specify a positive target averageValue",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ObjectMetricSourceType,
- Object: &autoscaling.ObjectMetricSource{
- DescribedObject: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- },
- },
+ msg: "must specify a positive target averageValue",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ObjectMetricSourceType,
+ Object: &autoscaling.ObjectMetricSource{
+ DescribedObject: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
+ },
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
},
},
- },
+ }},
},
- msg: "must set either a target value or averageValue",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ObjectMetricSourceType,
- Object: &autoscaling.ObjectMetricSource{
- DescribedObject: autoscaling.CrossVersionObjectReference{
- Name: "myrc",
- },
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- Value: resource.NewMilliQuantity(100, resource.DecimalSI),
- },
- },
+ msg: "must set either a target value or averageValue",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ObjectMetricSourceType,
+ Object: &autoscaling.ObjectMetricSource{
+ DescribedObject: autoscaling.CrossVersionObjectReference{
+ Name: "myrc",
+ },
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ Value: resource.NewMilliQuantity(100, resource.DecimalSI),
},
},
- },
+ }},
},
- msg: "object.describedObject.kind: Required",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ObjectMetricSourceType,
- Object: &autoscaling.ObjectMetricSource{
- DescribedObject: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- Value: resource.NewMilliQuantity(100, resource.DecimalSI),
- },
- },
+ msg: "object.describedObject.kind: Required",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ObjectMetricSourceType,
+ Object: &autoscaling.ObjectMetricSource{
+ DescribedObject: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ Value: resource.NewMilliQuantity(100, resource.DecimalSI),
},
},
- },
+ }},
},
- msg: "must specify a metric name",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- Value: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ msg: "must specify a metric name",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ Value: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- msg: "must specify a metric name",
},
- {
- horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
- ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
- MinReplicas: utilpointer.Int32(1),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "foo/../",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- Value: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ msg: "must specify a metric name",
+ }, {
+ horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
+ ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
+ MinReplicas: utilpointer.Int32(1),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "foo/../",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ Value: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- msg: "'/'",
},
+ msg: "'/'",
+ },
{
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
@@ -1387,177 +1209,156 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
},
},
- },
+ }},
},
},
msg: "must set either a target value for metric or a per-pod target",
- },
- {
+ }, {
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- Value: resource.NewMilliQuantity(-300, resource.DecimalSI),
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ Value: resource.NewMilliQuantity(-300, resource.DecimalSI),
},
},
- },
+ }},
},
},
msg: "must be positive",
- },
- {
+ }, {
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- AverageValue: resource.NewMilliQuantity(-300, resource.DecimalSI),
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ AverageValue: resource.NewMilliQuantity(-300, resource.DecimalSI),
},
},
- },
+ }},
},
},
msg: "must be positive",
- },
- {
+ }, {
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- Value: resource.NewMilliQuantity(300, resource.DecimalSI),
- AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ Value: resource.NewMilliQuantity(300, resource.DecimalSI),
+ AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
},
msg: "may not set both a target value for metric and a per-pod target",
- },
- {
+ }, {
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: "boogity",
- Value: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: "boogity",
+ Value: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
},
msg: "must be either Utilization, Value, or AverageValue",
- },
- {
+ }, {
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Value: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Value: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
},
msg: "must specify a metric target type",
- },
- {
+ }, {
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
},
},
- },
+ }},
},
},
msg: "must specify a metric target",
- },
- {
+ }, {
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
@@ -1570,51 +1371,45 @@ func TestValidateHorizontalPodAutoscaler(t *testing.T) {
},
},
msg: "must specify a metric source type",
- },
- {
+ }, {
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.MetricSourceType("InvalidType"),
- },
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.MetricSourceType("InvalidType"),
+ }},
},
},
msg: "type: Unsupported value",
- },
- {
+ }, {
horizontalPodAutoscaler: autoscaling.HorizontalPodAutoscaler{
ObjectMeta: metav1.ObjectMeta{Name: "myautoscaler", Namespace: metav1.NamespaceDefault},
Spec: autoscaling.HorizontalPodAutoscalerSpec{
ScaleTargetRef: autoscaling.CrossVersionObjectReference{Name: "myrc", Kind: "ReplicationController"},
MinReplicas: utilpointer.Int32(1),
MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ResourceMetricSourceType,
- Resource: &autoscaling.ResourceMetricSource{
- Name: api.ResourceCPU,
- Target: autoscaling.MetricTarget{
- Type: autoscaling.AverageValueMetricType,
- AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
- },
- },
- Pods: &autoscaling.PodsMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.AverageValueMetricType,
- AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
- },
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ResourceMetricSourceType,
+ Resource: &autoscaling.ResourceMetricSource{
+ Name: api.ResourceCPU,
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.AverageValueMetricType,
+ AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
},
},
- },
+ Pods: &autoscaling.PodsMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.AverageValueMetricType,
+ AverageValue: resource.NewMilliQuantity(100, resource.DecimalSI),
+ },
+ },
+ }},
},
},
msg: "must populate the given metric source only",
@@ -1711,71 +1506,64 @@ func prepareMinReplicasCases(t *testing.T, minReplicas int32) []autoscaling.Hori
if err != nil {
t.Errorf("unable to parse label selector: %v", err)
}
- minReplicasCases := []autoscaling.HorizontalPodAutoscaler{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
- ResourceVersion: "theversion",
+ minReplicasCases := []autoscaling.HorizontalPodAutoscaler{{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ ResourceVersion: "theversion",
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(minReplicas),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ObjectMetricSourceType,
- Object: &autoscaling.ObjectMetricSource{
- DescribedObject: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.ValueMetricType,
- Value: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ MinReplicas: utilpointer.Int32(minReplicas),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ObjectMetricSourceType,
+ Object: &autoscaling.ObjectMetricSource{
+ DescribedObject: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
+ },
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.ValueMetricType,
+ Value: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "myautoscaler",
- Namespace: metav1.NamespaceDefault,
- ResourceVersion: "theversion",
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "myautoscaler",
+ Namespace: metav1.NamespaceDefault,
+ ResourceVersion: "theversion",
+ },
+ Spec: autoscaling.HorizontalPodAutoscalerSpec{
+ ScaleTargetRef: autoscaling.CrossVersionObjectReference{
+ Kind: "ReplicationController",
+ Name: "myrc",
},
- Spec: autoscaling.HorizontalPodAutoscalerSpec{
- ScaleTargetRef: autoscaling.CrossVersionObjectReference{
- Kind: "ReplicationController",
- Name: "myrc",
- },
- MinReplicas: utilpointer.Int32(minReplicas),
- MaxReplicas: 5,
- Metrics: []autoscaling.MetricSpec{
- {
- Type: autoscaling.ExternalMetricSourceType,
- External: &autoscaling.ExternalMetricSource{
- Metric: autoscaling.MetricIdentifier{
- Name: "somemetric",
- Selector: metricLabelSelector,
- },
- Target: autoscaling.MetricTarget{
- Type: autoscaling.AverageValueMetricType,
- AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
- },
- },
+ MinReplicas: utilpointer.Int32(minReplicas),
+ MaxReplicas: 5,
+ Metrics: []autoscaling.MetricSpec{{
+ Type: autoscaling.ExternalMetricSourceType,
+ External: &autoscaling.ExternalMetricSource{
+ Metric: autoscaling.MetricIdentifier{
+ Name: "somemetric",
+ Selector: metricLabelSelector,
+ },
+ Target: autoscaling.MetricTarget{
+ Type: autoscaling.AverageValueMetricType,
+ AverageValue: resource.NewMilliQuantity(300, resource.DecimalSI),
},
},
- },
+ }},
},
- }
+ }}
return minReplicasCases
}
diff --git a/pkg/apis/batch/validation/validation_test.go b/pkg/apis/batch/validation/validation_test.go
index af5828d284e..ad82d9cf516 100644
--- a/pkg/apis/batch/validation/validation_test.go
+++ b/pkg/apis/batch/validation/validation_test.go
@@ -112,49 +112,39 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.DisruptionTarget,
- Status: api.ConditionTrue,
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.DisruptionTarget,
+ Status: api.ConditionTrue,
+ }},
+ }, {
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.PodConditionType("CustomConditionType"),
+ Status: api.ConditionFalse,
+ }},
+ }, {
+ Action: batch.PodFailurePolicyActionCount,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ ContainerName: pointer.String("abc"),
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{1, 2, 3},
},
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.PodConditionType("CustomConditionType"),
- Status: api.ConditionFalse,
- },
- },
+ }, {
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ ContainerName: pointer.String("def"),
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{4},
},
- {
- Action: batch.PodFailurePolicyActionCount,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- ContainerName: pointer.String("abc"),
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{1, 2, 3},
- },
+ }, {
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: batch.PodFailurePolicyOnExitCodesOpNotIn,
+ Values: []int32{5, 6, 7},
},
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- ContainerName: pointer.String("def"),
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{4},
- },
- },
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: batch.PodFailurePolicyOnExitCodesOpNotIn,
- Values: []int32{5, 6, 7},
- },
- },
- },
+ }},
},
},
},
@@ -314,11 +304,9 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionFailJob,
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionFailJob,
+ }},
},
},
},
@@ -331,15 +319,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{11, 11},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{11, 11},
},
- },
+ }},
},
},
},
@@ -352,21 +338,19 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: func() (values []int32) {
- tooManyValues := make([]int32, maxPodFailurePolicyOnExitCodesValues+1)
- for i := range tooManyValues {
- tooManyValues[i] = int32(i)
- }
- return tooManyValues
- }(),
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: func() (values []int32) {
+ tooManyValues := make([]int32, maxPodFailurePolicyOnExitCodesValues+1)
+ for i := range tooManyValues {
+ tooManyValues[i] = int32(i)
+ }
+ return tooManyValues
+ }(),
},
- },
+ }},
},
},
},
@@ -404,21 +388,19 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnPodConditions: func() []batch.PodFailurePolicyOnPodConditionsPattern {
- tooManyPatterns := make([]batch.PodFailurePolicyOnPodConditionsPattern, maxPodFailurePolicyOnPodConditionsPatterns+1)
- for i := range tooManyPatterns {
- tooManyPatterns[i] = batch.PodFailurePolicyOnPodConditionsPattern{
- Type: api.PodConditionType(fmt.Sprintf("CustomType_%d", i)),
- Status: api.ConditionTrue,
- }
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnPodConditions: func() []batch.PodFailurePolicyOnPodConditionsPattern {
+ tooManyPatterns := make([]batch.PodFailurePolicyOnPodConditionsPattern, maxPodFailurePolicyOnPodConditionsPatterns+1)
+ for i := range tooManyPatterns {
+ tooManyPatterns[i] = batch.PodFailurePolicyOnPodConditionsPattern{
+ Type: api.PodConditionType(fmt.Sprintf("CustomType_%d", i)),
+ Status: api.ConditionTrue,
}
- return tooManyPatterns
- }(),
- },
- },
+ }
+ return tooManyPatterns
+ }(),
+ }},
},
},
},
@@ -431,15 +413,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{12, 13, 13, 13},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{12, 13, 13, 13},
},
- },
+ }},
},
},
},
@@ -452,15 +432,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{19, 11},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{19, 11},
},
- },
+ }},
},
},
},
@@ -473,15 +451,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{},
},
- },
+ }},
},
},
},
@@ -494,15 +470,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: "",
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{1, 2, 3},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: "",
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{1, 2, 3},
},
- },
+ }},
},
},
},
@@ -515,15 +489,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: "",
- Values: []int32{1, 2, 3},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: "",
+ Values: []int32{1, 2, 3},
},
- },
+ }},
},
},
},
@@ -536,22 +508,18 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- ContainerName: pointer.String("abc"),
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{1, 2, 3},
- },
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.DisruptionTarget,
- Status: api.ConditionTrue,
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ ContainerName: pointer.String("abc"),
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{1, 2, 3},
},
- },
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.DisruptionTarget,
+ Status: api.ConditionTrue,
+ }},
+ }},
},
},
},
@@ -564,15 +532,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{1, 0, 2},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{1, 0, 2},
},
- },
+ }},
},
},
},
@@ -585,24 +551,21 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- ContainerName: pointer.String("abc"),
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{1, 2, 3},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ ContainerName: pointer.String("abc"),
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{1, 2, 3},
},
- {
- Action: batch.PodFailurePolicyActionFailJob,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- ContainerName: pointer.String("xyz"),
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{5, 6, 7},
- },
+ }, {
+ Action: batch.PodFailurePolicyActionFailJob,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ ContainerName: pointer.String("xyz"),
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{5, 6, 7},
},
- },
+ }},
},
},
},
@@ -615,16 +578,14 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: "UnknownAction",
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- ContainerName: pointer.String("abc"),
- Operator: batch.PodFailurePolicyOnExitCodesOpIn,
- Values: []int32{1, 2, 3},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: "UnknownAction",
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ ContainerName: pointer.String("abc"),
+ Operator: batch.PodFailurePolicyOnExitCodesOpIn,
+ Values: []int32{1, 2, 3},
},
- },
+ }},
},
},
},
@@ -637,15 +598,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
- Operator: "UnknownOperator",
- Values: []int32{1, 2, 3},
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnExitCodes: &batch.PodFailurePolicyOnExitCodesRequirement{
+ Operator: "UnknownOperator",
+ Values: []int32{1, 2, 3},
},
- },
+ }},
},
},
},
@@ -658,16 +617,12 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.DisruptionTarget,
- },
- },
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.DisruptionTarget,
+ }},
+ }},
},
},
},
@@ -680,17 +635,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.DisruptionTarget,
- Status: "UnknownStatus",
- },
- },
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.DisruptionTarget,
+ Status: "UnknownStatus",
+ }},
+ }},
},
},
},
@@ -703,16 +654,12 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Status: api.ConditionTrue,
- },
- },
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Status: api.ConditionTrue,
+ }},
+ }},
},
},
},
@@ -725,17 +672,13 @@ func TestValidateJob(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.PodConditionType("Invalid Condition Type"),
- Status: api.ConditionTrue,
- },
- },
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.PodConditionType("Invalid Condition Type"),
+ Status: api.ConditionTrue,
+ }},
+ }},
},
},
},
@@ -1116,17 +1059,13 @@ func TestValidateJobUpdate(t *testing.T) {
validNodeAffinity := &api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
- NodeSelectorTerms: []api.NodeSelectorTerm{
- {
- MatchExpressions: []api.NodeSelectorRequirement{
- {
- Key: "foo",
- Operator: api.NodeSelectorOpIn,
- Values: []string{"bar", "value2"},
- },
- },
- },
- },
+ NodeSelectorTerms: []api.NodeSelectorTerm{{
+ MatchExpressions: []api.NodeSelectorRequirement{{
+ Key: "foo",
+ Operator: api.NodeSelectorOpIn,
+ Values: []string{"bar", "value2"},
+ }},
+ }},
},
},
}
@@ -1134,17 +1073,13 @@ func TestValidateJobUpdate(t *testing.T) {
validPodTemplateWithAffinity.Spec.Affinity = &api.Affinity{
NodeAffinity: &api.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{
- NodeSelectorTerms: []api.NodeSelectorTerm{
- {
- MatchExpressions: []api.NodeSelectorRequirement{
- {
- Key: "foo",
- Operator: api.NodeSelectorOpIn,
- Values: []string{"bar", "value"},
- },
- },
- },
- },
+ NodeSelectorTerms: []api.NodeSelectorTerm{{
+ MatchExpressions: []api.NodeSelectorRequirement{{
+ Key: "foo",
+ Operator: api.NodeSelectorOpIn,
+ Values: []string{"bar", "value"},
+ }},
+ }},
},
},
}
@@ -1242,17 +1177,13 @@ func TestValidateJobUpdate(t *testing.T) {
},
update: func(job *batch.Job) {
job.Spec.PodFailurePolicy = &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.DisruptionTarget,
- Status: api.ConditionTrue,
- },
- },
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.DisruptionTarget,
+ Status: api.ConditionTrue,
+ }},
+ }},
}
},
err: &field.Error{
@@ -1267,29 +1198,23 @@ func TestValidateJobUpdate(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.DisruptionTarget,
- Status: api.ConditionTrue,
- },
- },
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.DisruptionTarget,
+ Status: api.ConditionTrue,
+ }},
+ }},
},
},
},
update: func(job *batch.Job) {
job.Spec.PodFailurePolicy.Rules = append(job.Spec.PodFailurePolicy.Rules, batch.PodFailurePolicyRule{
Action: batch.PodFailurePolicyActionCount,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.DisruptionTarget,
- Status: api.ConditionTrue,
- },
- },
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.DisruptionTarget,
+ Status: api.ConditionTrue,
+ }},
})
},
err: &field.Error{
@@ -1304,17 +1229,13 @@ func TestValidateJobUpdate(t *testing.T) {
Selector: validGeneratedSelector,
Template: validPodTemplateSpecForGeneratedRestartPolicyNever,
PodFailurePolicy: &batch.PodFailurePolicy{
- Rules: []batch.PodFailurePolicyRule{
- {
- Action: batch.PodFailurePolicyActionIgnore,
- OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{
- {
- Type: api.DisruptionTarget,
- Status: api.ConditionTrue,
- },
- },
- },
- },
+ Rules: []batch.PodFailurePolicyRule{{
+ Action: batch.PodFailurePolicyActionIgnore,
+ OnPodConditions: []batch.PodFailurePolicyOnPodConditionsPattern{{
+ Type: api.DisruptionTarget,
+ Status: api.ConditionTrue,
+ }},
+ }},
},
},
},
diff --git a/pkg/apis/certificates/validation/validation_test.go b/pkg/apis/certificates/validation/validation_test.go
index 988b4c5be6f..bd4fc0cd93f 100644
--- a/pkg/apis/certificates/validation/validation_test.go
+++ b/pkg/apis/certificates/validation/validation_test.go
@@ -413,90 +413,79 @@ func Test_getValidationOptions(t *testing.T) {
newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest
want certificateValidationOptions
- }{
- {
- name: "strict create",
- oldCSR: nil,
- want: certificateValidationOptions{},
+ }{{
+ name: "strict create",
+ oldCSR: nil,
+ want: certificateValidationOptions{},
+ }, {
+ name: "strict update",
+ oldCSR: &capi.CertificateSigningRequest{},
+ want: certificateValidationOptions{},
+ }, {
+ name: "compatible update, approved+denied",
+ oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateDenied}},
+ }},
+ want: certificateValidationOptions{
+ allowBothApprovedAndDenied: true,
},
- {
- name: "strict update",
- oldCSR: &capi.CertificateSigningRequest{},
- want: certificateValidationOptions{},
+ }, {
+ name: "compatible update, legacy signerName",
+ oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{SignerName: capi.LegacyUnknownSignerName}},
+ want: certificateValidationOptions{
+ allowLegacySignerName: true,
},
- {
- name: "compatible update, approved+denied",
- oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateDenied}},
- }},
- want: certificateValidationOptions{
- allowBothApprovedAndDenied: true,
- },
+ }, {
+ name: "compatible update, duplicate condition types",
+ oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateApproved}},
+ }},
+ want: certificateValidationOptions{
+ allowDuplicateConditionTypes: true,
},
- {
- name: "compatible update, legacy signerName",
- oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{SignerName: capi.LegacyUnknownSignerName}},
- want: certificateValidationOptions{
- allowLegacySignerName: true,
- },
+ }, {
+ name: "compatible update, empty condition types",
+ oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{}},
+ }},
+ want: certificateValidationOptions{
+ allowEmptyConditionType: true,
},
- {
- name: "compatible update, duplicate condition types",
- oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved}, {Type: capi.CertificateApproved}},
- }},
- want: certificateValidationOptions{
- allowDuplicateConditionTypes: true,
- },
+ }, {
+ name: "compatible update, no diff to certificate",
+ newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
+ Certificate: validCertificate,
+ }},
+ oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
+ Certificate: validCertificate,
+ }},
+ want: certificateValidationOptions{
+ allowArbitraryCertificate: true,
},
- {
- name: "compatible update, empty condition types",
- oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{}},
- }},
- want: certificateValidationOptions{
- allowEmptyConditionType: true,
- },
+ }, {
+ name: "compatible update, existing invalid certificate",
+ newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
+ Certificate: []byte(`new - no PEM blocks`),
+ }},
+ oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
+ Certificate: []byte(`old - no PEM blocks`),
+ }},
+ want: certificateValidationOptions{
+ allowArbitraryCertificate: true,
},
- {
- name: "compatible update, no diff to certificate",
- newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
- Certificate: validCertificate,
- }},
- oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
- Certificate: validCertificate,
- }},
- want: certificateValidationOptions{
- allowArbitraryCertificate: true,
- },
+ }, {
+ name: "compatible update, existing unknown usages",
+ oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"unknown"}}},
+ want: certificateValidationOptions{
+ allowUnknownUsages: true,
},
- {
- name: "compatible update, existing invalid certificate",
- newCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
- Certificate: []byte(`new - no PEM blocks`),
- }},
- oldCSR: &capi.CertificateSigningRequest{Status: capi.CertificateSigningRequestStatus{
- Certificate: []byte(`old - no PEM blocks`),
- }},
- want: certificateValidationOptions{
- allowArbitraryCertificate: true,
- },
+ }, {
+ name: "compatible update, existing duplicate usages",
+ oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"any", "any"}}},
+ want: certificateValidationOptions{
+ allowDuplicateUsages: true,
},
- {
- name: "compatible update, existing unknown usages",
- oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"unknown"}}},
- want: certificateValidationOptions{
- allowUnknownUsages: true,
- },
- },
- {
- name: "compatible update, existing duplicate usages",
- oldCSR: &capi.CertificateSigningRequest{Spec: capi.CertificateSigningRequestSpec{Usages: []capi.KeyUsage{"any", "any"}}},
- want: certificateValidationOptions{
- allowDuplicateUsages: true,
- },
- },
- }
+ }}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := getValidationOptions(tt.newCSR, tt.oldCSR); !reflect.DeepEqual(got, tt.want) {
@@ -524,86 +513,76 @@ func TestValidateCertificateSigningRequestUpdate(t *testing.T) {
newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest
errs []string
- }{
- {
- name: "no-op",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ }{{
+ name: "no-op",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ }, {
+ name: "finalizer change with invalid status",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
+ }, {
+ name: "add Approved condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not add a condition of type "Approved"`,
},
- {
- name: "finalizer change with invalid status",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
+ }, {
+ name: "remove Approved condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
+ }},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not remove a condition of type "Approved"`,
},
- {
- name: "add Approved condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{
- `status.conditions: Forbidden: updates may not add a condition of type "Approved"`,
- },
+ }, {
+ name: "add Denied condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not add a condition of type "Denied"`,
},
- {
- name: "remove Approved condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
- }},
- errs: []string{
- `status.conditions: Forbidden: updates may not remove a condition of type "Approved"`,
- },
+ }, {
+ name: "remove Denied condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
+ }},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
},
- {
- name: "add Denied condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{
- `status.conditions: Forbidden: updates may not add a condition of type "Denied"`,
- },
+ }, {
+ name: "add Failed condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{},
+ }, {
+ name: "remove Failed condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
+ }},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
},
- {
- name: "remove Denied condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
- }},
- errs: []string{
- `status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
- },
+ }, {
+ name: "set certificate",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Certificate: validCertificate,
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{
+ `status.certificate: Forbidden: updates may not set certificate content`,
},
- {
- name: "add Failed condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{},
- },
- {
- name: "remove Failed condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
- }},
- errs: []string{
- `status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
- },
- },
- {
- name: "set certificate",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Certificate: validCertificate,
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{
- `status.certificate: Forbidden: updates may not set certificate content`,
- },
- },
- }
+ }}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -640,119 +619,106 @@ func TestValidateCertificateSigningRequestStatusUpdate(t *testing.T) {
newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest
errs []string
- }{
- {
- name: "no-op",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ }{{
+ name: "no-op",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ }, {
+ name: "finalizer change with invalid status",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
+ }, {
+ name: "finalizer change with duplicate and unknown usages",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: capi.CertificateSigningRequestSpec{
+ Usages: []capi.KeyUsage{"unknown", "unknown"},
+ Request: newCSRPEM(t),
+ SignerName: validSignerName,
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: capi.CertificateSigningRequestSpec{
+ Usages: []capi.KeyUsage{"unknown", "unknown"},
+ Request: newCSRPEM(t),
+ SignerName: validSignerName,
+ }},
+ }, {
+ name: "add Approved condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not add a condition of type "Approved"`,
},
- {
- name: "finalizer change with invalid status",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
+ }, {
+ name: "remove Approved condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
+ }},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not remove a condition of type "Approved"`,
},
- {
- name: "finalizer change with duplicate and unknown usages",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: capi.CertificateSigningRequestSpec{
- Usages: []capi.KeyUsage{"unknown", "unknown"},
- Request: newCSRPEM(t),
- SignerName: validSignerName,
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: capi.CertificateSigningRequestSpec{
- Usages: []capi.KeyUsage{"unknown", "unknown"},
- Request: newCSRPEM(t),
- SignerName: validSignerName,
- }},
+ }, {
+ name: "add Denied condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not add a condition of type "Denied"`,
},
- {
- name: "add Approved condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{
- `status.conditions: Forbidden: updates may not add a condition of type "Approved"`,
- },
+ }, {
+ name: "remove Denied condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
+ }},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
},
- {
- name: "remove Approved condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
- }},
- errs: []string{
- `status.conditions: Forbidden: updates may not remove a condition of type "Approved"`,
- },
+ }, {
+ name: "add Failed condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{},
+ }, {
+ name: "remove Failed condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
+ }},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
},
- {
- name: "add Denied condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{
- `status.conditions: Forbidden: updates may not add a condition of type "Denied"`,
- },
+ }, {
+ name: "set valid certificate",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Certificate: validCertificate,
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{},
+ }, {
+ name: "set invalid certificate",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Certificate: invalidCertificateNoPEM,
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{
+ `status.certificate: Invalid value: "": must contain at least one CERTIFICATE PEM block`,
},
- {
- name: "remove Denied condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
- }},
- errs: []string{
- `status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
- },
+ }, {
+ name: "reset certificate",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Certificate: invalidCertificateNonCertificatePEM,
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Certificate: invalidCertificateNoPEM,
+ }},
+ errs: []string{
+ `status.certificate: Forbidden: updates may not modify existing certificate content`,
},
- {
- name: "add Failed condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{},
- },
- {
- name: "remove Failed condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
- }},
- errs: []string{
- `status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
- },
- },
- {
- name: "set valid certificate",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Certificate: validCertificate,
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{},
- },
- {
- name: "set invalid certificate",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Certificate: invalidCertificateNoPEM,
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{
- `status.certificate: Invalid value: "": must contain at least one CERTIFICATE PEM block`,
- },
- },
- {
- name: "reset certificate",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Certificate: invalidCertificateNonCertificatePEM,
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Certificate: invalidCertificateNoPEM,
- }},
- errs: []string{
- `status.certificate: Forbidden: updates may not modify existing certificate content`,
- },
- },
- }
+ }}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -789,80 +755,70 @@ func TestValidateCertificateSigningRequestApprovalUpdate(t *testing.T) {
newCSR *capi.CertificateSigningRequest
oldCSR *capi.CertificateSigningRequest
errs []string
- }{
- {
- name: "no-op",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ }{{
+ name: "no-op",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ }, {
+ name: "finalizer change with invalid certificate",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
+ }, {
+ name: "add Approved condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ }, {
+ name: "remove Approved condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
+ }},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not remove a condition of type "Approved"`,
},
- {
- name: "finalizer change with invalid certificate",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{Certificate: invalidCertificateNoPEM}},
+ }, {
+ name: "add Denied condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ }, {
+ name: "remove Denied condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
+ }},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
},
- {
- name: "add Approved condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ }, {
+ name: "add Failed condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{},
+ }, {
+ name: "remove Failed condition",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
+ }},
+ errs: []string{
+ `status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
},
- {
- name: "remove Approved condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
- }},
- errs: []string{
- `status.conditions: Forbidden: updates may not remove a condition of type "Approved"`,
- },
+ }, {
+ name: "set certificate",
+ newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
+ Certificate: validCertificate,
+ }},
+ oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
+ errs: []string{
+ `status.certificate: Forbidden: updates may not set certificate content`,
},
- {
- name: "add Denied condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- },
- {
- name: "remove Denied condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
- }},
- errs: []string{
- `status.conditions: Forbidden: updates may not remove a condition of type "Denied"`,
- },
- },
- {
- name: "add Failed condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{},
- },
- {
- name: "remove Failed condition",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
- }},
- errs: []string{
- `status.conditions: Forbidden: updates may not remove a condition of type "Failed"`,
- },
- },
- {
- name: "set certificate",
- newCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMeta, Spec: validSpec, Status: capi.CertificateSigningRequestStatus{
- Certificate: validCertificate,
- }},
- oldCSR: &capi.CertificateSigningRequest{ObjectMeta: validUpdateMetaWithFinalizers, Spec: validSpec},
- errs: []string{
- `status.certificate: Forbidden: updates may not set certificate content`,
- },
- },
- }
+ }}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -909,32 +865,28 @@ func Test_validateCertificateSigningRequestOptions(t *testing.T) {
{
name: "no status",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec},
- },
- {
+ }, {
name: "approved condition",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateApproved, Status: core.ConditionTrue}},
},
},
- },
- {
+ }, {
name: "denied condition",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateDenied, Status: core.ConditionTrue}},
},
},
- },
- {
+ }, {
name: "failed condition",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
Conditions: []capi.CertificateSigningRequestCondition{{Type: capi.CertificateFailed, Status: core.ConditionTrue}},
},
},
- },
- {
+ }, {
name: "approved+issued",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
@@ -969,8 +921,7 @@ func Test_validateCertificateSigningRequestOptions(t *testing.T) {
},
lenientOpts: certificateValidationOptions{allowEmptyConditionType: true},
strictErrs: []string{`status.conditions[0].type: Required value`},
- },
- {
+ }, {
name: "approved and denied",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
@@ -979,8 +930,7 @@ func Test_validateCertificateSigningRequestOptions(t *testing.T) {
},
lenientOpts: certificateValidationOptions{allowBothApprovedAndDenied: true},
strictErrs: []string{`status.conditions[1].type: Invalid value: "Denied": Approved and Denied conditions are mutually exclusive`},
- },
- {
+ }, {
name: "duplicate condition",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
@@ -1002,8 +952,7 @@ func Test_validateCertificateSigningRequestOptions(t *testing.T) {
},
lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
strictErrs: []string{`status.certificate: Invalid value: "": must contain at least one CERTIFICATE PEM block`},
- },
- {
+ }, {
name: "status.certificate, non-CERTIFICATE PEM",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
@@ -1013,8 +962,7 @@ func Test_validateCertificateSigningRequestOptions(t *testing.T) {
},
lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
strictErrs: []string{`status.certificate: Invalid value: "": only CERTIFICATE PEM blocks are allowed, found "CERTIFICATE1"`},
- },
- {
+ }, {
name: "status.certificate, PEM headers",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
@@ -1024,8 +972,7 @@ func Test_validateCertificateSigningRequestOptions(t *testing.T) {
},
lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
strictErrs: []string{`status.certificate: Invalid value: "": no PEM block headers are permitted`},
- },
- {
+ }, {
name: "status.certificate, non-base64 PEM",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
@@ -1035,8 +982,7 @@ func Test_validateCertificateSigningRequestOptions(t *testing.T) {
},
lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
strictErrs: []string{`status.certificate: Invalid value: "": must contain at least one CERTIFICATE PEM block`},
- },
- {
+ }, {
name: "status.certificate, empty PEM block",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
@@ -1046,8 +992,7 @@ func Test_validateCertificateSigningRequestOptions(t *testing.T) {
},
lenientOpts: certificateValidationOptions{allowArbitraryCertificate: true},
strictErrs: []string{`status.certificate: Invalid value: "": found CERTIFICATE PEM block containing 0 certificates`},
- },
- {
+ }, {
name: "status.certificate, non-ASN1 data",
csr: &capi.CertificateSigningRequest{ObjectMeta: validObjectMeta, Spec: validSpec,
Status: capi.CertificateSigningRequestStatus{
@@ -1164,241 +1109,223 @@ func TestValidateClusterTrustBundle(t *testing.T) {
bundle *capi.ClusterTrustBundle
opts ValidateClusterTrustBundleOptions
wantErrors field.ErrorList
- }{
- {
- description: "valid, no signer name",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- TrustBundle: goodCert1Block,
- },
+ }{{
+ description: "valid, no signer name",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Spec: capi.ClusterTrustBundleSpec{
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "invalid, no signer name, invalid name",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:bar:foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- TrustBundle: goodCert1Block,
- },
+ }, {
+ description: "invalid, no signer name, invalid name",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:bar:foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "name"), "k8s.io:bar:foo", "ClusterTrustBundle without signer name must not have \":\" in its name"),
+ Spec: capi.ClusterTrustBundleSpec{
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "valid, with signer name",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:bar",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: goodCert1Block,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "name"), "k8s.io:bar:foo", "ClusterTrustBundle without signer name must not have \":\" in its name"),
+ },
+ }, {
+ description: "valid, with signer name",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:bar",
+ },
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "invalid, with signer name, missing name prefix",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "look-ma-no-prefix",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: goodCert1Block,
- },
+ }, {
+ description: "invalid, with signer name, missing name prefix",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "look-ma-no-prefix",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "name"), "look-ma-no-prefix", "ClusterTrustBundle for signerName k8s.io/foo must be named with prefix k8s.io:foo:"),
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "invalid, with signer name, empty name suffix",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: goodCert1Block,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "name"), "look-ma-no-prefix", "ClusterTrustBundle for signerName k8s.io/foo must be named with prefix k8s.io:foo:"),
+ },
+ }, {
+ description: "invalid, with signer name, empty name suffix",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "name"), "k8s.io:foo:", `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])?)*')`),
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "invalid, with signer name, bad name suffix",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:123notvalidDNSSubdomain",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: goodCert1Block,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "name"), "k8s.io:foo:", `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])?)*')`),
+ },
+ }, {
+ description: "invalid, with signer name, bad name suffix",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:123notvalidDNSSubdomain",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "name"), "k8s.io:foo:123notvalidDNSSubdomain", `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])?)*')`),
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "valid, with signer name, with inter-block garbage",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:abc",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: "garbage\n" + goodCert1Block + "\ngarbage\n" + goodCert2Block,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "name"), "k8s.io:foo:123notvalidDNSSubdomain", `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])?)*')`),
+ },
+ }, {
+ description: "valid, with signer name, with inter-block garbage",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:abc",
+ },
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: "garbage\n" + goodCert1Block + "\ngarbage\n" + goodCert2Block,
},
},
- {
- description: "invalid, no signer name, no trust anchors",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: capi.ClusterTrustBundleSpec{},
+ }, {
+ description: "invalid, no signer name, no trust anchors",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ Spec: capi.ClusterTrustBundleSpec{},
+ },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ },
+ }, {
+ description: "invalid, no trust anchors",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:abc",
+ },
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
},
},
- {
- description: "invalid, no trust anchors",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:abc",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ },
+ }, {
+ description: "invalid, bad signer name",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "invalid:foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "invalid",
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "invalid, bad signer name",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "invalid:foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "invalid",
- TrustBundle: goodCert1Block,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "signerName"), "invalid", "must be a fully qualified domain and path of the form 'example.com/signer-name'"),
+ },
+ }, {
+ description: "invalid, no blocks",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "signerName"), "invalid", "must be a fully qualified domain and path of the form 'example.com/signer-name'"),
+ Spec: capi.ClusterTrustBundleSpec{
+ TrustBundle: "non block garbage",
},
},
- {
- description: "invalid, no blocks",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- TrustBundle: "non block garbage",
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ },
+ }, {
+ description: "invalid, bad block type",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ Spec: capi.ClusterTrustBundleSpec{
+ TrustBundle: goodCert1Block + "\n" + badBlockTypeBlock,
},
},
- {
- description: "invalid, bad block type",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- TrustBundle: goodCert1Block + "\n" + badBlockTypeBlock,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "entry 1 has bad block type: NOTACERTIFICATE"),
+ },
+ }, {
+ description: "invalid, block with headers",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "entry 1 has bad block type: NOTACERTIFICATE"),
+ Spec: capi.ClusterTrustBundleSpec{
+ TrustBundle: goodCert1Block + "\n" + badBlockHeadersBlock,
},
},
- {
- description: "invalid, block with headers",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- TrustBundle: goodCert1Block + "\n" + badBlockHeadersBlock,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "entry 1 has PEM block headers"),
+ },
+ }, {
+ description: "invalid, cert is not a CA cert",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "entry 1 has PEM block headers"),
+ Spec: capi.ClusterTrustBundleSpec{
+ TrustBundle: badNotCACertBlock,
},
},
- {
- description: "invalid, cert is not a CA cert",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- TrustBundle: badNotCACertBlock,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "entry 0 does not have the CA bit set"),
+ },
+ }, {
+ description: "invalid, duplicated blocks",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "entry 0 does not have the CA bit set"),
+ Spec: capi.ClusterTrustBundleSpec{
+ TrustBundle: goodCert1Block + "\n" + goodCert1AlternateBlock,
},
},
- {
- description: "invalid, duplicated blocks",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- TrustBundle: goodCert1Block + "\n" + goodCert1AlternateBlock,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "duplicate trust anchor (indices [0 1])"),
+ },
+ }, {
+ description: "invalid, non-certificate entry",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "duplicate trust anchor (indices [0 1])"),
+ Spec: capi.ClusterTrustBundleSpec{
+ TrustBundle: goodCert1Block + "\n" + badNonParseableBlock,
},
},
- {
- description: "invalid, non-certificate entry",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- TrustBundle: goodCert1Block + "\n" + badNonParseableBlock,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "entry 1 does not parse as X.509"),
+ },
+ }, {
+ description: "allow any old garbage in the PEM field if we suppress parsing",
+ bundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "entry 1 does not parse as X.509"),
+ Spec: capi.ClusterTrustBundleSpec{
+ TrustBundle: "garbage",
},
},
- {
- description: "allow any old garbage in the PEM field if we suppress parsing",
- bundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: capi.ClusterTrustBundleSpec{
- TrustBundle: "garbage",
- },
- },
- opts: ValidateClusterTrustBundleOptions{
- SuppressBundleParsing: true,
- },
+ opts: ValidateClusterTrustBundleOptions{
+ SuppressBundleParsing: true,
},
- }
+ }}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
gotErrors := ValidateClusterTrustBundle(tc.bundle, tc.opts)
@@ -1454,102 +1381,97 @@ func TestValidateClusterTrustBundleUpdate(t *testing.T) {
description string
oldBundle, newBundle *capi.ClusterTrustBundle
wantErrors field.ErrorList
- }{
- {
- description: "changing signer name disallowed",
- oldBundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:bar",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: goodCert1Block,
- },
+ }{{
+ description: "changing signer name disallowed",
+ oldBundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:bar",
},
- newBundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:bar",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/bar",
- TrustBundle: goodCert1Block,
- },
- },
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("metadata", "name"), "k8s.io:foo:bar", "ClusterTrustBundle for signerName k8s.io/bar must be named with prefix k8s.io:bar:"),
- field.Invalid(field.NewPath("spec", "signerName"), "k8s.io/bar", "field is immutable"),
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "adding certificate allowed",
- oldBundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:bar",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: goodCert1Block,
- },
+ newBundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:bar",
},
- newBundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:bar",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: goodCert1Block + "\n" + goodCert2Block,
- },
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/bar",
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "emptying trustBundle disallowed",
- oldBundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:bar",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: goodCert1Block,
- },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("metadata", "name"), "k8s.io:foo:bar", "ClusterTrustBundle for signerName k8s.io/bar must be named with prefix k8s.io:bar:"),
+ field.Invalid(field.NewPath("spec", "signerName"), "k8s.io/bar", "field is immutable"),
+ },
+ }, {
+ description: "adding certificate allowed",
+ oldBundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:bar",
},
- newBundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:bar",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: "",
- },
- },
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: goodCert1Block,
},
},
- {
- description: "emptying trustBundle (replace with non-block garbage) disallowed",
- oldBundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:bar",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: goodCert1Block,
- },
+ newBundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:bar",
},
- newBundle: &capi.ClusterTrustBundle{
- ObjectMeta: metav1.ObjectMeta{
- Name: "k8s.io:foo:bar",
- },
- Spec: capi.ClusterTrustBundleSpec{
- SignerName: "k8s.io/foo",
- TrustBundle: "non block garbage",
- },
- },
- wantErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: goodCert1Block + "\n" + goodCert2Block,
},
},
- }
+ }, {
+ description: "emptying trustBundle disallowed",
+ oldBundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:bar",
+ },
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: goodCert1Block,
+ },
+ },
+ newBundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:bar",
+ },
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: "",
+ },
+ },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ },
+ }, {
+ description: "emptying trustBundle (replace with non-block garbage) disallowed",
+ oldBundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:bar",
+ },
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: goodCert1Block,
+ },
+ },
+ newBundle: &capi.ClusterTrustBundle{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "k8s.io:foo:bar",
+ },
+ Spec: capi.ClusterTrustBundleSpec{
+ SignerName: "k8s.io/foo",
+ TrustBundle: "non block garbage",
+ },
+ },
+ wantErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec", "trustBundle"), "", "at least one trust anchor must be provided"),
+ },
+ }}
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
diff --git a/pkg/apis/core/v1/validation/validation_test.go b/pkg/apis/core/v1/validation/validation_test.go
index 97fd7735861..19738026bfa 100644
--- a/pkg/apis/core/v1/validation/validation_test.go
+++ b/pkg/apis/core/v1/validation/validation_test.go
@@ -31,56 +31,51 @@ func TestValidateResourceRequirements(t *testing.T) {
successCase := []struct {
name string
requirements v1.ResourceRequirements
- }{
- {
- name: "Resources with Requests equal to Limits",
- requirements: v1.ResourceRequirements{
- Requests: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
- Limits: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- },
+ }{{
+ name: "Resources with Requests equal to Limits",
+ requirements: v1.ResourceRequirements{
+ Requests: v1.ResourceList{
+ v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
+ v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
+ },
+ Limits: v1.ResourceList{
+ v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
+ v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
},
},
- {
- name: "Resources with only Limits",
- requirements: v1.ResourceRequirements{
- Limits: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- v1.ResourceName("my.org/resource"): resource.MustParse("10"),
- },
+ }, {
+ name: "Resources with only Limits",
+ requirements: v1.ResourceRequirements{
+ Limits: v1.ResourceList{
+ v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
+ v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
+ v1.ResourceName("my.org/resource"): resource.MustParse("10"),
},
},
- {
- name: "Resources with only Requests",
- requirements: v1.ResourceRequirements{
- Requests: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- v1.ResourceName("my.org/resource"): resource.MustParse("10"),
- },
+ }, {
+ name: "Resources with only Requests",
+ requirements: v1.ResourceRequirements{
+ Requests: v1.ResourceList{
+ v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
+ v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
+ v1.ResourceName("my.org/resource"): resource.MustParse("10"),
},
},
- {
- name: "Resources with Requests Less Than Limits",
- requirements: v1.ResourceRequirements{
- Requests: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("9"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("9G"),
- v1.ResourceName("my.org/resource"): resource.MustParse("9"),
- },
- Limits: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- v1.ResourceName("my.org/resource"): resource.MustParse("9"),
- },
+ }, {
+ name: "Resources with Requests Less Than Limits",
+ requirements: v1.ResourceRequirements{
+ Requests: v1.ResourceList{
+ v1.ResourceName(v1.ResourceCPU): resource.MustParse("9"),
+ v1.ResourceName(v1.ResourceMemory): resource.MustParse("9G"),
+ v1.ResourceName("my.org/resource"): resource.MustParse("9"),
+ },
+ Limits: v1.ResourceList{
+ v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
+ v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
+ v1.ResourceName("my.org/resource"): resource.MustParse("9"),
},
},
- }
+ }}
for _, tc := range successCase {
t.Run(tc.name, func(t *testing.T) {
if errs := ValidateResourceRequirements(&tc.requirements, field.NewPath("resources")); len(errs) != 0 {
@@ -94,41 +89,37 @@ func TestValidateResourceRequirements(t *testing.T) {
requirements v1.ResourceRequirements
skipLimitValueCheck bool
skipRequestValueCheck bool
- }{
- {
- name: "Resources with Requests Larger Than Limits",
- requirements: v1.ResourceRequirements{
- Requests: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
- v1.ResourceName("my.org/resource"): resource.MustParse("10m"),
- },
- Limits: v1.ResourceList{
- v1.ResourceName(v1.ResourceCPU): resource.MustParse("9"),
- v1.ResourceName(v1.ResourceMemory): resource.MustParse("9G"),
- v1.ResourceName("my.org/resource"): resource.MustParse("9m"),
- },
+ }{{
+ name: "Resources with Requests Larger Than Limits",
+ requirements: v1.ResourceRequirements{
+ Requests: v1.ResourceList{
+ v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
+ v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
+ v1.ResourceName("my.org/resource"): resource.MustParse("10m"),
+ },
+ Limits: v1.ResourceList{
+ v1.ResourceName(v1.ResourceCPU): resource.MustParse("9"),
+ v1.ResourceName(v1.ResourceMemory): resource.MustParse("9G"),
+ v1.ResourceName("my.org/resource"): resource.MustParse("9m"),
},
},
- {
- name: "Invalid Resources with Requests",
- requirements: v1.ResourceRequirements{
- Requests: v1.ResourceList{
- v1.ResourceName("my.org"): resource.MustParse("10m"),
- },
+ }, {
+ name: "Invalid Resources with Requests",
+ requirements: v1.ResourceRequirements{
+ Requests: v1.ResourceList{
+ v1.ResourceName("my.org"): resource.MustParse("10m"),
},
- skipRequestValueCheck: true,
},
- {
- name: "Invalid Resources with Limits",
- requirements: v1.ResourceRequirements{
- Limits: v1.ResourceList{
- v1.ResourceName("my.org"): resource.MustParse("9m"),
- },
+ skipRequestValueCheck: true,
+ }, {
+ name: "Invalid Resources with Limits",
+ requirements: v1.ResourceRequirements{
+ Limits: v1.ResourceList{
+ v1.ResourceName("my.org"): resource.MustParse("9m"),
},
- skipLimitValueCheck: true,
},
- }
+ skipLimitValueCheck: true,
+ }}
for _, tc := range errorCase {
t.Run(tc.name, func(t *testing.T) {
errs := ValidateResourceRequirements(&tc.requirements, field.NewPath("resources"))
@@ -168,28 +159,22 @@ func TestValidateContainerResourceName(t *testing.T) {
successCase := []struct {
name string
ResourceName string
- }{
- {
- name: "CPU resource",
- ResourceName: "cpu",
- },
- {
- name: "Memory resource",
- ResourceName: "memory",
- },
- {
- name: "Hugepages resource",
- ResourceName: "hugepages-2Mi",
- },
- {
- name: "Namespaced resource",
- ResourceName: "kubernetes.io/resource-foo",
- },
- {
- name: "Extended Resource",
- ResourceName: "my.org/resource-bar",
- },
- }
+ }{{
+ name: "CPU resource",
+ ResourceName: "cpu",
+ }, {
+ name: "Memory resource",
+ ResourceName: "memory",
+ }, {
+ name: "Hugepages resource",
+ ResourceName: "hugepages-2Mi",
+ }, {
+ name: "Namespaced resource",
+ ResourceName: "kubernetes.io/resource-foo",
+ }, {
+ name: "Extended Resource",
+ ResourceName: "my.org/resource-bar",
+ }}
for _, tc := range successCase {
t.Run(tc.name, func(t *testing.T) {
if errs := ValidateContainerResourceName(tc.ResourceName, field.NewPath(tc.ResourceName)); len(errs) != 0 {
@@ -201,20 +186,16 @@ func TestValidateContainerResourceName(t *testing.T) {
errorCase := []struct {
name string
ResourceName string
- }{
- {
- name: "Invalid standard resource",
- ResourceName: "cpu-core",
- },
- {
- name: "Invalid namespaced resource",
- ResourceName: "kubernetes.io/",
- },
- {
- name: "Invalid extended resource",
- ResourceName: "my.org-foo-resource",
- },
- }
+ }{{
+ name: "Invalid standard resource",
+ ResourceName: "cpu-core",
+ }, {
+ name: "Invalid namespaced resource",
+ ResourceName: "kubernetes.io/",
+ }, {
+ name: "Invalid extended resource",
+ ResourceName: "my.org-foo-resource",
+ }}
for _, tc := range errorCase {
t.Run(tc.name, func(t *testing.T) {
if errs := ValidateContainerResourceName(tc.ResourceName, field.NewPath(tc.ResourceName)); len(errs) == 0 {
@@ -239,45 +220,38 @@ func TestValidatePodLogOptions(t *testing.T) {
successCase := []struct {
name string
podLogOptions v1.PodLogOptions
- }{
- {
- name: "Empty PodLogOptions",
- podLogOptions: v1.PodLogOptions{},
+ }{{
+ name: "Empty PodLogOptions",
+ podLogOptions: v1.PodLogOptions{},
+ }, {
+ name: "PodLogOptions with TailLines",
+ podLogOptions: v1.PodLogOptions{
+ TailLines: &positiveLine,
},
- {
- name: "PodLogOptions with TailLines",
- podLogOptions: v1.PodLogOptions{
- TailLines: &positiveLine,
- },
+ }, {
+ name: "PodLogOptions with LimitBytes",
+ podLogOptions: v1.PodLogOptions{
+ LimitBytes: &limitBytesGreaterThan1,
},
- {
- name: "PodLogOptions with LimitBytes",
- podLogOptions: v1.PodLogOptions{
- LimitBytes: &limitBytesGreaterThan1,
- },
+ }, {
+ name: "PodLogOptions with only sinceSeconds",
+ podLogOptions: v1.PodLogOptions{
+ SinceSeconds: &sinceSecondsGreaterThan1,
},
- {
- name: "PodLogOptions with only sinceSeconds",
- podLogOptions: v1.PodLogOptions{
- SinceSeconds: &sinceSecondsGreaterThan1,
- },
+ }, {
+ name: "PodLogOptions with LimitBytes with TailLines",
+ podLogOptions: v1.PodLogOptions{
+ LimitBytes: &limitBytesGreaterThan1,
+ TailLines: &positiveLine,
},
- {
- name: "PodLogOptions with LimitBytes with TailLines",
- podLogOptions: v1.PodLogOptions{
- LimitBytes: &limitBytesGreaterThan1,
- TailLines: &positiveLine,
- },
+ }, {
+ name: "PodLogOptions with LimitBytes with TailLines with SinceSeconds",
+ podLogOptions: v1.PodLogOptions{
+ LimitBytes: &limitBytesGreaterThan1,
+ TailLines: &positiveLine,
+ SinceSeconds: &sinceSecondsGreaterThan1,
},
- {
- name: "PodLogOptions with LimitBytes with TailLines with SinceSeconds",
- podLogOptions: v1.PodLogOptions{
- LimitBytes: &limitBytesGreaterThan1,
- TailLines: &positiveLine,
- SinceSeconds: &sinceSecondsGreaterThan1,
- },
- },
- }
+ }}
for _, tc := range successCase {
t.Run(tc.name, func(t *testing.T) {
if errs := ValidatePodLogOptions(&tc.podLogOptions); len(errs) != 0 {
@@ -289,40 +263,36 @@ func TestValidatePodLogOptions(t *testing.T) {
errorCase := []struct {
name string
podLogOptions v1.PodLogOptions
- }{
- {
- name: "Invalid podLogOptions with Negative TailLines",
- podLogOptions: v1.PodLogOptions{
- TailLines: &negativeLine,
- LimitBytes: &limitBytesGreaterThan1,
- SinceSeconds: &sinceSecondsGreaterThan1,
- },
+ }{{
+ name: "Invalid podLogOptions with Negative TailLines",
+ podLogOptions: v1.PodLogOptions{
+ TailLines: &negativeLine,
+ LimitBytes: &limitBytesGreaterThan1,
+ SinceSeconds: &sinceSecondsGreaterThan1,
},
- {
- name: "Invalid podLogOptions with zero or negative LimitBytes",
- podLogOptions: v1.PodLogOptions{
- TailLines: &positiveLine,
- LimitBytes: &limitBytesLessThan1,
- SinceSeconds: &sinceSecondsGreaterThan1,
- },
+ }, {
+ name: "Invalid podLogOptions with zero or negative LimitBytes",
+ podLogOptions: v1.PodLogOptions{
+ TailLines: &positiveLine,
+ LimitBytes: &limitBytesLessThan1,
+ SinceSeconds: &sinceSecondsGreaterThan1,
},
- {
- name: "Invalid podLogOptions with zero or negative SinceSeconds",
- podLogOptions: v1.PodLogOptions{
- TailLines: &negativeLine,
- LimitBytes: &limitBytesGreaterThan1,
- SinceSeconds: &sinceSecondsLessThan1,
- },
- }, {
- name: "Invalid podLogOptions with both SinceSeconds and SinceTime set",
- podLogOptions: v1.PodLogOptions{
- TailLines: &negativeLine,
- LimitBytes: &limitBytesGreaterThan1,
- SinceSeconds: &sinceSecondsGreaterThan1,
- SinceTime: ×tamp,
- },
+ }, {
+ name: "Invalid podLogOptions with zero or negative SinceSeconds",
+ podLogOptions: v1.PodLogOptions{
+ TailLines: &negativeLine,
+ LimitBytes: &limitBytesGreaterThan1,
+ SinceSeconds: &sinceSecondsLessThan1,
},
- }
+ }, {
+ name: "Invalid podLogOptions with both SinceSeconds and SinceTime set",
+ podLogOptions: v1.PodLogOptions{
+ TailLines: &negativeLine,
+ LimitBytes: &limitBytesGreaterThan1,
+ SinceSeconds: &sinceSecondsGreaterThan1,
+ SinceTime: ×tamp,
+ },
+ }}
for _, tc := range errorCase {
t.Run(tc.name, func(t *testing.T) {
if errs := ValidatePodLogOptions(&tc.podLogOptions); len(errs) == 0 {
@@ -338,54 +308,37 @@ func TestAccumulateUniqueHostPorts(t *testing.T) {
containers []v1.Container
accumulator *sets.String
fldPath *field.Path
- }{
- {
- name: "HostPort is not allocated while containers use the same port with different protocol",
- containers: []v1.Container{
- {
- Ports: []v1.ContainerPort{
- {
- HostPort: 8080,
- Protocol: v1.ProtocolUDP,
- },
- },
- },
- {
- Ports: []v1.ContainerPort{
- {
- HostPort: 8080,
- Protocol: v1.ProtocolTCP,
- },
- },
- },
- },
- accumulator: &sets.String{},
- fldPath: field.NewPath("spec", "containers"),
- },
- {
- name: "HostPort is not allocated while containers use different ports",
- containers: []v1.Container{
- {
- Ports: []v1.ContainerPort{
- {
- HostPort: 8080,
- Protocol: v1.ProtocolUDP,
- },
- },
- },
- {
- Ports: []v1.ContainerPort{
- {
- HostPort: 8081,
- Protocol: v1.ProtocolUDP,
- },
- },
- },
- },
- accumulator: &sets.String{},
- fldPath: field.NewPath("spec", "containers"),
- },
- }
+ }{{
+ name: "HostPort is not allocated while containers use the same port with different protocol",
+ containers: []v1.Container{{
+ Ports: []v1.ContainerPort{{
+ HostPort: 8080,
+ Protocol: v1.ProtocolUDP,
+ }},
+ }, {
+ Ports: []v1.ContainerPort{{
+ HostPort: 8080,
+ Protocol: v1.ProtocolTCP,
+ }},
+ }},
+ accumulator: &sets.String{},
+ fldPath: field.NewPath("spec", "containers"),
+ }, {
+ name: "HostPort is not allocated while containers use different ports",
+ containers: []v1.Container{{
+ Ports: []v1.ContainerPort{{
+ HostPort: 8080,
+ Protocol: v1.ProtocolUDP,
+ }},
+ }, {
+ Ports: []v1.ContainerPort{{
+ HostPort: 8081,
+ Protocol: v1.ProtocolUDP,
+ }},
+ }},
+ accumulator: &sets.String{},
+ fldPath: field.NewPath("spec", "containers"),
+ }}
for _, tc := range successCase {
t.Run(tc.name, func(t *testing.T) {
if errs := AccumulateUniqueHostPorts(tc.containers, tc.accumulator, tc.fldPath); len(errs) != 0 {
@@ -398,54 +351,37 @@ func TestAccumulateUniqueHostPorts(t *testing.T) {
containers []v1.Container
accumulator *sets.String
fldPath *field.Path
- }{
- {
- name: "HostPort is already allocated while containers use the same port with UDP",
- containers: []v1.Container{
- {
- Ports: []v1.ContainerPort{
- {
- HostPort: 8080,
- Protocol: v1.ProtocolUDP,
- },
- },
- },
- {
- Ports: []v1.ContainerPort{
- {
- HostPort: 8080,
- Protocol: v1.ProtocolUDP,
- },
- },
- },
- },
- accumulator: &sets.String{},
- fldPath: field.NewPath("spec", "containers"),
- },
- {
- name: "HostPort is already allocated",
- containers: []v1.Container{
- {
- Ports: []v1.ContainerPort{
- {
- HostPort: 8080,
- Protocol: v1.ProtocolUDP,
- },
- },
- },
- {
- Ports: []v1.ContainerPort{
- {
- HostPort: 8081,
- Protocol: v1.ProtocolUDP,
- },
- },
- },
- },
- accumulator: &sets.String{"8080/UDP": sets.Empty{}},
- fldPath: field.NewPath("spec", "containers"),
- },
- }
+ }{{
+ name: "HostPort is already allocated while containers use the same port with UDP",
+ containers: []v1.Container{{
+ Ports: []v1.ContainerPort{{
+ HostPort: 8080,
+ Protocol: v1.ProtocolUDP,
+ }},
+ }, {
+ Ports: []v1.ContainerPort{{
+ HostPort: 8080,
+ Protocol: v1.ProtocolUDP,
+ }},
+ }},
+ accumulator: &sets.String{},
+ fldPath: field.NewPath("spec", "containers"),
+ }, {
+ name: "HostPort is already allocated",
+ containers: []v1.Container{{
+ Ports: []v1.ContainerPort{{
+ HostPort: 8080,
+ Protocol: v1.ProtocolUDP,
+ }},
+ }, {
+ Ports: []v1.ContainerPort{{
+ HostPort: 8081,
+ Protocol: v1.ProtocolUDP,
+ }},
+ }},
+ accumulator: &sets.String{"8080/UDP": sets.Empty{}},
+ fldPath: field.NewPath("spec", "containers"),
+ }}
for _, tc := range errorCase {
t.Run(tc.name, func(t *testing.T) {
if errs := AccumulateUniqueHostPorts(tc.containers, tc.accumulator, tc.fldPath); len(errs) == 0 {
diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go
index ce9269e8830..5caae4e1354 100644
--- a/pkg/apis/core/validation/validation_test.go
+++ b/pkg/apis/core/validation/validation_test.go
@@ -479,16 +479,12 @@ func TestValidatePersistentVolumes(t *testing.T) {
volume: testVolumeWithNodeAffinity(
&core.VolumeNodeAffinity{
Required: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Operator: core.NodeSelectorOpIn,
- Values: []string{"test-label-value"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"test-label-value"},
+ }},
+ }},
},
}),
},
@@ -1079,17 +1075,13 @@ func testVolumeWithNodeAffinity(affinity *core.VolumeNodeAffinity) *core.Persist
func simpleVolumeNodeAffinity(key, value string) *core.VolumeNodeAffinity {
return &core.VolumeNodeAffinity{
Required: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: key,
- Operator: core.NodeSelectorOpIn,
- Values: []string{value},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: key,
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{value},
+ }},
+ }},
},
}
}
@@ -1245,159 +1237,130 @@ func TestValidateVolumeNodeAffinityUpdate(t *testing.T) {
},
"affinity-same-terms-expressions-length-beta-to-GA-partially-changed": {
isExpectedFailure: false,
- oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
- },
- {
- topologyPair{kubeletapis.LabelOS, "bar"},
- topologyPair{kubeletapis.LabelArch, "bar"},
- topologyPair{v1.LabelInstanceType, "bar"},
- },
+ oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
+ }, {
+ topologyPair{kubeletapis.LabelOS, "bar"},
+ topologyPair{kubeletapis.LabelArch, "bar"},
+ topologyPair{v1.LabelInstanceType, "bar"},
+ },
})),
- newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelTopologyZone, "bar"},
- topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
- },
- {
- topologyPair{kubeletapis.LabelOS, "bar"},
- topologyPair{v1.LabelArchStable, "bar"},
- topologyPair{v1.LabelInstanceTypeStable, "bar"},
- },
+ newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelTopologyZone, "bar"},
+ topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
+ }, {
+ topologyPair{kubeletapis.LabelOS, "bar"},
+ topologyPair{v1.LabelArchStable, "bar"},
+ topologyPair{v1.LabelInstanceTypeStable, "bar"},
+ },
})),
},
"affinity-same-terms-expressions-length-beta-to-non-GA-partially-changed": {
isExpectedFailure: true,
- oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
- },
+ oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
+ },
})),
- newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- topologyPair{"foo", "bar"},
- },
+ newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ topologyPair{"foo", "bar"},
+ },
})),
},
"affinity-same-terms-expressions-length-GA-partially-changed": {
isExpectedFailure: true,
- oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelTopologyZone, "bar"},
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- topologyPair{v1.LabelOSStable, "bar"},
- },
+ oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelTopologyZone, "bar"},
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ topologyPair{v1.LabelOSStable, "bar"},
+ },
})),
- newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- topologyPair{v1.LabelOSStable, "bar"},
- },
+ newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ topologyPair{v1.LabelOSStable, "bar"},
+ },
})),
},
"affinity-same-terms-expressions-length-beta-fully-changed": {
isExpectedFailure: false,
- oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
- },
- {
- topologyPair{kubeletapis.LabelOS, "bar"},
- topologyPair{kubeletapis.LabelArch, "bar"},
- topologyPair{v1.LabelInstanceType, "bar"},
- },
+ oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
+ }, {
+ topologyPair{kubeletapis.LabelOS, "bar"},
+ topologyPair{kubeletapis.LabelArch, "bar"},
+ topologyPair{v1.LabelInstanceType, "bar"},
+ },
})),
- newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelTopologyZone, "bar"},
- topologyPair{v1.LabelTopologyRegion, "bar"},
- },
- {
- topologyPair{v1.LabelOSStable, "bar"},
- topologyPair{v1.LabelArchStable, "bar"},
- topologyPair{v1.LabelInstanceTypeStable, "bar"},
- },
+ newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelTopologyZone, "bar"},
+ topologyPair{v1.LabelTopologyRegion, "bar"},
+ }, {
+ topologyPair{v1.LabelOSStable, "bar"},
+ topologyPair{v1.LabelArchStable, "bar"},
+ topologyPair{v1.LabelInstanceTypeStable, "bar"},
+ },
})),
},
"affinity-same-terms-expressions-length-beta-GA-mixed-fully-changed": {
isExpectedFailure: true,
- oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- topologyPair{v1.LabelTopologyZone, "bar"},
- },
+ oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ topologyPair{v1.LabelTopologyZone, "bar"},
+ },
})),
- newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{"foo", "bar"},
- },
- {
- topologyPair{v1.LabelTopologyZone, "bar"},
- topologyPair{v1.LabelFailureDomainBetaZone, "bar2"},
- },
+ newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{"foo", "bar"},
+ }, {
+ topologyPair{v1.LabelTopologyZone, "bar"},
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar2"},
+ },
})),
},
"affinity-same-terms-length-different-expressions-length-beta-changed": {
isExpectedFailure: true,
- oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- },
+ oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ },
})),
- newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{v1.LabelTopologyZone, "bar"},
- topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
- },
+ newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{v1.LabelTopologyZone, "bar"},
+ topologyPair{v1.LabelFailureDomainBetaRegion, "bar"},
+ },
})),
},
"affinity-different-terms-expressions-length-beta-changed": {
isExpectedFailure: true,
- oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
- },
+ oldPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{v1.LabelFailureDomainBetaZone, "bar"},
+ },
})),
- newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{
- {
- topologyPair{v1.LabelTopologyZone, "bar"},
- },
- {
- topologyPair{v1.LabelArchStable, "bar"},
- },
+ newPV: testVolumeWithNodeAffinity(multipleVolumeNodeAffinity([][]topologyPair{{
+ topologyPair{v1.LabelTopologyZone, "bar"},
+ }, {
+ topologyPair{v1.LabelArchStable, "bar"},
+ },
})),
},
"nil-to-obj": {
@@ -1585,12 +1548,10 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
}
goodClaimSpec := core.PersistentVolumeClaimSpec{
Selector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: "Exists",
- },
- },
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: "Exists",
+ }},
},
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
@@ -1676,13 +1637,12 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
isExpectedFailure: ephemeral,
claim: func() *core.PersistentVolumeClaim {
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
- claim.OwnerReferences = []metav1.OwnerReference{
- {
- APIVersion: "v1",
- Kind: "pod",
- Name: "foo",
- UID: "ac051fac-2ead-46d9-b8b4-4e0fbeb7455d",
- },
+ claim.OwnerReferences = []metav1.OwnerReference{{
+ APIVersion: "v1",
+ Kind: "pod",
+ Name: "foo",
+ UID: "ac051fac-2ead-46d9-b8b4-4e0fbeb7455d",
+ },
}
return claim
}(),
@@ -1701,13 +1661,12 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
isExpectedFailure: ephemeral,
claim: func() *core.PersistentVolumeClaim {
claim := testVolumeClaim(goodName, goodNS, goodClaimSpec)
- claim.ManagedFields = []metav1.ManagedFieldsEntry{
- {
- FieldsType: "FieldsV1",
- Operation: "Apply",
- APIVersion: "apps/v1",
- Manager: "foo",
- },
+ claim.ManagedFields = []metav1.ManagedFieldsEntry{{
+ FieldsType: "FieldsV1",
+ Operation: "Apply",
+ APIVersion: "apps/v1",
+ Manager: "foo",
+ },
}
return claim
}(),
@@ -1790,12 +1749,10 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
isExpectedFailure: true,
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
Selector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: "Exists",
- },
- },
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: "Exists",
+ }},
},
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
@@ -1813,13 +1770,11 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
isExpectedFailure: true,
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
Selector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: "InvalidOp",
- Values: []string{"value1", "value2"},
- },
- },
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: "InvalidOp",
+ Values: []string{"value1", "value2"},
+ }},
},
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
@@ -1878,12 +1833,10 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
isExpectedFailure: true,
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
Selector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: "Exists",
- },
- },
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: "Exists",
+ }},
},
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
@@ -1900,12 +1853,10 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
isExpectedFailure: true,
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
Selector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: "Exists",
- },
- },
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: "Exists",
+ }},
},
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
@@ -1922,12 +1873,10 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
isExpectedFailure: true,
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
Selector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: "Exists",
- },
- },
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: "Exists",
+ }},
},
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadWriteOnce,
@@ -1985,18 +1934,17 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
var errs field.ErrorList
if ephemeral {
- volumes := []core.Volume{
- {
- Name: "foo",
- VolumeSource: core.VolumeSource{
- Ephemeral: &core.EphemeralVolumeSource{
- VolumeClaimTemplate: &core.PersistentVolumeClaimTemplate{
- ObjectMeta: scenario.claim.ObjectMeta,
- Spec: scenario.claim.Spec,
- },
+ volumes := []core.Volume{{
+ Name: "foo",
+ VolumeSource: core.VolumeSource{
+ Ephemeral: &core.EphemeralVolumeSource{
+ VolumeClaimTemplate: &core.PersistentVolumeClaimTemplate{
+ ObjectMeta: scenario.claim.ObjectMeta,
+ Spec: scenario.claim.Spec,
},
},
},
+ },
}
opts := PodValidationOptions{}
_, errs = ValidateVolumes(volumes, nil, field.NewPath(""), opts)
@@ -2854,63 +2802,51 @@ func TestValidateKeyToPath(t *testing.T) {
kp core.KeyToPath
ok bool
errtype field.ErrorType
- }{
- {
- kp: core.KeyToPath{Key: "k", Path: "p"},
- ok: true,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: "p/p/p/p"},
- ok: true,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: "p/..p/p../p..p"},
- ok: true,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: "p", Mode: utilpointer.Int32(0644)},
- ok: true,
- },
- {
- kp: core.KeyToPath{Key: "", Path: "p"},
- ok: false,
- errtype: field.ErrorTypeRequired,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: ""},
- ok: false,
- errtype: field.ErrorTypeRequired,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: "..p"},
- ok: false,
- errtype: field.ErrorTypeInvalid,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: "../p"},
- ok: false,
- errtype: field.ErrorTypeInvalid,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: "p/../p"},
- ok: false,
- errtype: field.ErrorTypeInvalid,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: "p/.."},
- ok: false,
- errtype: field.ErrorTypeInvalid,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: "p", Mode: utilpointer.Int32(01000)},
- ok: false,
- errtype: field.ErrorTypeInvalid,
- },
- {
- kp: core.KeyToPath{Key: "k", Path: "p", Mode: utilpointer.Int32(-1)},
- ok: false,
- errtype: field.ErrorTypeInvalid,
- },
+ }{{
+ kp: core.KeyToPath{Key: "k", Path: "p"},
+ ok: true,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: "p/p/p/p"},
+ ok: true,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: "p/..p/p../p..p"},
+ ok: true,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: "p", Mode: utilpointer.Int32(0644)},
+ ok: true,
+ }, {
+ kp: core.KeyToPath{Key: "", Path: "p"},
+ ok: false,
+ errtype: field.ErrorTypeRequired,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: ""},
+ ok: false,
+ errtype: field.ErrorTypeRequired,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: "..p"},
+ ok: false,
+ errtype: field.ErrorTypeInvalid,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: "../p"},
+ ok: false,
+ errtype: field.ErrorTypeInvalid,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: "p/../p"},
+ ok: false,
+ errtype: field.ErrorTypeInvalid,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: "p/.."},
+ ok: false,
+ errtype: field.ErrorTypeInvalid,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: "p", Mode: utilpointer.Int32(01000)},
+ ok: false,
+ errtype: field.ErrorTypeInvalid,
+ }, {
+ kp: core.KeyToPath{Key: "k", Path: "p", Mode: utilpointer.Int32(-1)},
+ ok: false,
+ errtype: field.ErrorTypeInvalid,
+ },
}
for i, tc := range testCases {
@@ -2936,26 +2872,23 @@ func TestValidateNFSVolumeSource(t *testing.T) {
errtype field.ErrorType
errfield string
errdetail string
- }{
- {
- name: "missing server",
- nfs: &core.NFSVolumeSource{Server: "", Path: "/tmp"},
- errtype: field.ErrorTypeRequired,
- errfield: "server",
- },
- {
- name: "missing path",
- nfs: &core.NFSVolumeSource{Server: "my-server", Path: ""},
- errtype: field.ErrorTypeRequired,
- errfield: "path",
- },
- {
- name: "abs path",
- nfs: &core.NFSVolumeSource{Server: "my-server", Path: "tmp"},
- errtype: field.ErrorTypeInvalid,
- errfield: "path",
- errdetail: "must be an absolute path",
- },
+ }{{
+ name: "missing server",
+ nfs: &core.NFSVolumeSource{Server: "", Path: "/tmp"},
+ errtype: field.ErrorTypeRequired,
+ errfield: "server",
+ }, {
+ name: "missing path",
+ nfs: &core.NFSVolumeSource{Server: "my-server", Path: ""},
+ errtype: field.ErrorTypeRequired,
+ errfield: "path",
+ }, {
+ name: "abs path",
+ nfs: &core.NFSVolumeSource{Server: "my-server", Path: "tmp"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "path",
+ errdetail: "must be an absolute path",
+ },
}
for i, tc := range testCases {
@@ -2983,25 +2916,22 @@ func TestValidateGlusterfs(t *testing.T) {
gfs *core.GlusterfsVolumeSource
errtype field.ErrorType
errfield string
- }{
- {
- name: "missing endpointname",
- gfs: &core.GlusterfsVolumeSource{EndpointsName: "", Path: "/tmp"},
- errtype: field.ErrorTypeRequired,
- errfield: "endpoints",
- },
- {
- name: "missing path",
- gfs: &core.GlusterfsVolumeSource{EndpointsName: "my-endpoint", Path: ""},
- errtype: field.ErrorTypeRequired,
- errfield: "path",
- },
- {
- name: "missing endpointname and path",
- gfs: &core.GlusterfsVolumeSource{EndpointsName: "", Path: ""},
- errtype: field.ErrorTypeRequired,
- errfield: "endpoints",
- },
+ }{{
+ name: "missing endpointname",
+ gfs: &core.GlusterfsVolumeSource{EndpointsName: "", Path: "/tmp"},
+ errtype: field.ErrorTypeRequired,
+ errfield: "endpoints",
+ }, {
+ name: "missing path",
+ gfs: &core.GlusterfsVolumeSource{EndpointsName: "my-endpoint", Path: ""},
+ errtype: field.ErrorTypeRequired,
+ errfield: "path",
+ }, {
+ name: "missing endpointname and path",
+ gfs: &core.GlusterfsVolumeSource{EndpointsName: "", Path: ""},
+ errtype: field.ErrorTypeRequired,
+ errfield: "endpoints",
+ },
}
for i, tc := range testCases {
@@ -3031,31 +2961,27 @@ func TestValidateGlusterfsPersistentVolumeSource(t *testing.T) {
gfs *core.GlusterfsPersistentVolumeSource
errtype field.ErrorType
errfield string
- }{
- {
- name: "missing endpointname",
- gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "", Path: "/tmp"},
- errtype: field.ErrorTypeRequired,
- errfield: "endpoints",
- },
- {
- name: "missing path",
- gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "my-endpoint", Path: ""},
- errtype: field.ErrorTypeRequired,
- errfield: "path",
- },
- {
- name: "non null endpointnamespace with empty string",
- gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "my-endpoint", Path: "/tmp", EndpointsNamespace: epNs},
- errtype: field.ErrorTypeInvalid,
- errfield: "endpointsNamespace",
- },
- {
- name: "missing endpointname and path",
- gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "", Path: ""},
- errtype: field.ErrorTypeRequired,
- errfield: "endpoints",
- },
+ }{{
+ name: "missing endpointname",
+ gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "", Path: "/tmp"},
+ errtype: field.ErrorTypeRequired,
+ errfield: "endpoints",
+ }, {
+ name: "missing path",
+ gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "my-endpoint", Path: ""},
+ errtype: field.ErrorTypeRequired,
+ errfield: "path",
+ }, {
+ name: "non null endpointnamespace with empty string",
+ gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "my-endpoint", Path: "/tmp", EndpointsNamespace: epNs},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "endpointsNamespace",
+ }, {
+ name: "missing endpointname and path",
+ gfs: &core.GlusterfsPersistentVolumeSource{EndpointsName: "", Path: ""},
+ errtype: field.ErrorTypeRequired,
+ errfield: "endpoints",
+ },
}
for i, tc := range testCases {
@@ -3081,89 +3007,73 @@ func TestValidateCSIVolumeSource(t *testing.T) {
csi *core.CSIVolumeSource
errtype field.ErrorType
errfield string
- }{
- {
- name: "all required fields ok",
- csi: &core.CSIVolumeSource{Driver: "test-driver"},
- },
- {
- name: "missing driver name",
- csi: &core.CSIVolumeSource{Driver: ""},
- errtype: field.ErrorTypeRequired,
- errfield: "driver",
- },
- {
- name: "driver name: ok no punctuations",
- csi: &core.CSIVolumeSource{Driver: "comgooglestoragecsigcepd"},
- },
- {
- name: "driver name: ok dot only",
- csi: &core.CSIVolumeSource{Driver: "io.kubernetes.storage.csi.flex"},
- },
- {
- name: "driver name: ok dash only",
- csi: &core.CSIVolumeSource{Driver: "io-kubernetes-storage-csi-flex"},
- },
- {
- name: "driver name: invalid underscore",
- csi: &core.CSIVolumeSource{Driver: "io_kubernetes_storage_csi_flex"},
- errtype: field.ErrorTypeInvalid,
- errfield: "driver",
- },
- {
- name: "driver name: invalid dot underscores",
- csi: &core.CSIVolumeSource{Driver: "io.kubernetes.storage_csi.flex"},
- errtype: field.ErrorTypeInvalid,
- errfield: "driver",
- },
- {
- name: "driver name: ok beginning with number",
- csi: &core.CSIVolumeSource{Driver: "2io.kubernetes.storage-csi.flex"},
- },
- {
- name: "driver name: ok ending with number",
- csi: &core.CSIVolumeSource{Driver: "io.kubernetes.storage-csi.flex2"},
- },
- {
- name: "driver name: invalid dot dash underscores",
- csi: &core.CSIVolumeSource{Driver: "io.kubernetes-storage.csi_flex"},
- errtype: field.ErrorTypeInvalid,
- errfield: "driver",
- },
+ }{{
+ name: "all required fields ok",
+ csi: &core.CSIVolumeSource{Driver: "test-driver"},
+ }, {
+ name: "missing driver name",
+ csi: &core.CSIVolumeSource{Driver: ""},
+ errtype: field.ErrorTypeRequired,
+ errfield: "driver",
+ }, {
+ name: "driver name: ok no punctuations",
+ csi: &core.CSIVolumeSource{Driver: "comgooglestoragecsigcepd"},
+ }, {
+ name: "driver name: ok dot only",
+ csi: &core.CSIVolumeSource{Driver: "io.kubernetes.storage.csi.flex"},
+ }, {
+ name: "driver name: ok dash only",
+ csi: &core.CSIVolumeSource{Driver: "io-kubernetes-storage-csi-flex"},
+ }, {
+ name: "driver name: invalid underscore",
+ csi: &core.CSIVolumeSource{Driver: "io_kubernetes_storage_csi_flex"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "driver",
+ }, {
+ name: "driver name: invalid dot underscores",
+ csi: &core.CSIVolumeSource{Driver: "io.kubernetes.storage_csi.flex"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "driver",
+ }, {
+ name: "driver name: ok beginning with number",
+ csi: &core.CSIVolumeSource{Driver: "2io.kubernetes.storage-csi.flex"},
+ }, {
+ name: "driver name: ok ending with number",
+ csi: &core.CSIVolumeSource{Driver: "io.kubernetes.storage-csi.flex2"},
+ }, {
+ name: "driver name: invalid dot dash underscores",
+ csi: &core.CSIVolumeSource{Driver: "io.kubernetes-storage.csi_flex"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "driver",
+ },
{
name: "driver name: ok length 1",
csi: &core.CSIVolumeSource{Driver: "a"},
- },
- {
+ }, {
name: "driver name: invalid length > 63",
csi: &core.CSIVolumeSource{Driver: strings.Repeat("g", 65)},
errtype: field.ErrorTypeTooLong,
errfield: "driver",
- },
- {
+ }, {
name: "driver name: invalid start char",
csi: &core.CSIVolumeSource{Driver: "_comgooglestoragecsigcepd"},
errtype: field.ErrorTypeInvalid,
errfield: "driver",
- },
- {
+ }, {
name: "driver name: invalid end char",
csi: &core.CSIVolumeSource{Driver: "comgooglestoragecsigcepd/"},
errtype: field.ErrorTypeInvalid,
errfield: "driver",
- },
- {
+ }, {
name: "driver name: invalid separators",
csi: &core.CSIVolumeSource{Driver: "com/google/storage/csi~gcepd"},
errtype: field.ErrorTypeInvalid,
errfield: "driver",
- },
- {
+ }, {
name: "valid nodePublishSecretRef",
csi: &core.CSIVolumeSource{Driver: "com.google.gcepd", NodePublishSecretRef: &core.LocalObjectReference{Name: "foobar"}},
- },
- {
+ }, {
name: "nodePublishSecretRef: invalid name missing",
csi: &core.CSIVolumeSource{Driver: "com.google.gcepd", NodePublishSecretRef: &core.LocalObjectReference{Name: ""}},
errtype: field.ErrorTypeRequired,
@@ -3194,206 +3104,167 @@ func TestValidateCSIPersistentVolumeSource(t *testing.T) {
csi *core.CSIPersistentVolumeSource
errtype field.ErrorType
errfield string
- }{
- {
- name: "all required fields ok",
- csi: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
- },
- {
- name: "with default values ok",
- csi: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123"},
- },
- {
- name: "missing driver name",
- csi: &core.CSIPersistentVolumeSource{VolumeHandle: "test-123"},
- errtype: field.ErrorTypeRequired,
- errfield: "driver",
- },
- {
- name: "missing volume handle",
- csi: &core.CSIPersistentVolumeSource{Driver: "my-driver"},
- errtype: field.ErrorTypeRequired,
- errfield: "volumeHandle",
- },
- {
- name: "driver name: ok no punctuations",
- csi: &core.CSIPersistentVolumeSource{Driver: "comgooglestoragecsigcepd", VolumeHandle: "test-123"},
- },
- {
- name: "driver name: ok dot only",
- csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes.storage.csi.flex", VolumeHandle: "test-123"},
- },
- {
- name: "driver name: ok dash only",
- csi: &core.CSIPersistentVolumeSource{Driver: "io-kubernetes-storage-csi-flex", VolumeHandle: "test-123"},
- },
- {
- name: "driver name: invalid underscore",
- csi: &core.CSIPersistentVolumeSource{Driver: "io_kubernetes_storage_csi_flex", VolumeHandle: "test-123"},
- errtype: field.ErrorTypeInvalid,
- errfield: "driver",
- },
- {
- name: "driver name: invalid dot underscores",
- csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes.storage_csi.flex", VolumeHandle: "test-123"},
- errtype: field.ErrorTypeInvalid,
- errfield: "driver",
- },
- {
- name: "driver name: ok beginning with number",
- csi: &core.CSIPersistentVolumeSource{Driver: "2io.kubernetes.storage-csi.flex", VolumeHandle: "test-123"},
- },
- {
- name: "driver name: ok ending with number",
- csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes.storage-csi.flex2", VolumeHandle: "test-123"},
- },
- {
- name: "driver name: invalid dot dash underscores",
- csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes-storage.csi_flex", VolumeHandle: "test-123"},
- errtype: field.ErrorTypeInvalid,
- errfield: "driver",
- },
- {
- name: "driver name: invalid length 0",
- csi: &core.CSIPersistentVolumeSource{Driver: "", VolumeHandle: "test-123"},
- errtype: field.ErrorTypeRequired,
- errfield: "driver",
- },
- {
- name: "driver name: ok length 1",
- csi: &core.CSIPersistentVolumeSource{Driver: "a", VolumeHandle: "test-123"},
- },
- {
- name: "driver name: invalid length > 63",
- csi: &core.CSIPersistentVolumeSource{Driver: strings.Repeat("g", 65), VolumeHandle: "test-123"},
- errtype: field.ErrorTypeTooLong,
- errfield: "driver",
- },
- {
- name: "driver name: invalid start char",
- csi: &core.CSIPersistentVolumeSource{Driver: "_comgooglestoragecsigcepd", VolumeHandle: "test-123"},
- errtype: field.ErrorTypeInvalid,
- errfield: "driver",
- },
- {
- name: "driver name: invalid end char",
- csi: &core.CSIPersistentVolumeSource{Driver: "comgooglestoragecsigcepd/", VolumeHandle: "test-123"},
- errtype: field.ErrorTypeInvalid,
- errfield: "driver",
- },
- {
- name: "driver name: invalid separators",
- csi: &core.CSIPersistentVolumeSource{Driver: "com/google/storage/csi~gcepd", VolumeHandle: "test-123"},
- errtype: field.ErrorTypeInvalid,
- errfield: "driver",
- },
- {
- name: "controllerExpandSecretRef: invalid name missing",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Namespace: "default"}},
- errtype: field.ErrorTypeRequired,
- errfield: "controllerExpandSecretRef.name",
- },
- {
- name: "controllerExpandSecretRef: invalid namespace missing",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar"}},
- errtype: field.ErrorTypeRequired,
- errfield: "controllerExpandSecretRef.namespace",
- },
- {
- name: "valid controllerExpandSecretRef",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
- },
- {
- name: "controllerPublishSecretRef: invalid name missing",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerPublishSecretRef: &core.SecretReference{Namespace: "default"}},
- errtype: field.ErrorTypeRequired,
- errfield: "controllerPublishSecretRef.name",
- },
- {
- name: "controllerPublishSecretRef: invalid namespace missing",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerPublishSecretRef: &core.SecretReference{Name: "foobar"}},
- errtype: field.ErrorTypeRequired,
- errfield: "controllerPublishSecretRef.namespace",
- },
- {
- name: "valid controllerPublishSecretRef",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerPublishSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
- },
- {
- name: "valid nodePublishSecretRef",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
- },
- {
- name: "nodePublishSecretRef: invalid name missing",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Namespace: "foobar"}},
- errtype: field.ErrorTypeRequired,
- errfield: "nodePublishSecretRef.name",
- },
- {
- name: "nodePublishSecretRef: invalid namespace missing",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Name: "foobar"}},
- errtype: field.ErrorTypeRequired,
- errfield: "nodePublishSecretRef.namespace",
- },
- {
- name: "nodeExpandSecretRef: invalid name missing",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Namespace: "default"}},
- errtype: field.ErrorTypeRequired,
- errfield: "nodeExpandSecretRef.name",
- },
- {
- name: "nodeExpandSecretRef: invalid namespace missing",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Name: "foobar"}},
- errtype: field.ErrorTypeRequired,
- errfield: "nodeExpandSecretRef.namespace",
- },
- {
- name: "valid nodeExpandSecretRef",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
- },
- {
- name: "Invalid nodePublishSecretRef",
- csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
- },
+ }{{
+ name: "all required fields ok",
+ csi: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123", ReadOnly: true},
+ }, {
+ name: "with default values ok",
+ csi: &core.CSIPersistentVolumeSource{Driver: "test-driver", VolumeHandle: "test-123"},
+ }, {
+ name: "missing driver name",
+ csi: &core.CSIPersistentVolumeSource{VolumeHandle: "test-123"},
+ errtype: field.ErrorTypeRequired,
+ errfield: "driver",
+ }, {
+ name: "missing volume handle",
+ csi: &core.CSIPersistentVolumeSource{Driver: "my-driver"},
+ errtype: field.ErrorTypeRequired,
+ errfield: "volumeHandle",
+ }, {
+ name: "driver name: ok no punctuations",
+ csi: &core.CSIPersistentVolumeSource{Driver: "comgooglestoragecsigcepd", VolumeHandle: "test-123"},
+ }, {
+ name: "driver name: ok dot only",
+ csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes.storage.csi.flex", VolumeHandle: "test-123"},
+ }, {
+ name: "driver name: ok dash only",
+ csi: &core.CSIPersistentVolumeSource{Driver: "io-kubernetes-storage-csi-flex", VolumeHandle: "test-123"},
+ }, {
+ name: "driver name: invalid underscore",
+ csi: &core.CSIPersistentVolumeSource{Driver: "io_kubernetes_storage_csi_flex", VolumeHandle: "test-123"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "driver",
+ }, {
+ name: "driver name: invalid dot underscores",
+ csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes.storage_csi.flex", VolumeHandle: "test-123"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "driver",
+ }, {
+ name: "driver name: ok beginning with number",
+ csi: &core.CSIPersistentVolumeSource{Driver: "2io.kubernetes.storage-csi.flex", VolumeHandle: "test-123"},
+ }, {
+ name: "driver name: ok ending with number",
+ csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes.storage-csi.flex2", VolumeHandle: "test-123"},
+ }, {
+ name: "driver name: invalid dot dash underscores",
+ csi: &core.CSIPersistentVolumeSource{Driver: "io.kubernetes-storage.csi_flex", VolumeHandle: "test-123"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "driver",
+ }, {
+ name: "driver name: invalid length 0",
+ csi: &core.CSIPersistentVolumeSource{Driver: "", VolumeHandle: "test-123"},
+ errtype: field.ErrorTypeRequired,
+ errfield: "driver",
+ }, {
+ name: "driver name: ok length 1",
+ csi: &core.CSIPersistentVolumeSource{Driver: "a", VolumeHandle: "test-123"},
+ }, {
+ name: "driver name: invalid length > 63",
+ csi: &core.CSIPersistentVolumeSource{Driver: strings.Repeat("g", 65), VolumeHandle: "test-123"},
+ errtype: field.ErrorTypeTooLong,
+ errfield: "driver",
+ }, {
+ name: "driver name: invalid start char",
+ csi: &core.CSIPersistentVolumeSource{Driver: "_comgooglestoragecsigcepd", VolumeHandle: "test-123"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "driver",
+ }, {
+ name: "driver name: invalid end char",
+ csi: &core.CSIPersistentVolumeSource{Driver: "comgooglestoragecsigcepd/", VolumeHandle: "test-123"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "driver",
+ }, {
+ name: "driver name: invalid separators",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com/google/storage/csi~gcepd", VolumeHandle: "test-123"},
+ errtype: field.ErrorTypeInvalid,
+ errfield: "driver",
+ }, {
+ name: "controllerExpandSecretRef: invalid name missing",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Namespace: "default"}},
+ errtype: field.ErrorTypeRequired,
+ errfield: "controllerExpandSecretRef.name",
+ }, {
+ name: "controllerExpandSecretRef: invalid namespace missing",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar"}},
+ errtype: field.ErrorTypeRequired,
+ errfield: "controllerExpandSecretRef.namespace",
+ }, {
+ name: "valid controllerExpandSecretRef",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
+ }, {
+ name: "controllerPublishSecretRef: invalid name missing",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerPublishSecretRef: &core.SecretReference{Namespace: "default"}},
+ errtype: field.ErrorTypeRequired,
+ errfield: "controllerPublishSecretRef.name",
+ }, {
+ name: "controllerPublishSecretRef: invalid namespace missing",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerPublishSecretRef: &core.SecretReference{Name: "foobar"}},
+ errtype: field.ErrorTypeRequired,
+ errfield: "controllerPublishSecretRef.namespace",
+ }, {
+ name: "valid controllerPublishSecretRef",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerPublishSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
+ }, {
+ name: "valid nodePublishSecretRef",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
+ }, {
+ name: "nodePublishSecretRef: invalid name missing",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Namespace: "foobar"}},
+ errtype: field.ErrorTypeRequired,
+ errfield: "nodePublishSecretRef.name",
+ }, {
+ name: "nodePublishSecretRef: invalid namespace missing",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Name: "foobar"}},
+ errtype: field.ErrorTypeRequired,
+ errfield: "nodePublishSecretRef.namespace",
+ }, {
+ name: "nodeExpandSecretRef: invalid name missing",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Namespace: "default"}},
+ errtype: field.ErrorTypeRequired,
+ errfield: "nodeExpandSecretRef.name",
+ }, {
+ name: "nodeExpandSecretRef: invalid namespace missing",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Name: "foobar"}},
+ errtype: field.ErrorTypeRequired,
+ errfield: "nodeExpandSecretRef.namespace",
+ }, {
+ name: "valid nodeExpandSecretRef",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
+ }, {
+ name: "Invalid nodePublishSecretRef",
+ csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Name: "foobar", Namespace: "default"}},
+ },
// tests with allowDNSSubDomainSecretName flag on/off
{
name: "valid nodeExpandSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Name: strings.Repeat("g", 63), Namespace: "default"}},
- },
- {
+ }, {
name: "valid long nodeExpandSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Name: strings.Repeat("g", 65), Namespace: "default"}},
- },
- {
+ }, {
name: "Invalid nodeExpandSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodeExpandSecretRef: &core.SecretReference{Name: strings.Repeat("g", 255), Namespace: "default"}},
errtype: field.ErrorTypeInvalid,
errfield: "nodeExpandSecretRef.name",
- },
- {
+ }, {
name: "valid nodePublishSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Name: strings.Repeat("g", 63), Namespace: "default"}},
- },
- {
+ }, {
name: "valid long nodePublishSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Name: strings.Repeat("g", 65), Namespace: "default"}},
- },
- {
+ }, {
name: "Invalid nodePublishSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", NodePublishSecretRef: &core.SecretReference{Name: strings.Repeat("g", 255), Namespace: "default"}},
errtype: field.ErrorTypeInvalid,
errfield: "nodePublishSecretRef.name",
- },
- {
+ }, {
name: "valid ControllerExpandSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: strings.Repeat("g", 63), Namespace: "default"}},
- },
- {
+ }, {
name: "valid long ControllerExpandSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: strings.Repeat("g", 65), Namespace: "default"}},
- },
- {
+ }, {
name: "Invalid ControllerExpandSecretRef",
csi: &core.CSIPersistentVolumeSource{Driver: "com.google.gcepd", VolumeHandle: "foobar", ControllerExpandSecretRef: &core.SecretReference{Name: strings.Repeat("g", 255), Namespace: "default"}},
errtype: field.ErrorTypeInvalid,
@@ -3446,8 +3317,7 @@ func TestValidateVolumes(t *testing.T) {
EmptyDir: &core.EmptyDirVolumeSource{},
},
},
- },
- {
+ }, {
name: "valid num name",
vol: core.Volume{
Name: "123",
@@ -3455,8 +3325,7 @@ func TestValidateVolumes(t *testing.T) {
EmptyDir: &core.EmptyDirVolumeSource{},
},
},
- },
- {
+ }, {
name: "valid alphanum name",
vol: core.Volume{
Name: "empty-123",
@@ -3464,8 +3333,7 @@ func TestValidateVolumes(t *testing.T) {
EmptyDir: &core.EmptyDirVolumeSource{},
},
},
- },
- {
+ }, {
name: "valid numalpha name",
vol: core.Volume{
Name: "123-empty",
@@ -3473,8 +3341,7 @@ func TestValidateVolumes(t *testing.T) {
EmptyDir: &core.EmptyDirVolumeSource{},
},
},
- },
- {
+ }, {
name: "zero-length name",
vol: core.Volume{
Name: "",
@@ -3484,8 +3351,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "name",
}},
- },
- {
+ }, {
name: "name > 63 characters",
vol: core.Volume{
Name: strings.Repeat("a", 64),
@@ -3496,8 +3362,7 @@ func TestValidateVolumes(t *testing.T) {
field: "name",
detail: "must be no more than",
}},
- },
- {
+ }, {
name: "name has dots",
vol: core.Volume{
Name: "a.b.c",
@@ -3508,8 +3373,7 @@ func TestValidateVolumes(t *testing.T) {
field: "name",
detail: "must not contain dots",
}},
- },
- {
+ }, {
name: "name not a DNS label",
vol: core.Volume{
Name: "Not a DNS label!",
@@ -3582,8 +3446,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeNotSupported,
field: "type",
}},
- },
- {
+ }, {
name: "invalid HostPath backsteps",
vol: core.Volume{
Name: "hostpath",
@@ -3643,8 +3506,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid GitRepo in .",
vol: core.Volume{
Name: "git-repo-dot",
@@ -3655,8 +3517,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid GitRepo with .. in name",
vol: core.Volume{
Name: "git-repo-dot-dot-foo",
@@ -3667,8 +3528,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "GitRepo starts with ../",
vol: core.Volume{
Name: "gitrepo",
@@ -3684,8 +3544,7 @@ func TestValidateVolumes(t *testing.T) {
field: "gitRepo.directory",
detail: `must not contain '..'`,
}},
- },
- {
+ }, {
name: "GitRepo contains ..",
vol: core.Volume{
Name: "gitrepo",
@@ -3701,8 +3560,7 @@ func TestValidateVolumes(t *testing.T) {
field: "gitRepo.directory",
detail: `must not contain '..'`,
}},
- },
- {
+ }, {
name: "GitRepo absolute target",
vol: core.Volume{
Name: "gitrepo",
@@ -3733,8 +3591,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid IQN: eui format",
vol: core.Volume{
Name: "iscsi",
@@ -3748,8 +3605,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid IQN: naa format",
vol: core.Volume{
Name: "iscsi",
@@ -3763,8 +3619,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "empty portal",
vol: core.Volume{
Name: "iscsi",
@@ -3782,8 +3637,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "iscsi.targetPortal",
}},
- },
- {
+ }, {
name: "empty iqn",
vol: core.Volume{
Name: "iscsi",
@@ -3801,8 +3655,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "iscsi.iqn",
}},
- },
- {
+ }, {
name: "invalid IQN: iqn format",
vol: core.Volume{
Name: "iscsi",
@@ -3820,8 +3673,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "iscsi.iqn",
}},
- },
- {
+ }, {
name: "invalid IQN: eui format",
vol: core.Volume{
Name: "iscsi",
@@ -3839,8 +3691,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "iscsi.iqn",
}},
- },
- {
+ }, {
name: "invalid IQN: naa format",
vol: core.Volume{
Name: "iscsi",
@@ -3858,8 +3709,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "iscsi.iqn",
}},
- },
- {
+ }, {
name: "valid initiatorName",
vol: core.Volume{
Name: "iscsi",
@@ -3874,8 +3724,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "invalid initiatorName",
vol: core.Volume{
Name: "iscsi",
@@ -3894,8 +3743,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "iscsi.initiatorname",
}},
- },
- {
+ }, {
name: "empty secret",
vol: core.Volume{
Name: "iscsi",
@@ -3914,8 +3762,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "iscsi.secretRef",
}},
- },
- {
+ }, {
name: "empty secret",
vol: core.Volume{
Name: "iscsi",
@@ -3946,8 +3793,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid Secret with defaultMode",
vol: core.Volume{
Name: "secret",
@@ -3958,8 +3804,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid Secret with projection and mode",
vol: core.Volume{
Name: "secret",
@@ -3974,8 +3819,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid Secret with subdir projection",
vol: core.Volume{
Name: "secret",
@@ -3989,8 +3833,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "secret with missing path",
vol: core.Volume{
Name: "secret",
@@ -4005,8 +3848,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "secret.items[0].path",
}},
- },
- {
+ }, {
name: "secret with leading ..",
vol: core.Volume{
Name: "secret",
@@ -4021,8 +3863,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "secret.items[0].path",
}},
- },
- {
+ }, {
name: "secret with .. inside",
vol: core.Volume{
Name: "secret",
@@ -4037,8 +3878,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "secret.items[0].path",
}},
- },
- {
+ }, {
name: "secret with invalid positive defaultMode",
vol: core.Volume{
Name: "secret",
@@ -4053,8 +3893,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "secret.defaultMode",
}},
- },
- {
+ }, {
name: "secret with invalid negative defaultMode",
vol: core.Volume{
Name: "secret",
@@ -4083,8 +3922,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid ConfigMap with defaultMode",
vol: core.Volume{
Name: "cfgmap",
@@ -4097,8 +3935,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid ConfigMap with projection and mode",
vol: core.Volume{
Name: "cfgmap",
@@ -4114,8 +3951,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid ConfigMap with subdir projection",
vol: core.Volume{
Name: "cfgmap",
@@ -4130,8 +3966,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "configmap with missing path",
vol: core.Volume{
Name: "cfgmap",
@@ -4146,8 +3981,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "configMap.items[0].path",
}},
- },
- {
+ }, {
name: "configmap with leading ..",
vol: core.Volume{
Name: "cfgmap",
@@ -4162,8 +3996,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "configMap.items[0].path",
}},
- },
- {
+ }, {
name: "configmap with .. inside",
vol: core.Volume{
Name: "cfgmap",
@@ -4178,8 +4011,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "configMap.items[0].path",
}},
- },
- {
+ }, {
name: "configmap with invalid positive defaultMode",
vol: core.Volume{
Name: "cfgmap",
@@ -4194,8 +4026,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "configMap.defaultMode",
}},
- },
- {
+ }, {
name: "configmap with invalid negative defaultMode",
vol: core.Volume{
Name: "cfgmap",
@@ -4224,8 +4055,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "empty hosts",
vol: core.Volume{
Name: "glusterfs",
@@ -4241,8 +4071,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "glusterfs.endpoints",
}},
- },
- {
+ }, {
name: "empty path",
vol: core.Volume{
Name: "glusterfs",
@@ -4270,8 +4099,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "valid Flocker -- datasetName",
vol: core.Volume{
Name: "flocker",
@@ -4281,8 +4109,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "both empty",
vol: core.Volume{
Name: "flocker",
@@ -4296,8 +4123,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "flocker",
}},
- },
- {
+ }, {
name: "both specified",
vol: core.Volume{
Name: "flocker",
@@ -4312,8 +4138,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "flocker",
}},
- },
- {
+ }, {
name: "slash in flocker datasetName",
vol: core.Volume{
Name: "flocker",
@@ -4342,8 +4167,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "empty rbd monitors",
vol: core.Volume{
Name: "rbd",
@@ -4359,8 +4183,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "rbd.monitors",
}},
- },
- {
+ }, {
name: "empty image",
vol: core.Volume{
Name: "rbd",
@@ -4402,8 +4225,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "empty cephfs monitors",
vol: core.Volume{
Name: "cephfs",
@@ -4425,151 +4247,129 @@ func TestValidateVolumes(t *testing.T) {
Name: "downwardapi",
VolumeSource: core.VolumeSource{
DownwardAPI: &core.DownwardAPIVolumeSource{
- Items: []core.DownwardAPIVolumeFile{
- {
- Path: "labels",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.labels",
- },
+ Items: []core.DownwardAPIVolumeFile{{
+ Path: "labels",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.labels",
},
- {
- Path: "labels with subscript",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.labels['key']",
- },
+ }, {
+ Path: "labels with subscript",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.labels['key']",
},
- {
- Path: "labels with complex subscript",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.labels['test.example.com/key']",
- },
+ }, {
+ Path: "labels with complex subscript",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.labels['test.example.com/key']",
},
- {
- Path: "annotations",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.annotations",
- },
+ }, {
+ Path: "annotations",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.annotations",
},
- {
- Path: "annotations with subscript",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.annotations['key']",
- },
+ }, {
+ Path: "annotations with subscript",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.annotations['key']",
},
- {
- Path: "annotations with complex subscript",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.annotations['TEST.EXAMPLE.COM/key']",
- },
+ }, {
+ Path: "annotations with complex subscript",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.annotations['TEST.EXAMPLE.COM/key']",
},
- {
- Path: "namespace",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.namespace",
- },
+ }, {
+ Path: "namespace",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.namespace",
},
- {
- Path: "name",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.name",
- },
+ }, {
+ Path: "name",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.name",
},
- {
- Path: "path/with/subdirs",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.labels",
- },
+ }, {
+ Path: "path/with/subdirs",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.labels",
},
- {
- Path: "path/./withdot",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.labels",
- },
+ }, {
+ Path: "path/./withdot",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.labels",
},
- {
- Path: "path/with/embedded..dotdot",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.labels",
- },
+ }, {
+ Path: "path/with/embedded..dotdot",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.labels",
},
- {
- Path: "path/with/leading/..dotdot",
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.labels",
- },
+ }, {
+ Path: "path/with/leading/..dotdot",
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.labels",
},
- {
- Path: "cpu_limit",
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "limits.cpu",
- },
+ }, {
+ Path: "cpu_limit",
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "limits.cpu",
},
- {
- Path: "cpu_request",
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "requests.cpu",
- },
+ }, {
+ Path: "cpu_request",
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "requests.cpu",
},
- {
- Path: "memory_limit",
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "limits.memory",
- },
+ }, {
+ Path: "memory_limit",
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "limits.memory",
},
- {
- Path: "memory_request",
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "requests.memory",
- },
+ }, {
+ Path: "memory_request",
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "requests.memory",
},
- },
+ }},
},
},
},
- },
- {
+ }, {
name: "hugepages-downwardAPI-enabled",
vol: core.Volume{
Name: "downwardapi",
VolumeSource: core.VolumeSource{
DownwardAPI: &core.DownwardAPIVolumeSource{
- Items: []core.DownwardAPIVolumeFile{
- {
- Path: "hugepages_request",
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "requests.hugepages-2Mi",
- },
+ Items: []core.DownwardAPIVolumeFile{{
+ Path: "hugepages_request",
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "requests.hugepages-2Mi",
},
- {
- Path: "hugepages_limit",
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "limits.hugepages-2Mi",
- },
+ }, {
+ Path: "hugepages_limit",
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "limits.hugepages-2Mi",
},
- },
+ }},
},
},
},
- },
- {
+ }, {
name: "downapi valid defaultMode",
vol: core.Volume{
Name: "downapi",
@@ -4579,8 +4379,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "downapi valid item mode",
vol: core.Volume{
Name: "downapi",
@@ -4597,8 +4396,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "downapi invalid positive item mode",
vol: core.Volume{
Name: "downapi",
@@ -4619,8 +4417,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "downwardAPI.mode",
}},
- },
- {
+ }, {
name: "downapi invalid negative item mode",
vol: core.Volume{
Name: "downapi",
@@ -4641,8 +4438,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "downwardAPI.mode",
}},
- },
- {
+ }, {
name: "downapi empty metatada path",
vol: core.Volume{
Name: "downapi",
@@ -4662,8 +4458,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "downwardAPI.path",
}},
- },
- {
+ }, {
name: "downapi absolute path",
vol: core.Volume{
Name: "downapi",
@@ -4683,8 +4478,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "downwardAPI.path",
}},
- },
- {
+ }, {
name: "downapi dot dot path",
vol: core.Volume{
Name: "downapi",
@@ -4705,8 +4499,7 @@ func TestValidateVolumes(t *testing.T) {
field: "downwardAPI.path",
detail: `must not contain '..'`,
}},
- },
- {
+ }, {
name: "downapi dot dot file name",
vol: core.Volume{
Name: "downapi",
@@ -4727,8 +4520,7 @@ func TestValidateVolumes(t *testing.T) {
field: "downwardAPI.path",
detail: `must not start with '..'`,
}},
- },
- {
+ }, {
name: "downapi dot dot first level dirent",
vol: core.Volume{
Name: "downapi",
@@ -4749,8 +4541,7 @@ func TestValidateVolumes(t *testing.T) {
field: "downwardAPI.path",
detail: `must not start with '..'`,
}},
- },
- {
+ }, {
name: "downapi fieldRef and ResourceFieldRef together",
vol: core.Volume{
Name: "downapi",
@@ -4775,8 +4566,7 @@ func TestValidateVolumes(t *testing.T) {
field: "downwardAPI",
detail: "fieldRef and resourceFieldRef can not be specified simultaneously",
}},
- },
- {
+ }, {
name: "downapi invalid positive defaultMode",
vol: core.Volume{
Name: "downapi",
@@ -4790,8 +4580,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "downwardAPI.defaultMode",
}},
- },
- {
+ }, {
name: "downapi invalid negative defaultMode",
vol: core.Volume{
Name: "downapi",
@@ -4820,8 +4609,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "FC valid wwids",
vol: core.Volume{
Name: "fc",
@@ -4833,8 +4621,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "FC empty targetWWNs and wwids",
vol: core.Volume{
Name: "fc",
@@ -4853,8 +4640,7 @@ func TestValidateVolumes(t *testing.T) {
field: "fc.targetWWNs",
detail: "must specify either targetWWNs or wwids",
}},
- },
- {
+ }, {
name: "FC invalid: both targetWWNs and wwids simultaneously",
vol: core.Volume{
Name: "fc",
@@ -4873,8 +4659,7 @@ func TestValidateVolumes(t *testing.T) {
field: "fc.targetWWNs",
detail: "targetWWNs and wwids can not be specified simultaneously",
}},
- },
- {
+ }, {
name: "FC valid targetWWNs and empty lun",
vol: core.Volume{
Name: "fc",
@@ -4892,8 +4677,7 @@ func TestValidateVolumes(t *testing.T) {
field: "fc.lun",
detail: "lun is required if targetWWNs is specified",
}},
- },
- {
+ }, {
name: "FC valid targetWWNs and invalid lun",
vol: core.Volume{
Name: "fc",
@@ -4938,8 +4722,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "AzureFile empty secret",
vol: core.Volume{
Name: "azure-file",
@@ -4955,8 +4738,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "azureFile.secretName",
}},
- },
- {
+ }, {
name: "AzureFile empty share",
vol: core.Volume{
Name: "azure-file",
@@ -4989,8 +4771,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "empty registry quobyte",
vol: core.Volume{
Name: "quobyte",
@@ -5005,8 +4786,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "quobyte.registry",
}},
- },
- {
+ }, {
name: "wrong format registry quobyte",
vol: core.Volume{
Name: "quobyte",
@@ -5022,8 +4802,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "quobyte.registry",
}},
- },
- {
+ }, {
name: "wrong format multiple registries quobyte",
vol: core.Volume{
Name: "quobyte",
@@ -5039,8 +4818,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeInvalid,
field: "quobyte.registry",
}},
- },
- {
+ }, {
name: "empty volume quobyte",
vol: core.Volume{
Name: "quobyte",
@@ -5055,8 +4833,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "quobyte.volume",
}},
- },
- {
+ }, {
name: "empty tenant quobyte",
vol: core.Volume{
Name: "quobyte",
@@ -5068,8 +4845,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "too long tenant quobyte",
vol: core.Volume{
Name: "quobyte",
@@ -5098,8 +4874,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "AzureDisk empty disk name",
vol: core.Volume{
Name: "azure-disk",
@@ -5114,8 +4889,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "azureDisk.diskName",
}},
- },
- {
+ }, {
name: "AzureDisk empty disk uri",
vol: core.Volume{
Name: "azure-disk",
@@ -5144,8 +4918,7 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
- },
- {
+ }, {
name: "ScaleIO with empty name",
vol: core.Volume{
Name: "scaleio-volume",
@@ -5161,8 +4934,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "scaleIO.volumeName",
}},
- },
- {
+ }, {
name: "ScaleIO with empty gateway",
vol: core.Volume{
Name: "scaleio-volume",
@@ -5178,8 +4950,7 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeRequired,
field: "scaleIO.gateway",
}},
- },
- {
+ }, {
name: "ScaleIO with empty system",
vol: core.Volume{
Name: "scaleio-volume",
@@ -5203,23 +4974,20 @@ func TestValidateVolumes(t *testing.T) {
Name: "projected-volume",
VolumeSource: core.VolumeSource{
Projected: &core.ProjectedVolumeSource{
- Sources: []core.VolumeProjection{
- {
- Secret: &core.SecretProjection{
- LocalObjectReference: core.LocalObjectReference{
- Name: "foo",
- },
+ Sources: []core.VolumeProjection{{
+ Secret: &core.SecretProjection{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "foo",
},
},
- {
- Secret: &core.SecretProjection{
- LocalObjectReference: core.LocalObjectReference{
- Name: "foo",
- },
+ }, {
+ Secret: &core.SecretProjection{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "foo",
},
- DownwardAPI: &core.DownwardAPIProjection{},
},
- },
+ DownwardAPI: &core.DownwardAPIProjection{},
+ }},
},
},
},
@@ -5227,39 +4995,31 @@ func TestValidateVolumes(t *testing.T) {
etype: field.ErrorTypeForbidden,
field: "projected.sources[1]",
}},
- },
- {
+ }, {
name: "ProjectedVolumeSource more than one projection in a source",
vol: core.Volume{
Name: "projected-volume",
VolumeSource: core.VolumeSource{
Projected: &core.ProjectedVolumeSource{
- Sources: []core.VolumeProjection{
- {
- Secret: &core.SecretProjection{},
- },
- {
- Secret: &core.SecretProjection{},
- DownwardAPI: &core.DownwardAPIProjection{},
- },
- },
+ Sources: []core.VolumeProjection{{
+ Secret: &core.SecretProjection{},
+ }, {
+ Secret: &core.SecretProjection{},
+ DownwardAPI: &core.DownwardAPIProjection{},
+ }},
},
},
},
- errs: []verr{
- {
- etype: field.ErrorTypeRequired,
- field: "projected.sources[0].secret.name",
- },
- {
- etype: field.ErrorTypeRequired,
- field: "projected.sources[1].secret.name",
- },
- {
- etype: field.ErrorTypeForbidden,
- field: "projected.sources[1]",
- },
- },
+ errs: []verr{{
+ etype: field.ErrorTypeRequired,
+ field: "projected.sources[0].secret.name",
+ }, {
+ etype: field.ErrorTypeRequired,
+ field: "projected.sources[1].secret.name",
+ }, {
+ etype: field.ErrorTypeForbidden,
+ field: "projected.sources[1]",
+ }},
},
}
@@ -5317,91 +5077,79 @@ func TestValidateReadOnlyPersistentDisks(t *testing.T) {
oldVolume []core.Volume
gateValue bool
expectError bool
- }{
- {
- name: "gate on, read-only disk, nil old",
- gateValue: true,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- oldVolume: []core.Volume(nil),
- expectError: false,
- },
- {
- name: "gate off, read-only disk, nil old",
- gateValue: false,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- oldVolume: []core.Volume(nil),
- expectError: false,
- },
- {
- name: "gate on, read-write, nil old",
- gateValue: true,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
- oldVolume: []core.Volume(nil),
- expectError: false,
- },
- {
- name: "gate off, read-write, nil old",
- gateValue: false,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
- oldVolume: []core.Volume(nil),
- expectError: true,
- },
- {
- name: "gate on, new read-only and old read-write",
- gateValue: true,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
- expectError: false,
- },
- {
- name: "gate off, new read-only and old read-write",
- gateValue: false,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
- expectError: false,
- },
- {
- name: "gate on, new read-write and old read-write",
- gateValue: true,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
- expectError: false,
- },
- {
- name: "gate off, new read-write and old read-write",
- gateValue: false,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
- oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
- expectError: false,
- },
- {
- name: "gate on, new read-only and old read-only",
- gateValue: true,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- expectError: false,
- },
- {
- name: "gate off, new read-only and old read-only",
- gateValue: false,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- expectError: false,
- },
- {
- name: "gate on, new read-write and old read-only",
- gateValue: true,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
- oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- expectError: false,
- },
- {
- name: "gate off, new read-write and old read-only",
- gateValue: false,
- volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
- oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
- expectError: true,
- },
+ }{{
+ name: "gate on, read-only disk, nil old",
+ gateValue: true,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ oldVolume: []core.Volume(nil),
+ expectError: false,
+ }, {
+ name: "gate off, read-only disk, nil old",
+ gateValue: false,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ oldVolume: []core.Volume(nil),
+ expectError: false,
+ }, {
+ name: "gate on, read-write, nil old",
+ gateValue: true,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
+ oldVolume: []core.Volume(nil),
+ expectError: false,
+ }, {
+ name: "gate off, read-write, nil old",
+ gateValue: false,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
+ oldVolume: []core.Volume(nil),
+ expectError: true,
+ }, {
+ name: "gate on, new read-only and old read-write",
+ gateValue: true,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
+ expectError: false,
+ }, {
+ name: "gate off, new read-only and old read-write",
+ gateValue: false,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
+ expectError: false,
+ }, {
+ name: "gate on, new read-write and old read-write",
+ gateValue: true,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
+ expectError: false,
+ }, {
+ name: "gate off, new read-write and old read-write",
+ gateValue: false,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
+ oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
+ expectError: false,
+ }, {
+ name: "gate on, new read-only and old read-only",
+ gateValue: true,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ expectError: false,
+ }, {
+ name: "gate off, new read-only and old read-only",
+ gateValue: false,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ expectError: false,
+ }, {
+ name: "gate on, new read-write and old read-only",
+ gateValue: true,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
+ oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ expectError: false,
+ }, {
+ name: "gate off, new read-write and old read-only",
+ gateValue: false,
+ volumes: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: false}}}},
+ oldVolume: []core.Volume{{VolumeSource: core.VolumeSource{GCEPersistentDisk: &core.GCEPersistentDiskVolumeSource{ReadOnly: true}}}},
+ expectError: true,
+ },
}
for _, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {
@@ -5424,23 +5172,21 @@ func TestHugePagesIsolation(t *testing.T) {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
- },
- Limits: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -5450,25 +5196,23 @@ func TestHugePagesIsolation(t *testing.T) {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-shared", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
- core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
- },
- Limits: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
- core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
- },
+ Containers: []core.Container{{
+ Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
+ core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
+ core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -5479,25 +5223,23 @@ func TestHugePagesIsolation(t *testing.T) {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-multiple", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
- core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
- },
- Limits: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
- core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
- },
+ Containers: []core.Container{{
+ Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
+ core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
+ core.ResourceName("hugepages-1Gi"): resource.MustParse("2Gi"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -5507,19 +5249,17 @@ func TestHugePagesIsolation(t *testing.T) {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-requireCpuOrMemory", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
- },
- Limits: core.ResourceList{
- core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -5530,23 +5270,21 @@ func TestHugePagesIsolation(t *testing.T) {
pod: &core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "hugepages-shared", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
- },
- Limits: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
- },
+ Containers: []core.Container{{
+ Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("1Gi"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("2Gi"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -5860,25 +5598,23 @@ func TestValidatePorts(t *testing.T) {
}
func TestLocalStorageEnvWithFeatureGate(t *testing.T) {
- testCases := []core.EnvVar{
- {
- Name: "ephemeral-storage-limits",
- ValueFrom: &core.EnvVarSource{
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "limits.ephemeral-storage",
- },
+ testCases := []core.EnvVar{{
+ Name: "ephemeral-storage-limits",
+ ValueFrom: &core.EnvVarSource{
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "limits.ephemeral-storage",
},
},
- {
- Name: "ephemeral-storage-requests",
- ValueFrom: &core.EnvVarSource{
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "requests.ephemeral-storage",
- },
+ }, {
+ Name: "ephemeral-storage-requests",
+ ValueFrom: &core.EnvVarSource{
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "requests.ephemeral-storage",
},
},
+ },
}
for _, testCase := range testCases {
if errs := validateEnvVarValueFrom(testCase, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
@@ -5888,25 +5624,23 @@ func TestLocalStorageEnvWithFeatureGate(t *testing.T) {
}
func TestHugePagesEnv(t *testing.T) {
- testCases := []core.EnvVar{
- {
- Name: "hugepages-limits",
- ValueFrom: &core.EnvVarSource{
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "limits.hugepages-2Mi",
- },
+ testCases := []core.EnvVar{{
+ Name: "hugepages-limits",
+ ValueFrom: &core.EnvVarSource{
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "limits.hugepages-2Mi",
},
},
- {
- Name: "hugepages-requests",
- ValueFrom: &core.EnvVarSource{
- ResourceFieldRef: &core.ResourceFieldSelector{
- ContainerName: "test-container",
- Resource: "requests.hugepages-2Mi",
- },
+ }, {
+ Name: "hugepages-requests",
+ ValueFrom: &core.EnvVarSource{
+ ResourceFieldRef: &core.ResourceFieldSelector{
+ ContainerName: "test-container",
+ Resource: "requests.hugepages-2Mi",
},
},
+ },
}
// enable gate
for _, testCase := range testCases {
@@ -5927,8 +5661,7 @@ func TestValidateEnv(t *testing.T) {
{Name: "AbC_123", Value: "value"},
{Name: "abc", Value: ""},
{Name: "a.b.c", Value: "value"},
- {Name: "a-b-c", Value: "value"},
- {
+ {Name: "a-b-c", Value: "value"}, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -5936,8 +5669,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "metadata.annotations['key']",
},
},
- },
- {
+ }, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -5945,8 +5677,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "metadata.labels['key']",
},
},
- },
- {
+ }, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -5954,8 +5685,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "metadata.name",
},
},
- },
- {
+ }, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -5963,8 +5693,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "metadata.namespace",
},
},
- },
- {
+ }, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -5972,8 +5701,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "metadata.uid",
},
},
- },
- {
+ }, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -5981,8 +5709,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "spec.nodeName",
},
},
- },
- {
+ }, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -5990,8 +5717,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "spec.serviceAccountName",
},
},
- },
- {
+ }, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -5999,8 +5725,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "status.hostIP",
},
},
- },
- {
+ }, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -6008,8 +5733,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "status.podIP",
},
},
- },
- {
+ }, {
Name: "abc",
ValueFrom: &core.EnvVarSource{
FieldRef: &core.ObjectFieldSelector{
@@ -6017,8 +5741,7 @@ func TestValidateEnv(t *testing.T) {
FieldPath: "status.podIPs",
},
},
- },
- {
+ }, {
Name: "secret_value",
ValueFrom: &core.EnvVarSource{
SecretKeyRef: &core.SecretKeySelector{
@@ -6028,8 +5751,7 @@ func TestValidateEnv(t *testing.T) {
Key: "secret-key",
},
},
- },
- {
+ }, {
Name: "ENV_VAR_1",
ValueFrom: &core.EnvVarSource{
ConfigMapKeyRef: &core.ConfigMapKeySelector{
@@ -6049,260 +5771,239 @@ func TestValidateEnv(t *testing.T) {
name string
envs []core.EnvVar
expectedError string
- }{
- {
- name: "zero-length name",
- envs: []core.EnvVar{{Name: ""}},
- expectedError: "[0].name: Required value",
- },
- {
- name: "illegal character",
- envs: []core.EnvVar{{Name: "a!b"}},
- expectedError: `[0].name: Invalid value: "a!b": ` + envVarNameErrMsg,
- },
- {
- name: "dot only",
- envs: []core.EnvVar{{Name: "."}},
- expectedError: `[0].name: Invalid value: ".": must not be`,
- },
- {
- name: "double dots only",
- envs: []core.EnvVar{{Name: ".."}},
- expectedError: `[0].name: Invalid value: "..": must not be`,
- },
- {
- name: "leading double dots",
- envs: []core.EnvVar{{Name: "..abc"}},
- expectedError: `[0].name: Invalid value: "..abc": must not start with`,
- },
- {
- name: "value and valueFrom specified",
- envs: []core.EnvVar{{
- Name: "abc",
- Value: "foo",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.name",
- },
+ }{{
+ name: "zero-length name",
+ envs: []core.EnvVar{{Name: ""}},
+ expectedError: "[0].name: Required value",
+ }, {
+ name: "illegal character",
+ envs: []core.EnvVar{{Name: "a!b"}},
+ expectedError: `[0].name: Invalid value: "a!b": ` + envVarNameErrMsg,
+ }, {
+ name: "dot only",
+ envs: []core.EnvVar{{Name: "."}},
+ expectedError: `[0].name: Invalid value: ".": must not be`,
+ }, {
+ name: "double dots only",
+ envs: []core.EnvVar{{Name: ".."}},
+ expectedError: `[0].name: Invalid value: "..": must not be`,
+ }, {
+ name: "leading double dots",
+ envs: []core.EnvVar{{Name: "..abc"}},
+ expectedError: `[0].name: Invalid value: "..abc": must not start with`,
+ }, {
+ name: "value and valueFrom specified",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ Value: "foo",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.name",
},
- }},
- expectedError: "[0].valueFrom: Invalid value: \"\": may not be specified when `value` is not empty",
- },
- {
- name: "valueFrom without a source",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{},
- }},
- expectedError: "[0].valueFrom: Invalid value: \"\": must specify one of: `fieldRef`, `resourceFieldRef`, `configMapKeyRef` or `secretKeyRef`",
- },
- {
- name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.name",
- },
- SecretKeyRef: &core.SecretKeySelector{
- LocalObjectReference: core.LocalObjectReference{
- Name: "a-secret",
- },
- Key: "a-key",
- },
+ },
+ }},
+ expectedError: "[0].valueFrom: Invalid value: \"\": may not be specified when `value` is not empty",
+ }, {
+ name: "valueFrom without a source",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{},
+ }},
+ expectedError: "[0].valueFrom: Invalid value: \"\": must specify one of: `fieldRef`, `resourceFieldRef`, `configMapKeyRef` or `secretKeyRef`",
+ }, {
+ name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.name",
},
- }},
- expectedError: "[0].valueFrom: Invalid value: \"\": may not have more than one field specified at a time",
- },
- {
- name: "valueFrom.fieldRef and valueFrom.configMapKeyRef set",
- envs: []core.EnvVar{{
- Name: "some_var_name",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.name",
- },
- ConfigMapKeyRef: &core.ConfigMapKeySelector{
- LocalObjectReference: core.LocalObjectReference{
- Name: "some-config-map",
- },
- Key: "some-key",
+ SecretKeyRef: &core.SecretKeySelector{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "a-secret",
},
+ Key: "a-key",
},
- }},
- expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
- },
- {
- name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
- FieldPath: "metadata.name",
- },
- SecretKeyRef: &core.SecretKeySelector{
- LocalObjectReference: core.LocalObjectReference{
- Name: "a-secret",
- },
- Key: "a-key",
- },
- ConfigMapKeyRef: &core.ConfigMapKeySelector{
- LocalObjectReference: core.LocalObjectReference{
- Name: "some-config-map",
- },
- Key: "some-key",
- },
+ },
+ }},
+ expectedError: "[0].valueFrom: Invalid value: \"\": may not have more than one field specified at a time",
+ }, {
+ name: "valueFrom.fieldRef and valueFrom.configMapKeyRef set",
+ envs: []core.EnvVar{{
+ Name: "some_var_name",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.name",
},
- }},
- expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
- },
- {
- name: "valueFrom.secretKeyRef.name invalid",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- SecretKeyRef: &core.SecretKeySelector{
- LocalObjectReference: core.LocalObjectReference{
- Name: "$%^&*#",
- },
- Key: "a-key",
+ ConfigMapKeyRef: &core.ConfigMapKeySelector{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "some-config-map",
},
+ Key: "some-key",
},
- }},
- },
- {
- name: "valueFrom.configMapKeyRef.name invalid",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- ConfigMapKeyRef: &core.ConfigMapKeySelector{
- LocalObjectReference: core.LocalObjectReference{
- Name: "$%^&*#",
- },
- Key: "some-key",
+ },
+ }},
+ expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
+ }, {
+ name: "valueFrom.fieldRef and valueFrom.secretKeyRef specified",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
+ FieldPath: "metadata.name",
+ },
+ SecretKeyRef: &core.SecretKeySelector{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "a-secret",
},
+ Key: "a-key",
},
- }},
- },
- {
- name: "missing FieldPath on ObjectFieldSelector",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- APIVersion: "v1",
+ ConfigMapKeyRef: &core.ConfigMapKeySelector{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "some-config-map",
},
+ Key: "some-key",
},
- }},
- expectedError: `[0].valueFrom.fieldRef.fieldPath: Required value`,
- },
- {
- name: "missing APIVersion on ObjectFieldSelector",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- FieldPath: "metadata.name",
+ },
+ }},
+ expectedError: `[0].valueFrom: Invalid value: "": may not have more than one field specified at a time`,
+ }, {
+ name: "valueFrom.secretKeyRef.name invalid",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ SecretKeyRef: &core.SecretKeySelector{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "$%^&*#",
},
+ Key: "a-key",
},
- }},
- expectedError: `[0].valueFrom.fieldRef.apiVersion: Required value`,
- },
- {
- name: "invalid fieldPath",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- FieldPath: "metadata.whoops",
- APIVersion: "v1",
+ },
+ }},
+ }, {
+ name: "valueFrom.configMapKeyRef.name invalid",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ ConfigMapKeyRef: &core.ConfigMapKeySelector{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "$%^&*#",
},
+ Key: "some-key",
},
- }},
- expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.whoops": error converting fieldPath`,
- },
- {
- name: "metadata.name with subscript",
- envs: []core.EnvVar{{
- Name: "labels",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- FieldPath: "metadata.name['key']",
- APIVersion: "v1",
- },
+ },
+ }},
+ }, {
+ name: "missing FieldPath on ObjectFieldSelector",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ APIVersion: "v1",
},
- }},
- expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.name['key']": error converting fieldPath: field label does not support subscript`,
- },
- {
- name: "metadata.labels without subscript",
- envs: []core.EnvVar{{
- Name: "labels",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- FieldPath: "metadata.labels",
- APIVersion: "v1",
- },
+ },
+ }},
+ expectedError: `[0].valueFrom.fieldRef.fieldPath: Required value`,
+ }, {
+ name: "missing APIVersion on ObjectFieldSelector",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ FieldPath: "metadata.name",
},
- }},
- expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.labels": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP", "status.podIPs"`,
- },
- {
- name: "metadata.annotations without subscript",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- FieldPath: "metadata.annotations",
- APIVersion: "v1",
- },
+ },
+ }},
+ expectedError: `[0].valueFrom.fieldRef.apiVersion: Required value`,
+ }, {
+ name: "invalid fieldPath",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ FieldPath: "metadata.whoops",
+ APIVersion: "v1",
},
- }},
- expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.annotations": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP", "status.podIPs"`,
- },
- {
- name: "metadata.annotations with invalid key",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- FieldPath: "metadata.annotations['invalid~key']",
- APIVersion: "v1",
- },
+ },
+ }},
+ expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.whoops": error converting fieldPath`,
+ }, {
+ name: "metadata.name with subscript",
+ envs: []core.EnvVar{{
+ Name: "labels",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ FieldPath: "metadata.name['key']",
+ APIVersion: "v1",
},
- }},
- expectedError: `field[0].valueFrom.fieldRef: Invalid value: "invalid~key"`,
- },
- {
- name: "metadata.labels with invalid key",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- FieldPath: "metadata.labels['Www.k8s.io/test']",
- APIVersion: "v1",
- },
+ },
+ }},
+ expectedError: `[0].valueFrom.fieldRef.fieldPath: Invalid value: "metadata.name['key']": error converting fieldPath: field label does not support subscript`,
+ }, {
+ name: "metadata.labels without subscript",
+ envs: []core.EnvVar{{
+ Name: "labels",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ FieldPath: "metadata.labels",
+ APIVersion: "v1",
},
- }},
- expectedError: `field[0].valueFrom.fieldRef: Invalid value: "Www.k8s.io/test"`,
- },
- {
- name: "unsupported fieldPath",
- envs: []core.EnvVar{{
- Name: "abc",
- ValueFrom: &core.EnvVarSource{
- FieldRef: &core.ObjectFieldSelector{
- FieldPath: "status.phase",
- APIVersion: "v1",
- },
+ },
+ }},
+ expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.labels": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP", "status.podIPs"`,
+ }, {
+ name: "metadata.annotations without subscript",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ FieldPath: "metadata.annotations",
+ APIVersion: "v1",
},
- }},
- expectedError: `valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP", "status.podIPs"`,
- },
+ },
+ }},
+ expectedError: `[0].valueFrom.fieldRef.fieldPath: Unsupported value: "metadata.annotations": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP", "status.podIPs"`,
+ }, {
+ name: "metadata.annotations with invalid key",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ FieldPath: "metadata.annotations['invalid~key']",
+ APIVersion: "v1",
+ },
+ },
+ }},
+ expectedError: `field[0].valueFrom.fieldRef: Invalid value: "invalid~key"`,
+ }, {
+ name: "metadata.labels with invalid key",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ FieldPath: "metadata.labels['Www.k8s.io/test']",
+ APIVersion: "v1",
+ },
+ },
+ }},
+ expectedError: `field[0].valueFrom.fieldRef: Invalid value: "Www.k8s.io/test"`,
+ }, {
+ name: "unsupported fieldPath",
+ envs: []core.EnvVar{{
+ Name: "abc",
+ ValueFrom: &core.EnvVarSource{
+ FieldRef: &core.ObjectFieldSelector{
+ FieldPath: "status.phase",
+ APIVersion: "v1",
+ },
+ },
+ }},
+ expectedError: `valueFrom.fieldRef.fieldPath: Unsupported value: "status.phase": supported values: "metadata.name", "metadata.namespace", "metadata.uid", "spec.nodeName", "spec.serviceAccountName", "status.hostIP", "status.podIP", "status.podIPs"`,
+ },
}
for _, tc := range errorCases {
if errs := ValidateEnv(tc.envs, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
@@ -6319,41 +6020,35 @@ func TestValidateEnv(t *testing.T) {
}
func TestValidateEnvFrom(t *testing.T) {
- successCase := []core.EnvFromSource{
- {
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"},
- },
+ successCase := []core.EnvFromSource{{
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"},
},
- {
- Prefix: "pre_",
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"},
- },
+ }, {
+ Prefix: "pre_",
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"},
},
- {
- Prefix: "a.b",
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"},
- },
+ }, {
+ Prefix: "a.b",
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"},
},
- {
- SecretRef: &core.SecretEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"},
- },
+ }, {
+ SecretRef: &core.SecretEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"},
},
- {
- Prefix: "pre_",
- SecretRef: &core.SecretEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"},
- },
+ }, {
+ Prefix: "pre_",
+ SecretRef: &core.SecretEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"},
},
- {
- Prefix: "a.b",
- SecretRef: &core.SecretEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"},
- },
+ }, {
+ Prefix: "a.b",
+ SecretRef: &core.SecretEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"},
},
+ },
}
if errs := ValidateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
@@ -6363,108 +6058,80 @@ func TestValidateEnvFrom(t *testing.T) {
name string
envs []core.EnvFromSource
expectedError string
- }{
- {
- name: "zero-length name",
- envs: []core.EnvFromSource{
- {
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: ""}},
- },
- },
- expectedError: "field[0].configMapRef.name: Required value",
- },
- {
- name: "invalid name",
- envs: []core.EnvFromSource{
- {
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "$"}},
- },
- },
- expectedError: "field[0].configMapRef.name: Invalid value",
- },
- {
- name: "invalid prefix",
- envs: []core.EnvFromSource{
- {
- Prefix: "a!b",
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
- },
- },
- expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
- },
- {
- name: "zero-length name",
- envs: []core.EnvFromSource{
- {
- SecretRef: &core.SecretEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: ""}},
- },
- },
- expectedError: "field[0].secretRef.name: Required value",
- },
- {
- name: "invalid name",
- envs: []core.EnvFromSource{
- {
- SecretRef: &core.SecretEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "&"}},
- },
- },
- expectedError: "field[0].secretRef.name: Invalid value",
- },
- {
- name: "invalid prefix",
- envs: []core.EnvFromSource{
- {
- Prefix: "a!b",
- SecretRef: &core.SecretEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
- },
- },
- expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
- },
- {
- name: "no refs",
- envs: []core.EnvFromSource{
- {},
- },
- expectedError: "field: Invalid value: \"\": must specify one of: `configMapRef` or `secretRef`",
- },
- {
- name: "multiple refs",
- envs: []core.EnvFromSource{
- {
- SecretRef: &core.SecretEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
- },
- },
- expectedError: "field: Invalid value: \"\": may not have more than one field specified at a time",
- },
- {
- name: "invalid secret ref name",
- envs: []core.EnvFromSource{
- {
- SecretRef: &core.SecretEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
- },
- },
- expectedError: "field[0].secretRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
- },
- {
- name: "invalid config ref name",
- envs: []core.EnvFromSource{
- {
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
- },
- },
- expectedError: "field[0].configMapRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
+ }{{
+ name: "zero-length name",
+ envs: []core.EnvFromSource{{
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: ""}},
+ }},
+ expectedError: "field[0].configMapRef.name: Required value",
+ }, {
+ name: "invalid name",
+ envs: []core.EnvFromSource{{
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "$"}},
+ }},
+ expectedError: "field[0].configMapRef.name: Invalid value",
+ }, {
+ name: "invalid prefix",
+ envs: []core.EnvFromSource{{
+ Prefix: "a!b",
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
+ }},
+ expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
+ }, {
+ name: "zero-length name",
+ envs: []core.EnvFromSource{{
+ SecretRef: &core.SecretEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: ""}},
+ }},
+ expectedError: "field[0].secretRef.name: Required value",
+ }, {
+ name: "invalid name",
+ envs: []core.EnvFromSource{{
+ SecretRef: &core.SecretEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "&"}},
+ }},
+ expectedError: "field[0].secretRef.name: Invalid value",
+ }, {
+ name: "invalid prefix",
+ envs: []core.EnvFromSource{{
+ Prefix: "a!b",
+ SecretRef: &core.SecretEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
+ }},
+ expectedError: `field[0].prefix: Invalid value: "a!b": ` + envVarNameErrMsg,
+ }, {
+ name: "no refs",
+ envs: []core.EnvFromSource{
+ {},
},
+ expectedError: "field: Invalid value: \"\": must specify one of: `configMapRef` or `secretRef`",
+ }, {
+ name: "multiple refs",
+ envs: []core.EnvFromSource{{
+ SecretRef: &core.SecretEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "abc"}},
+ }},
+ expectedError: "field: Invalid value: \"\": may not have more than one field specified at a time",
+ }, {
+ name: "invalid secret ref name",
+ envs: []core.EnvFromSource{{
+ SecretRef: &core.SecretEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
+ }},
+ expectedError: "field[0].secretRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
+ }, {
+ name: "invalid config ref name",
+ envs: []core.EnvFromSource{{
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "$%^&*#"}},
+ }},
+ expectedError: "field[0].configMapRef.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
+ },
}
for _, tc := range errorCases {
if errs := ValidateEnvFrom(tc.envs, field.NewPath("field")); len(errs) == 0 {
@@ -6580,43 +6247,35 @@ func TestValidateSubpathMutuallyExclusive(t *testing.T) {
expectError bool
}{
"subpath and subpathexpr not specified": {
- []core.VolumeMount{
- {
- Name: "abc-123",
- MountPath: "/bab",
- },
- },
+ []core.VolumeMount{{
+ Name: "abc-123",
+ MountPath: "/bab",
+ }},
false,
},
"subpath expr specified": {
- []core.VolumeMount{
- {
- Name: "abc-123",
- MountPath: "/bab",
- SubPathExpr: "$(POD_NAME)",
- },
- },
+ []core.VolumeMount{{
+ Name: "abc-123",
+ MountPath: "/bab",
+ SubPathExpr: "$(POD_NAME)",
+ }},
false,
},
"subpath specified": {
- []core.VolumeMount{
- {
- Name: "abc-123",
- MountPath: "/bab",
- SubPath: "baz",
- },
- },
+ []core.VolumeMount{{
+ Name: "abc-123",
+ MountPath: "/bab",
+ SubPath: "baz",
+ }},
false,
},
"subpath and subpathexpr specified": {
- []core.VolumeMount{
- {
- Name: "abc-123",
- MountPath: "/bab",
- SubPath: "baz",
- SubPathExpr: "$(POD_NAME)",
- },
- },
+ []core.VolumeMount{{
+ Name: "abc-123",
+ MountPath: "/bab",
+ SubPath: "baz",
+ SubPathExpr: "$(POD_NAME)",
+ }},
true,
},
}
@@ -6661,22 +6320,18 @@ func TestValidateDisabledSubpathExpr(t *testing.T) {
expectError bool
}{
"subpath expr not specified": {
- []core.VolumeMount{
- {
- Name: "abc-123",
- MountPath: "/bab",
- },
- },
+ []core.VolumeMount{{
+ Name: "abc-123",
+ MountPath: "/bab",
+ }},
false,
},
"subpath expr specified": {
- []core.VolumeMount{
- {
- Name: "abc-123",
- MountPath: "/bab",
- SubPathExpr: "$(POD_NAME)",
- },
- },
+ []core.VolumeMount{{
+ Name: "abc-123",
+ MountPath: "/bab",
+ SubPathExpr: "$(POD_NAME)",
+ }},
false,
},
}
@@ -6718,79 +6373,67 @@ func TestValidateMountPropagation(t *testing.T) {
mount core.VolumeMount
container *core.Container
expectError bool
- }{
- {
- // implicitly non-privileged container + no propagation
- core.VolumeMount{Name: "foo", MountPath: "/foo"},
- defaultContainer,
- false,
- },
- {
- // implicitly non-privileged container + HostToContainer
- core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
- defaultContainer,
- false,
- },
- {
- // non-privileged container + None
- core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationNone},
- defaultContainer,
- false,
- },
- {
- // error: implicitly non-privileged container + Bidirectional
- core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
- defaultContainer,
- true,
- },
- {
- // explicitly non-privileged container + no propagation
- core.VolumeMount{Name: "foo", MountPath: "/foo"},
- nonPrivilegedContainer,
- false,
- },
- {
- // explicitly non-privileged container + HostToContainer
- core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
- nonPrivilegedContainer,
- false,
- },
- {
- // explicitly non-privileged container + HostToContainer
- core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
- nonPrivilegedContainer,
- true,
- },
- {
- // privileged container + no propagation
- core.VolumeMount{Name: "foo", MountPath: "/foo"},
- privilegedContainer,
- false,
- },
- {
- // privileged container + HostToContainer
- core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
- privilegedContainer,
- false,
- },
- {
- // privileged container + Bidirectional
- core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
- privilegedContainer,
- false,
- },
- {
- // error: privileged container + invalid mount propagation
- core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationInvalid},
- privilegedContainer,
- true,
- },
- {
- // no container + Bidirectional
- core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
- nil,
- false,
- },
+ }{{
+ // implicitly non-privileged container + no propagation
+ core.VolumeMount{Name: "foo", MountPath: "/foo"},
+ defaultContainer,
+ false,
+ }, {
+ // implicitly non-privileged container + HostToContainer
+ core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
+ defaultContainer,
+ false,
+ }, {
+ // non-privileged container + None
+ core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationNone},
+ defaultContainer,
+ false,
+ }, {
+ // error: implicitly non-privileged container + Bidirectional
+ core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
+ defaultContainer,
+ true,
+ }, {
+ // explicitly non-privileged container + no propagation
+ core.VolumeMount{Name: "foo", MountPath: "/foo"},
+ nonPrivilegedContainer,
+ false,
+ }, {
+ // explicitly non-privileged container + HostToContainer
+ core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
+ nonPrivilegedContainer,
+ false,
+ }, {
+ // explicitly non-privileged container + HostToContainer
+ core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
+ nonPrivilegedContainer,
+ true,
+ }, {
+ // privileged container + no propagation
+ core.VolumeMount{Name: "foo", MountPath: "/foo"},
+ privilegedContainer,
+ false,
+ }, {
+ // privileged container + HostToContainer
+ core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationHostToContainer},
+ privilegedContainer,
+ false,
+ }, {
+ // privileged container + Bidirectional
+ core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
+ privilegedContainer,
+ false,
+ }, {
+ // error: privileged container + invalid mount propagation
+ core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationInvalid},
+ privilegedContainer,
+ true,
+ }, {
+ // no container + Bidirectional
+ core.VolumeMount{Name: "foo", MountPath: "/foo", MountPropagation: &propagationBidirectional},
+ nil,
+ false,
+ },
}
volumes := []core.Volume{
@@ -6922,103 +6565,93 @@ func Test_validateProbe(t *testing.T) {
name string
args args
want field.ErrorList
- }{
- {
- args: args{
- probe: &core.Probe{},
- fldPath: fldPath,
- },
- want: field.ErrorList{field.Required(fldPath, "must specify a handler type")},
+ }{{
+ args: args{
+ probe: &core.Probe{},
+ fldPath: fldPath,
},
- {
- args: args{
- probe: &core.Probe{
- ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
- },
- fldPath: fldPath,
+ want: field.ErrorList{field.Required(fldPath, "must specify a handler type")},
+ }, {
+ args: args{
+ probe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
},
- want: field.ErrorList{},
+ fldPath: fldPath,
},
- {
- args: args{
- probe: &core.Probe{
- ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
- InitialDelaySeconds: -1,
- },
- fldPath: fldPath,
+ want: field.ErrorList{},
+ }, {
+ args: args{
+ probe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
+ InitialDelaySeconds: -1,
},
- want: field.ErrorList{field.Invalid(fldPath.Child("initialDelaySeconds"), -1, "must be greater than or equal to 0")},
+ fldPath: fldPath,
},
- {
- args: args{
- probe: &core.Probe{
- ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
- TimeoutSeconds: -1,
- },
- fldPath: fldPath,
+ want: field.ErrorList{field.Invalid(fldPath.Child("initialDelaySeconds"), -1, "must be greater than or equal to 0")},
+ }, {
+ args: args{
+ probe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
+ TimeoutSeconds: -1,
},
- want: field.ErrorList{field.Invalid(fldPath.Child("timeoutSeconds"), -1, "must be greater than or equal to 0")},
+ fldPath: fldPath,
},
- {
- args: args{
- probe: &core.Probe{
- ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
- PeriodSeconds: -1,
- },
- fldPath: fldPath,
+ want: field.ErrorList{field.Invalid(fldPath.Child("timeoutSeconds"), -1, "must be greater than or equal to 0")},
+ }, {
+ args: args{
+ probe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
+ PeriodSeconds: -1,
},
- want: field.ErrorList{field.Invalid(fldPath.Child("periodSeconds"), -1, "must be greater than or equal to 0")},
+ fldPath: fldPath,
},
- {
- args: args{
- probe: &core.Probe{
- ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
- SuccessThreshold: -1,
- },
- fldPath: fldPath,
+ want: field.ErrorList{field.Invalid(fldPath.Child("periodSeconds"), -1, "must be greater than or equal to 0")},
+ }, {
+ args: args{
+ probe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
+ SuccessThreshold: -1,
},
- want: field.ErrorList{field.Invalid(fldPath.Child("successThreshold"), -1, "must be greater than or equal to 0")},
+ fldPath: fldPath,
},
- {
- args: args{
- probe: &core.Probe{
- ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
- FailureThreshold: -1,
- },
- fldPath: fldPath,
+ want: field.ErrorList{field.Invalid(fldPath.Child("successThreshold"), -1, "must be greater than or equal to 0")},
+ }, {
+ args: args{
+ probe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
+ FailureThreshold: -1,
},
- want: field.ErrorList{field.Invalid(fldPath.Child("failureThreshold"), -1, "must be greater than or equal to 0")},
+ fldPath: fldPath,
},
- {
- args: args{
- probe: &core.Probe{
- ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
- TerminationGracePeriodSeconds: utilpointer.Int64(-1),
- },
- fldPath: fldPath,
+ want: field.ErrorList{field.Invalid(fldPath.Child("failureThreshold"), -1, "must be greater than or equal to 0")},
+ }, {
+ args: args{
+ probe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
+ TerminationGracePeriodSeconds: utilpointer.Int64(-1),
},
- want: field.ErrorList{field.Invalid(fldPath.Child("terminationGracePeriodSeconds"), -1, "must be greater than 0")},
+ fldPath: fldPath,
},
- {
- args: args{
- probe: &core.Probe{
- ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
- TerminationGracePeriodSeconds: utilpointer.Int64(0),
- },
- fldPath: fldPath,
+ want: field.ErrorList{field.Invalid(fldPath.Child("terminationGracePeriodSeconds"), -1, "must be greater than 0")},
+ }, {
+ args: args{
+ probe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
+ TerminationGracePeriodSeconds: utilpointer.Int64(0),
},
- want: field.ErrorList{field.Invalid(fldPath.Child("terminationGracePeriodSeconds"), 0, "must be greater than 0")},
+ fldPath: fldPath,
},
- {
- args: args{
- probe: &core.Probe{
- ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
- TerminationGracePeriodSeconds: utilpointer.Int64(1),
- },
- fldPath: fldPath,
+ want: field.ErrorList{field.Invalid(fldPath.Child("terminationGracePeriodSeconds"), 0, "must be greater than 0")},
+ }, {
+ args: args{
+ probe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{Exec: &core.ExecAction{Command: []string{"echo"}}},
+ TerminationGracePeriodSeconds: utilpointer.Int64(1),
},
- want: field.ErrorList{},
+ fldPath: fldPath,
},
+ want: field.ErrorList{},
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -7244,52 +6877,46 @@ func TestValidateEphemeralContainers(t *testing.T) {
{EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
{EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug2", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
},
- "Single Container with Target": {
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- TargetContainerName: "ctr",
- },
- },
- "All allowed fields": {
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
+ "Single Container with Target": {{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
+ TargetContainerName: "ctr",
+ }},
+ "All allowed fields": {{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- Command: []string{"bash"},
- Args: []string{"bash"},
- WorkingDir: "/",
- EnvFrom: []core.EnvFromSource{
- {
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{Name: "dummy"},
- Optional: &[]bool{true}[0],
- },
- },
+ Name: "debug",
+ Image: "image",
+ Command: []string{"bash"},
+ Args: []string{"bash"},
+ WorkingDir: "/",
+ EnvFrom: []core.EnvFromSource{{
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "dummy"},
+ Optional: &[]bool{true}[0],
},
- Env: []core.EnvVar{
- {Name: "TEST", Value: "TRUE"},
- },
- VolumeMounts: []core.VolumeMount{
- {Name: "vol", MountPath: "/vol"},
- },
- VolumeDevices: []core.VolumeDevice{
- {Name: "blk", DevicePath: "/dev/block"},
- },
- TerminationMessagePath: "/dev/termination-log",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "IfNotPresent",
- SecurityContext: &core.SecurityContext{
- Capabilities: &core.Capabilities{
- Add: []core.Capability{"SYS_ADMIN"},
- },
- },
- Stdin: true,
- StdinOnce: true,
- TTY: true,
+ }},
+ Env: []core.EnvVar{
+ {Name: "TEST", Value: "TRUE"},
},
+ VolumeMounts: []core.VolumeMount{
+ {Name: "vol", MountPath: "/vol"},
+ },
+ VolumeDevices: []core.VolumeDevice{
+ {Name: "blk", DevicePath: "/dev/block"},
+ },
+ TerminationMessagePath: "/dev/termination-log",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "IfNotPresent",
+ SecurityContext: &core.SecurityContext{
+ Capabilities: &core.Capabilities{
+ Add: []core.Capability{"SYS_ADMIN"},
+ },
+ },
+ Stdin: true,
+ StdinOnce: true,
+ TTY: true,
},
- },
+ }},
} {
if errs := validateEphemeralContainers(ephemeralContainers, containers, initContainers, vols, nil, field.NewPath("ephemeralContainers"), PodValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success for '%s' but got errors: %v", title, errs)
@@ -7301,292 +6928,248 @@ func TestValidateEphemeralContainers(t *testing.T) {
title, line string
ephemeralContainers []core.EphemeralContainer
expectedErrors field.ErrorList
- }{
- {
- "Name Collision with Container.Containers",
- line(),
- []core.EphemeralContainer{
- {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- },
- field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "ephemeralContainers[0].name"}},
+ }{{
+ "Name Collision with Container.Containers",
+ line(),
+ []core.EphemeralContainer{
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
},
- {
- "Name Collision with Container.InitContainers",
- line(),
- []core.EphemeralContainer{
- {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "ictr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- },
- field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "ephemeralContainers[0].name"}},
+ field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "ephemeralContainers[0].name"}},
+ }, {
+ "Name Collision with Container.InitContainers",
+ line(),
+ []core.EphemeralContainer{
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "ictr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
},
- {
- "Name Collision with EphemeralContainers",
- line(),
- []core.EphemeralContainer{
- {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- },
- field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "ephemeralContainers[1].name"}},
+ field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "ephemeralContainers[0].name"}},
+ }, {
+ "Name Collision with EphemeralContainers",
+ line(),
+ []core.EphemeralContainer{
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug1", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
},
- {
- "empty Container",
- line(),
- []core.EphemeralContainer{
- {EphemeralContainerCommon: core.EphemeralContainerCommon{}},
- },
- field.ErrorList{
- {Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].name"},
- {Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].image"},
- {Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].terminationMessagePolicy"},
- {Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].imagePullPolicy"},
- },
+ field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "ephemeralContainers[1].name"}},
+ }, {
+ "empty Container",
+ line(),
+ []core.EphemeralContainer{
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{}},
},
- {
- "empty Container Name",
- line(),
- []core.EphemeralContainer{
- {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- },
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].name"}},
+ field.ErrorList{
+ {Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].name"},
+ {Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].image"},
+ {Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].terminationMessagePolicy"},
+ {Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].imagePullPolicy"},
},
- {
- "whitespace padded image name",
- line(),
- []core.EphemeralContainer{
- {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: " image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "ephemeralContainers[0].image"}},
+ }, {
+ "empty Container Name",
+ line(),
+ []core.EphemeralContainer{
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
},
- {
- "invalid image pull policy",
- line(),
- []core.EphemeralContainer{
- {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "PullThreeTimes", TerminationMessagePolicy: "File"}},
- },
- field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "ephemeralContainers[0].imagePullPolicy"}},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "ephemeralContainers[0].name"}},
+ }, {
+ "whitespace padded image name",
+ line(),
+ []core.EphemeralContainer{
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: " image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
},
- {
- "TargetContainerName doesn't exist",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- TargetContainerName: "bogus",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeNotFound, Field: "ephemeralContainers[0].targetContainerName"}},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "ephemeralContainers[0].image"}},
+ }, {
+ "invalid image pull policy",
+ line(),
+ []core.EphemeralContainer{
+ {EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "PullThreeTimes", TerminationMessagePolicy: "File"}},
},
- {
- "Targets an ephemeral container",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- },
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debugception", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- TargetContainerName: "debug",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeNotFound, Field: "ephemeralContainers[1].targetContainerName"}},
- },
- {
- "Container uses disallowed field: Lifecycle",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{
- Exec: &core.ExecAction{Command: []string{"ls", "-l"}},
- },
- },
+ field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "ephemeralContainers[0].imagePullPolicy"}},
+ }, {
+ "TargetContainerName doesn't exist",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
+ TargetContainerName: "bogus",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeNotFound, Field: "ephemeralContainers[0].targetContainerName"}},
+ }, {
+ "Targets an ephemeral container",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debug", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "debugception", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
+ TargetContainerName: "debug",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeNotFound, Field: "ephemeralContainers[1].targetContainerName"}},
+ }, {
+ "Container uses disallowed field: Lifecycle",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{
+ Exec: &core.ExecAction{Command: []string{"ls", "-l"}},
},
},
},
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].lifecycle"}},
- },
- {
- "Container uses disallowed field: LivenessProbe",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- LivenessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
- },
- SuccessThreshold: 1,
- },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].lifecycle"}},
+ }, {
+ "Container uses disallowed field: LivenessProbe",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ LivenessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
+ },
+ SuccessThreshold: 1,
+ },
+ },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].livenessProbe"}},
+ }, {
+ "Container uses disallowed field: Ports",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ Ports: []core.ContainerPort{
+ {Protocol: "TCP", ContainerPort: 80},
+ },
+ },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].ports"}},
+ }, {
+ "Container uses disallowed field: ReadinessProbe",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ ReadinessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
},
},
},
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].livenessProbe"}},
- },
- {
- "Container uses disallowed field: Ports",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- Ports: []core.ContainerPort{
- {Protocol: "TCP", ContainerPort: 80},
- },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].readinessProbe"}},
+ }, {
+ "Container uses disallowed field: StartupProbe",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ StartupProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
+ },
+ SuccessThreshold: 1,
+ },
+ },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].startupProbe"}},
+ }, {
+ "Container uses disallowed field: Resources",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ Resources: core.ResourceRequirements{
+ Limits: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
},
},
},
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].ports"}},
- },
- {
- "Container uses disallowed field: ReadinessProbe",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- ReadinessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
- },
- },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].resources"}},
+ }, {
+ "Container uses disallowed field: VolumeMount.SubPath",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ VolumeMounts: []core.VolumeMount{
+ {Name: "vol", MountPath: "/vol"},
+ {Name: "vol", MountPath: "/volsub", SubPath: "foo"},
+ },
+ },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].volumeMounts[1].subPath"}},
+ }, {
+ "Container uses disallowed field: VolumeMount.SubPathExpr",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ VolumeMounts: []core.VolumeMount{
+ {Name: "vol", MountPath: "/vol"},
+ {Name: "vol", MountPath: "/volsub", SubPathExpr: "$(POD_NAME)"},
+ },
+ },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].volumeMounts[1].subPathExpr"}},
+ }, {
+ "Disallowed field with other errors should only return a single Forbidden",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{
+ Exec: &core.ExecAction{Command: []string{}},
},
},
},
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].readinessProbe"}},
- },
- {
- "Container uses disallowed field: StartupProbe",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- StartupProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
- },
- SuccessThreshold: 1,
- },
- },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].lifecycle"}},
+ }, {
+ "Container uses disallowed field: ResizePolicy",
+ line(),
+ []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "resources-resize-policy",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ ResizePolicy: []core.ContainerResizePolicy{
+ {ResourceName: "cpu", RestartPolicy: "NotRequired"},
},
},
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].startupProbe"}},
- },
- {
- "Container uses disallowed field: Resources",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- Resources: core.ResourceRequirements{
- Limits: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- },
- },
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].resources"}},
- },
- {
- "Container uses disallowed field: VolumeMount.SubPath",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- VolumeMounts: []core.VolumeMount{
- {Name: "vol", MountPath: "/vol"},
- {Name: "vol", MountPath: "/volsub", SubPath: "foo"},
- },
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].volumeMounts[1].subPath"}},
- },
- {
- "Container uses disallowed field: VolumeMount.SubPathExpr",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- VolumeMounts: []core.VolumeMount{
- {Name: "vol", MountPath: "/vol"},
- {Name: "vol", MountPath: "/volsub", SubPathExpr: "$(POD_NAME)"},
- },
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].volumeMounts[1].subPathExpr"}},
- },
- {
- "Disallowed field with other errors should only return a single Forbidden",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{
- Exec: &core.ExecAction{Command: []string{}},
- },
- },
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].lifecycle"}},
- },
- {
- "Container uses disallowed field: ResizePolicy",
- line(),
- []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "resources-resize-policy",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- ResizePolicy: []core.ContainerResizePolicy{
- {ResourceName: "cpu", RestartPolicy: "NotRequired"},
- },
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].resizePolicy"}},
- },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "ephemeralContainers[0].resizePolicy"}},
+ },
}
for _, tc := range tcs {
@@ -7702,8 +7285,7 @@ func TestValidateContainers(t *testing.T) {
{Name: "def", Image: " ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
{Name: "ghi", Image: " some ", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
{Name: "123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- {Name: "abc-123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- {
+ {Name: "abc-123", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}, {
Name: "life-123",
Image: "image",
Lifecycle: &core.Lifecycle{
@@ -7713,8 +7295,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "resources-test",
Image: "image",
Resources: core.ResourceRequirements{
@@ -7726,8 +7307,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "resources-test-with-request-and-limit",
Image: "image",
Resources: core.ResourceRequirements{
@@ -7742,8 +7322,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "resources-request-limit-simple",
Image: "image",
Resources: core.ResourceRequirements{
@@ -7756,8 +7335,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "resources-request-limit-edge",
Image: "image",
Resources: core.ResourceRequirements{
@@ -7774,8 +7352,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "resources-request-limit-partials",
Image: "image",
Resources: core.ResourceRequirements{
@@ -7790,8 +7367,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "resources-request",
Image: "image",
Resources: core.ResourceRequirements{
@@ -7802,8 +7378,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "resources-resize-policy",
Image: "image",
ResizePolicy: []core.ContainerResizePolicy{
@@ -7812,8 +7387,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "same-host-port-different-protocol",
Image: "image",
Ports: []core.ContainerPort{
@@ -7822,36 +7396,30 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "fallback-to-logs-termination-message",
Image: "image",
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "FallbackToLogsOnError",
- },
- {
+ }, {
Name: "file-termination-message",
Image: "image",
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "env-from-source",
Image: "image",
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- EnvFrom: []core.EnvFromSource{
- {
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{
- Name: "test",
- },
+ EnvFrom: []core.EnvFromSource{{
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "test",
},
},
- },
+ }},
},
- {Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", SecurityContext: fakeValidSecurityContext(true)},
- {
+ {Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", SecurityContext: fakeValidSecurityContext(true)}, {
Name: "live-123",
Image: "image",
LivenessProbe: &core.Probe{
@@ -7864,8 +7432,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "startup-123",
Image: "image",
StartupProbe: &core.Probe{
@@ -7878,8 +7445,7 @@ func TestValidateContainers(t *testing.T) {
},
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
- },
- {
+ }, {
Name: "resize-policy-cpu",
Image: "image",
ImagePullPolicy: "IfNotPresent",
@@ -7887,8 +7453,7 @@ func TestValidateContainers(t *testing.T) {
ResizePolicy: []core.ContainerResizePolicy{
{ResourceName: "cpu", RestartPolicy: "NotRequired"},
},
- },
- {
+ }, {
Name: "resize-policy-mem",
Image: "image",
ImagePullPolicy: "IfNotPresent",
@@ -7896,8 +7461,7 @@ func TestValidateContainers(t *testing.T) {
ResizePolicy: []core.ContainerResizePolicy{
{ResourceName: "memory", RestartPolicy: "RestartContainer"},
},
- },
- {
+ }, {
Name: "resize-policy-cpu-and-mem",
Image: "image",
ImagePullPolicy: "IfNotPresent",
@@ -7919,648 +7483,550 @@ func TestValidateContainers(t *testing.T) {
title, line string
containers []core.Container
expectedErrors field.ErrorList
- }{
- {
- "zero-length name",
- line(),
- []core.Container{{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].name"}},
+ }{{
+ "zero-length name",
+ line(),
+ []core.Container{{Name: "", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].name"}},
+ }, {
+ "zero-length-image",
+ line(),
+ []core.Container{{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].image"}},
+ }, {
+ "name > 63 characters",
+ line(),
+ []core.Container{{Name: strings.Repeat("a", 64), Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].name"}},
+ }, {
+ "name not a DNS label",
+ line(),
+ []core.Container{{Name: "a.b.c", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].name"}},
+ }, {
+ "name not unique",
+ line(),
+ []core.Container{
+ {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
+ {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
},
- {
- "zero-length-image",
- line(),
- []core.Container{{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].image"}},
+ field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "containers[1].name"}},
+ }, {
+ "zero-length image",
+ line(),
+ []core.Container{{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].image"}},
+ }, {
+ "host port not unique",
+ line(),
+ []core.Container{
+ {Name: "abc", Image: "image", Ports: []core.ContainerPort{{ContainerPort: 80, HostPort: 80, Protocol: "TCP"}},
+ ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
+ {Name: "def", Image: "image", Ports: []core.ContainerPort{{ContainerPort: 81, HostPort: 80, Protocol: "TCP"}},
+ ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
},
- {
- "name > 63 characters",
- line(),
- []core.Container{{Name: strings.Repeat("a", 64), Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].name"}},
+ field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "containers[1].ports[0].hostPort"}},
+ }, {
+ "invalid env var name",
+ line(),
+ []core.Container{
+ {Name: "abc", Image: "image", Env: []core.EnvVar{{Name: "ev!1"}}, ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
},
- {
- "name not a DNS label",
- line(),
- []core.Container{{Name: "a.b.c", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].name"}},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].env[0].name"}},
+ }, {
+ "unknown volume name",
+ line(),
+ []core.Container{
+ {Name: "abc", Image: "image", VolumeMounts: []core.VolumeMount{{Name: "anything", MountPath: "/foo"}},
+ ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
},
- {
- "name not unique",
- line(),
- []core.Container{
- {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- {Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- },
- field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "containers[1].name"}},
- },
- {
- "zero-length image",
- line(),
- []core.Container{{Name: "abc", Image: "", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].image"}},
- },
- {
- "host port not unique",
- line(),
- []core.Container{
- {Name: "abc", Image: "image", Ports: []core.ContainerPort{{ContainerPort: 80, HostPort: 80, Protocol: "TCP"}},
- ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- {Name: "def", Image: "image", Ports: []core.ContainerPort{{ContainerPort: 81, HostPort: 80, Protocol: "TCP"}},
- ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- },
- field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "containers[1].ports[0].hostPort"}},
- },
- {
- "invalid env var name",
- line(),
- []core.Container{
- {Name: "abc", Image: "image", Env: []core.EnvVar{{Name: "ev!1"}}, ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].env[0].name"}},
- },
- {
- "unknown volume name",
- line(),
- []core.Container{
- {Name: "abc", Image: "image", VolumeMounts: []core.VolumeMount{{Name: "anything", MountPath: "/foo"}},
- ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"},
- },
- field.ErrorList{{Type: field.ErrorTypeNotFound, Field: "containers[0].volumeMounts[0].name"}},
- },
- {
- "invalid lifecycle, no exec command.",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{
- Exec: &core.ExecAction{},
- },
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ field.ErrorList{{Type: field.ErrorTypeNotFound, Field: "containers[0].volumeMounts[0].name"}},
+ }, {
+ "invalid lifecycle, no exec command.",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{
+ Exec: &core.ExecAction{},
},
},
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].lifecycle.preStop.exec.command"}},
- },
- {
- "invalid lifecycle, no http path.",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{
- HTTPGet: &core.HTTPGetAction{
- Port: intstr.FromInt32(80),
- Scheme: "HTTP",
- },
- },
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].lifecycle.preStop.httpGet.path"}},
- },
- {
- "invalid lifecycle, no http port.",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{
- HTTPGet: &core.HTTPGetAction{
- Path: "/",
- Scheme: "HTTP",
- },
- },
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].lifecycle.preStop.httpGet.port"}},
- },
- {
- "invalid lifecycle, no http scheme.",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{
- HTTPGet: &core.HTTPGetAction{
- Path: "/",
- Port: intstr.FromInt32(80),
- },
- },
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "containers[0].lifecycle.preStop.httpGet.scheme"}},
- },
- {
- "invalid lifecycle, no tcp socket port.",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{
- TCPSocket: &core.TCPSocketAction{},
- },
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].lifecycle.preStop.tcpSocket.port"}},
- },
- {
- "invalid lifecycle, zero tcp socket port.",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{
- TCPSocket: &core.TCPSocketAction{
- Port: intstr.FromInt32(0),
- },
- },
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].lifecycle.preStop.tcpSocket.port"}},
- },
- {
- "invalid lifecycle, no action.",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{},
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].lifecycle.preStop"}},
- },
- {
- "invalid readiness probe, terminationGracePeriodSeconds set.",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- ReadinessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{
- Port: intstr.FromInt32(80),
- },
- },
- TerminationGracePeriodSeconds: utilpointer.Int64(10),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.terminationGracePeriodSeconds"}},
- },
- {
- "invalid liveness probe, no tcp socket port.",
- line(),
- []core.Container{
- {
- Name: "live-123",
- Image: "image",
- LivenessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{},
- },
- SuccessThreshold: 1,
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.tcpSocket.port"}},
- },
- {
- "invalid liveness probe, no action.",
- line(),
- []core.Container{
- {
- Name: "live-123",
- Image: "image",
- LivenessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{},
- SuccessThreshold: 1,
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].livenessProbe"}},
- },
- {
- "invalid liveness probe, successThreshold != 1",
- line(),
- []core.Container{
- {
- Name: "live-123",
- Image: "image",
- LivenessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{
- Port: intstr.FromInt32(80),
- },
- },
- SuccessThreshold: 2,
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.successThreshold"}},
- },
- {
- "invalid startup probe, successThreshold != 1",
- line(),
- []core.Container{
- {
- Name: "startup-123",
- Image: "image",
- StartupProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{
- Port: intstr.FromInt32(80),
- },
- },
- SuccessThreshold: 2,
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.successThreshold"}},
- },
- {
- "invalid liveness probe, negative numbers",
- line(),
- []core.Container{
- {
- Name: "live-123",
- Image: "image",
- LivenessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{
- Port: intstr.FromInt32(80),
- },
- },
- InitialDelaySeconds: -1,
- TimeoutSeconds: -1,
- PeriodSeconds: -1,
- SuccessThreshold: -1,
- FailureThreshold: -1,
- TerminationGracePeriodSeconds: utilpointer.Int64(-1),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{
- {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.initialDelaySeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.timeoutSeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.periodSeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.successThreshold"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.failureThreshold"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.terminationGracePeriodSeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.successThreshold"},
- },
- },
- {
- "invalid readiness probe, negative numbers",
- line(),
- []core.Container{
- {
- Name: "ready-123",
- Image: "image",
- ReadinessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{
- Port: intstr.FromInt32(80),
- },
- },
- InitialDelaySeconds: -1,
- TimeoutSeconds: -1,
- PeriodSeconds: -1,
- SuccessThreshold: -1,
- FailureThreshold: -1,
- TerminationGracePeriodSeconds: utilpointer.Int64(-1),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{
- {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.initialDelaySeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.timeoutSeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.periodSeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.successThreshold"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.failureThreshold"},
- // terminationGracePeriodSeconds returns multiple validation errors here:
- // containers[0].readinessProbe.terminationGracePeriodSeconds: Invalid value: -1: must be greater than 0
- {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.terminationGracePeriodSeconds"},
- // containers[0].readinessProbe.terminationGracePeriodSeconds: Invalid value: -1: must not be set for readinessProbes
- {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.terminationGracePeriodSeconds"},
- },
- },
- {
- "invalid startup probe, negative numbers",
- line(),
- []core.Container{
- {
- Name: "startup-123",
- Image: "image",
- StartupProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{
- Port: intstr.FromInt32(80),
- },
- },
- InitialDelaySeconds: -1,
- TimeoutSeconds: -1,
- PeriodSeconds: -1,
- SuccessThreshold: -1,
- FailureThreshold: -1,
- TerminationGracePeriodSeconds: utilpointer.Int64(-1),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{
- {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.initialDelaySeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.timeoutSeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.periodSeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.successThreshold"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.failureThreshold"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.terminationGracePeriodSeconds"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.successThreshold"},
- },
- },
- {
- "invalid message termination policy",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "Unknown",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "containers[0].terminationMessagePolicy"}},
- },
- {
- "empty message termination policy",
- line(),
- []core.Container{
- {
- Name: "life-123",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].terminationMessagePolicy"}},
- },
- {
- "privilege disabled",
- line(),
- []core.Container{
- {
- Name: "abc",
- Image: "image",
- SecurityContext: fakeValidSecurityContext(true),
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "containers[0].securityContext.privileged"}},
- },
- {
- "invalid compute resource",
- line(),
- []core.Container{
- {
- Name: "abc-123",
- Image: "image",
- Resources: core.ResourceRequirements{
- Limits: core.ResourceList{
- "disk": resource.MustParse("10G"),
- },
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{
- {Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[disk]"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[disk]"},
- },
- },
- {
- "Resource CPU invalid",
- line(),
- []core.Container{
- {
- Name: "abc-123",
- Image: "image",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("-10", "0"),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[cpu]"}},
- },
- {
- "Resource Requests CPU invalid",
- line(),
- []core.Container{
- {
- Name: "abc-123",
- Image: "image",
- Resources: core.ResourceRequirements{
- Requests: getResourceLimits("-10", "0"),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.requests[cpu]"}},
- },
- {
- "Resource Memory invalid",
- line(),
- []core.Container{
- {
- Name: "abc-123",
- Image: "image",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("0", "-10"),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[memory]"}},
- },
- {
- "Request limit simple invalid",
- line(),
- []core.Container{
- {
- Name: "abc-123",
- Image: "image",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("5", "3"),
- Requests: getResourceLimits("6", "3"),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.requests"}},
- },
- {
- "Invalid storage limit request",
- line(),
- []core.Container{
- {
- Name: "abc-123",
- Image: "image",
- Resources: core.ResourceRequirements{
- Limits: core.ResourceList{
- core.ResourceName("attachable-volumes-aws-ebs"): *resource.NewQuantity(10, resource.DecimalSI),
- },
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{
- {Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[attachable-volumes-aws-ebs]"},
- {Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[attachable-volumes-aws-ebs]"},
- },
- },
- {
- "CPU request limit multiple invalid",
- line(),
- []core.Container{
- {
- Name: "abc-123",
- Image: "image",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("5", "3"),
- Requests: getResourceLimits("6", "3"),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.requests"}},
- },
- {
- "Memory request limit multiple invalid",
- line(),
- []core.Container{
- {
- Name: "abc-123",
- Image: "image",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("5", "3"),
- Requests: getResourceLimits("5", "4"),
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.requests"}},
- },
- {
- "Invalid env from",
- line(),
- []core.Container{
- {
- Name: "env-from-source",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- EnvFrom: []core.EnvFromSource{
- {
- ConfigMapRef: &core.ConfigMapEnvSource{
- LocalObjectReference: core.LocalObjectReference{
- Name: "$%^&*#",
- },
- },
- },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].lifecycle.preStop.exec.command"}},
+ }, {
+ "invalid lifecycle, no http path.",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{
+ HTTPGet: &core.HTTPGetAction{
+ Port: intstr.FromInt32(80),
+ Scheme: "HTTP",
},
},
},
- field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].envFrom[0].configMapRef.name"}},
- },
- {
- "Unsupported resize policy for memory",
- line(),
- []core.Container{
- {
- Name: "resize-policy-mem-invalid",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- ResizePolicy: []core.ContainerResizePolicy{
- {ResourceName: "memory", RestartPolicy: "RestartContainerrrr"},
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].lifecycle.preStop.httpGet.path"}},
+ }, {
+ "invalid lifecycle, no http port.",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{
+ HTTPGet: &core.HTTPGetAction{
+ Path: "/",
+ Scheme: "HTTP",
},
},
},
- field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "containers[0].resizePolicy"}},
- },
- {
- "Unsupported resize policy for CPU",
- line(),
- []core.Container{
- {
- Name: "resize-policy-cpu-invalid",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- ResizePolicy: []core.ContainerResizePolicy{
- {ResourceName: "cpu", RestartPolicy: "RestartNotRequired"},
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].lifecycle.preStop.httpGet.port"}},
+ }, {
+ "invalid lifecycle, no http scheme.",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{
+ HTTPGet: &core.HTTPGetAction{
+ Path: "/",
+ Port: intstr.FromInt32(80),
},
},
},
- field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "containers[0].resizePolicy"}},
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "containers[0].lifecycle.preStop.httpGet.scheme"}},
+ }, {
+ "invalid lifecycle, no tcp socket port.",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{
+ TCPSocket: &core.TCPSocketAction{},
+ },
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].lifecycle.preStop.tcpSocket.port"}},
+ }, {
+ "invalid lifecycle, zero tcp socket port.",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{
+ TCPSocket: &core.TCPSocketAction{
+ Port: intstr.FromInt32(0),
+ },
+ },
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].lifecycle.preStop.tcpSocket.port"}},
+ }, {
+ "invalid lifecycle, no action.",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{},
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].lifecycle.preStop"}},
+ }, {
+ "invalid readiness probe, terminationGracePeriodSeconds set.",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ ReadinessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{
+ Port: intstr.FromInt32(80),
+ },
+ },
+ TerminationGracePeriodSeconds: utilpointer.Int64(10),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.terminationGracePeriodSeconds"}},
+ }, {
+ "invalid liveness probe, no tcp socket port.",
+ line(),
+ []core.Container{{
+ Name: "live-123",
+ Image: "image",
+ LivenessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{},
+ },
+ SuccessThreshold: 1,
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.tcpSocket.port"}},
+ }, {
+ "invalid liveness probe, no action.",
+ line(),
+ []core.Container{{
+ Name: "live-123",
+ Image: "image",
+ LivenessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{},
+ SuccessThreshold: 1,
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].livenessProbe"}},
+ }, {
+ "invalid liveness probe, successThreshold != 1",
+ line(),
+ []core.Container{{
+ Name: "live-123",
+ Image: "image",
+ LivenessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{
+ Port: intstr.FromInt32(80),
+ },
+ },
+ SuccessThreshold: 2,
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.successThreshold"}},
+ }, {
+ "invalid startup probe, successThreshold != 1",
+ line(),
+ []core.Container{{
+ Name: "startup-123",
+ Image: "image",
+ StartupProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{
+ Port: intstr.FromInt32(80),
+ },
+ },
+ SuccessThreshold: 2,
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.successThreshold"}},
+ }, {
+ "invalid liveness probe, negative numbers",
+ line(),
+ []core.Container{{
+ Name: "live-123",
+ Image: "image",
+ LivenessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{
+ Port: intstr.FromInt32(80),
+ },
+ },
+ InitialDelaySeconds: -1,
+ TimeoutSeconds: -1,
+ PeriodSeconds: -1,
+ SuccessThreshold: -1,
+ FailureThreshold: -1,
+ TerminationGracePeriodSeconds: utilpointer.Int64(-1),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.initialDelaySeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.timeoutSeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.periodSeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.successThreshold"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.failureThreshold"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.terminationGracePeriodSeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].livenessProbe.successThreshold"},
},
+ }, {
+ "invalid readiness probe, negative numbers",
+ line(),
+ []core.Container{{
+ Name: "ready-123",
+ Image: "image",
+ ReadinessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{
+ Port: intstr.FromInt32(80),
+ },
+ },
+ InitialDelaySeconds: -1,
+ TimeoutSeconds: -1,
+ PeriodSeconds: -1,
+ SuccessThreshold: -1,
+ FailureThreshold: -1,
+ TerminationGracePeriodSeconds: utilpointer.Int64(-1),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.initialDelaySeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.timeoutSeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.periodSeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.successThreshold"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.failureThreshold"},
+ // terminationGracePeriodSeconds returns multiple validation errors here:
+ // containers[0].readinessProbe.terminationGracePeriodSeconds: Invalid value: -1: must be greater than 0
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.terminationGracePeriodSeconds"},
+ // containers[0].readinessProbe.terminationGracePeriodSeconds: Invalid value: -1: must not be set for readinessProbes
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].readinessProbe.terminationGracePeriodSeconds"},
+ },
+ }, {
+ "invalid startup probe, negative numbers",
+ line(),
+ []core.Container{{
+ Name: "startup-123",
+ Image: "image",
+ StartupProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{
+ Port: intstr.FromInt32(80),
+ },
+ },
+ InitialDelaySeconds: -1,
+ TimeoutSeconds: -1,
+ PeriodSeconds: -1,
+ SuccessThreshold: -1,
+ FailureThreshold: -1,
+ TerminationGracePeriodSeconds: utilpointer.Int64(-1),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.initialDelaySeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.timeoutSeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.periodSeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.successThreshold"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.failureThreshold"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.terminationGracePeriodSeconds"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].startupProbe.successThreshold"},
+ },
+ }, {
+ "invalid message termination policy",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "Unknown",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "containers[0].terminationMessagePolicy"}},
+ }, {
+ "empty message termination policy",
+ line(),
+ []core.Container{{
+ Name: "life-123",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "containers[0].terminationMessagePolicy"}},
+ }, {
+ "privilege disabled",
+ line(),
+ []core.Container{{
+ Name: "abc",
+ Image: "image",
+ SecurityContext: fakeValidSecurityContext(true),
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "containers[0].securityContext.privileged"}},
+ }, {
+ "invalid compute resource",
+ line(),
+ []core.Container{{
+ Name: "abc-123",
+ Image: "image",
+ Resources: core.ResourceRequirements{
+ Limits: core.ResourceList{
+ "disk": resource.MustParse("10G"),
+ },
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[disk]"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[disk]"},
+ },
+ }, {
+ "Resource CPU invalid",
+ line(),
+ []core.Container{{
+ Name: "abc-123",
+ Image: "image",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("-10", "0"),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[cpu]"}},
+ }, {
+ "Resource Requests CPU invalid",
+ line(),
+ []core.Container{{
+ Name: "abc-123",
+ Image: "image",
+ Resources: core.ResourceRequirements{
+ Requests: getResourceLimits("-10", "0"),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.requests[cpu]"}},
+ }, {
+ "Resource Memory invalid",
+ line(),
+ []core.Container{{
+ Name: "abc-123",
+ Image: "image",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("0", "-10"),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[memory]"}},
+ }, {
+ "Request limit simple invalid",
+ line(),
+ []core.Container{{
+ Name: "abc-123",
+ Image: "image",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("5", "3"),
+ Requests: getResourceLimits("6", "3"),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.requests"}},
+ }, {
+ "Invalid storage limit request",
+ line(),
+ []core.Container{{
+ Name: "abc-123",
+ Image: "image",
+ Resources: core.ResourceRequirements{
+ Limits: core.ResourceList{
+ core.ResourceName("attachable-volumes-aws-ebs"): *resource.NewQuantity(10, resource.DecimalSI),
+ },
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[attachable-volumes-aws-ebs]"},
+ {Type: field.ErrorTypeInvalid, Field: "containers[0].resources.limits[attachable-volumes-aws-ebs]"},
+ },
+ }, {
+ "CPU request limit multiple invalid",
+ line(),
+ []core.Container{{
+ Name: "abc-123",
+ Image: "image",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("5", "3"),
+ Requests: getResourceLimits("6", "3"),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.requests"}},
+ }, {
+ "Memory request limit multiple invalid",
+ line(),
+ []core.Container{{
+ Name: "abc-123",
+ Image: "image",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("5", "3"),
+ Requests: getResourceLimits("5", "4"),
+ },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].resources.requests"}},
+ }, {
+ "Invalid env from",
+ line(),
+ []core.Container{{
+ Name: "env-from-source",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ EnvFrom: []core.EnvFromSource{{
+ ConfigMapRef: &core.ConfigMapEnvSource{
+ LocalObjectReference: core.LocalObjectReference{
+ Name: "$%^&*#",
+ },
+ },
+ }},
+ }},
+ field.ErrorList{{Type: field.ErrorTypeInvalid, Field: "containers[0].envFrom[0].configMapRef.name"}},
+ }, {
+ "Unsupported resize policy for memory",
+ line(),
+ []core.Container{{
+ Name: "resize-policy-mem-invalid",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ ResizePolicy: []core.ContainerResizePolicy{
+ {ResourceName: "memory", RestartPolicy: "RestartContainerrrr"},
+ },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "containers[0].resizePolicy"}},
+ }, {
+ "Unsupported resize policy for CPU",
+ line(),
+ []core.Container{{
+ Name: "resize-policy-cpu-invalid",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ ResizePolicy: []core.ContainerResizePolicy{
+ {ResourceName: "cpu", RestartPolicy: "RestartNotRequired"},
+ },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "containers[0].resizePolicy"}},
+ },
}
for _, tc := range errorCases {
t.Run(tc.title+"__@L"+tc.line, func(t *testing.T) {
@@ -8583,36 +8049,33 @@ func TestValidateInitContainers(t *testing.T) {
AllowPrivileged: true,
})
- containers := []core.Container{
- {
- Name: "app",
- Image: "nginx",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
+ containers := []core.Container{{
+ Name: "app",
+ Image: "nginx",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
}
- successCase := []core.Container{
- {
- Name: "container-1-same-host-port-different-protocol",
- Image: "image",
- Ports: []core.ContainerPort{
- {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
- {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ successCase := []core.Container{{
+ Name: "container-1-same-host-port-different-protocol",
+ Image: "image",
+ Ports: []core.ContainerPort{
+ {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
+ {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
},
- {
- Name: "container-2-same-host-port-different-protocol",
- Image: "image",
- Ports: []core.ContainerPort{
- {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
- {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }, {
+ Name: "container-2-same-host-port-different-protocol",
+ Image: "image",
+ Ports: []core.ContainerPort{
+ {ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
+ {ContainerPort: 80, HostPort: 80, Protocol: "UDP"},
},
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
}
if errs := validateInitContainers(successCase, containers, volumeDevices, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
@@ -8625,184 +8088,150 @@ func TestValidateInitContainers(t *testing.T) {
title, line string
initContainers []core.Container
expectedErrors field.ErrorList
- }{
- {
- "empty name",
- line(),
- []core.Container{
- {
- Name: "",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ }{{
+ "empty name",
+ line(),
+ []core.Container{{
+ Name: "",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeRequired, Field: "initContainers[0].name", BadValue: ""}},
+ }, {
+ "name collision with regular container",
+ line(),
+ []core.Container{{
+ Name: "app",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "initContainers[0].name", BadValue: "app"}},
+ }, {
+ "invalid termination message policy",
+ line(),
+ []core.Container{{
+ Name: "init",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "Unknown",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "initContainers[0].terminationMessagePolicy", BadValue: core.TerminationMessagePolicy("Unknown")}},
+ }, {
+ "duplicate names",
+ line(),
+ []core.Container{{
+ Name: "init",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }, {
+ Name: "init",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "initContainers[1].name", BadValue: "init"}},
+ }, {
+ "duplicate ports",
+ line(),
+ []core.Container{{
+ Name: "abc",
+ Image: "image",
+ Ports: []core.ContainerPort{{
+ ContainerPort: 8080, HostPort: 8080, Protocol: "TCP",
+ }, {
+ ContainerPort: 8080, HostPort: 8080, Protocol: "TCP",
+ }},
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "initContainers[0].ports[1].hostPort", BadValue: "TCP//8080"}},
+ }, {
+ "uses disallowed field: Lifecycle",
+ line(),
+ []core.Container{{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ Lifecycle: &core.Lifecycle{
+ PreStop: &core.LifecycleHandler{
+ Exec: &core.ExecAction{Command: []string{"ls", "-l"}},
},
},
- field.ErrorList{{Type: field.ErrorTypeRequired, Field: "initContainers[0].name", BadValue: ""}},
- },
- {
- "name collision with regular container",
- line(),
- []core.Container{
- {
- Name: "app",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].lifecycle", BadValue: ""}},
+ }, {
+ "uses disallowed field: LivenessProbe",
+ line(),
+ []core.Container{{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ LivenessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
+ },
+ SuccessThreshold: 1,
+ },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].livenessProbe", BadValue: ""}},
+ }, {
+ "uses disallowed field: ReadinessProbe",
+ line(),
+ []core.Container{{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ ReadinessProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
},
},
- field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "initContainers[0].name", BadValue: "app"}},
- },
- {
- "invalid termination message policy",
- line(),
- []core.Container{
- {
- Name: "init",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "Unknown",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].readinessProbe", BadValue: ""}},
+ }, {
+ "Container uses disallowed field: StartupProbe",
+ line(),
+ []core.Container{{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ StartupProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
},
+ SuccessThreshold: 1,
},
- field.ErrorList{{Type: field.ErrorTypeNotSupported, Field: "initContainers[0].terminationMessagePolicy", BadValue: core.TerminationMessagePolicy("Unknown")}},
- },
- {
- "duplicate names",
- line(),
- []core.Container{
- {
- Name: "init",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- {
- Name: "init",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].startupProbe", BadValue: ""}},
+ }, {
+ "Disallowed field with other errors should only return a single Forbidden",
+ line(),
+ []core.Container{{
+ Name: "debug",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ StartupProbe: &core.Probe{
+ ProbeHandler: core.ProbeHandler{
+ TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
},
+ InitialDelaySeconds: -1,
+ TimeoutSeconds: -1,
+ PeriodSeconds: -1,
+ SuccessThreshold: -1,
+ FailureThreshold: -1,
+ TerminationGracePeriodSeconds: utilpointer.Int64(-1),
},
- field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "initContainers[1].name", BadValue: "init"}},
- },
- {
- "duplicate ports",
- line(),
- []core.Container{
- {
- Name: "abc",
- Image: "image",
- Ports: []core.ContainerPort{
- {
- ContainerPort: 8080, HostPort: 8080, Protocol: "TCP",
- },
- {
- ContainerPort: 8080, HostPort: 8080, Protocol: "TCP",
- },
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- },
- field.ErrorList{{Type: field.ErrorTypeDuplicate, Field: "initContainers[0].ports[1].hostPort", BadValue: "TCP//8080"}},
- },
- {
- "uses disallowed field: Lifecycle",
- line(),
- []core.Container{
- {
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- Lifecycle: &core.Lifecycle{
- PreStop: &core.LifecycleHandler{
- Exec: &core.ExecAction{Command: []string{"ls", "-l"}},
- },
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].lifecycle", BadValue: ""}},
- },
- {
- "uses disallowed field: LivenessProbe",
- line(),
- []core.Container{
- {
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- LivenessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
- },
- SuccessThreshold: 1,
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].livenessProbe", BadValue: ""}},
- },
- {
- "uses disallowed field: ReadinessProbe",
- line(),
- []core.Container{
- {
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- ReadinessProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
- },
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].readinessProbe", BadValue: ""}},
- },
- {
- "Container uses disallowed field: StartupProbe",
- line(),
- []core.Container{
- {
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- StartupProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
- },
- SuccessThreshold: 1,
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].startupProbe", BadValue: ""}},
- },
- {
- "Disallowed field with other errors should only return a single Forbidden",
- line(),
- []core.Container{
- {
- Name: "debug",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- StartupProbe: &core.Probe{
- ProbeHandler: core.ProbeHandler{
- TCPSocket: &core.TCPSocketAction{Port: intstr.FromInt32(80)},
- },
- InitialDelaySeconds: -1,
- TimeoutSeconds: -1,
- PeriodSeconds: -1,
- SuccessThreshold: -1,
- FailureThreshold: -1,
- TerminationGracePeriodSeconds: utilpointer.Int64(-1),
- },
- },
- },
- field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].startupProbe", BadValue: ""}},
- },
+ }},
+ field.ErrorList{{Type: field.ErrorTypeForbidden, Field: "initContainers[0].startupProbe", BadValue: ""}},
+ },
}
for _, tc := range errorCases {
t.Run(tc.title+"__@L"+tc.line, func(t *testing.T) {
@@ -8874,249 +8303,229 @@ func TestValidatePodDNSConfig(t *testing.T) {
dnsPolicy *core.DNSPolicy
opts PodValidationOptions
expectedError bool
- }{
- {
- desc: "valid: empty DNSConfig",
- dnsConfig: &core.PodDNSConfig{},
- expectedError: false,
+ }{{
+ desc: "valid: empty DNSConfig",
+ dnsConfig: &core.PodDNSConfig{},
+ expectedError: false,
+ }, {
+ desc: "valid: 1 option",
+ dnsConfig: &core.PodDNSConfig{
+ Options: []core.PodDNSConfigOption{
+ {Name: "ndots", Value: &testOptionValue},
+ },
},
- {
- desc: "valid: 1 option",
- dnsConfig: &core.PodDNSConfig{
- Options: []core.PodDNSConfigOption{
- {Name: "ndots", Value: &testOptionValue},
- },
- },
- expectedError: false,
+ expectedError: false,
+ }, {
+ desc: "valid: 1 nameserver",
+ dnsConfig: &core.PodDNSConfig{
+ Nameservers: []string{"127.0.0.1"},
},
- {
- desc: "valid: 1 nameserver",
- dnsConfig: &core.PodDNSConfig{
- Nameservers: []string{"127.0.0.1"},
- },
- expectedError: false,
+ expectedError: false,
+ }, {
+ desc: "valid: DNSNone with 1 nameserver",
+ dnsConfig: &core.PodDNSConfig{
+ Nameservers: []string{"127.0.0.1"},
},
- {
- desc: "valid: DNSNone with 1 nameserver",
- dnsConfig: &core.PodDNSConfig{
- Nameservers: []string{"127.0.0.1"},
- },
- dnsPolicy: &testDNSNone,
- expectedError: false,
+ dnsPolicy: &testDNSNone,
+ expectedError: false,
+ }, {
+ desc: "valid: 1 search path",
+ dnsConfig: &core.PodDNSConfig{
+ Searches: []string{"custom"},
},
- {
- desc: "valid: 1 search path",
- dnsConfig: &core.PodDNSConfig{
- Searches: []string{"custom"},
- },
- expectedError: false,
+ expectedError: false,
+ }, {
+ desc: "valid: 1 search path with trailing period",
+ dnsConfig: &core.PodDNSConfig{
+ Searches: []string{"custom."},
},
- {
- desc: "valid: 1 search path with trailing period",
- dnsConfig: &core.PodDNSConfig{
- Searches: []string{"custom."},
- },
- expectedError: false,
+ expectedError: false,
+ }, {
+ desc: "valid: 3 nameservers and 6 search paths(legacy)",
+ dnsConfig: &core.PodDNSConfig{
+ Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"},
+ Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local."},
},
- {
- desc: "valid: 3 nameservers and 6 search paths(legacy)",
- dnsConfig: &core.PodDNSConfig{
- Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"},
- Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local."},
- },
- expectedError: false,
+ expectedError: false,
+ }, {
+ desc: "valid: 3 nameservers and 32 search paths",
+ dnsConfig: &core.PodDNSConfig{
+ Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"},
+ Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local.", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"},
},
- {
- desc: "valid: 3 nameservers and 32 search paths",
- dnsConfig: &core.PodDNSConfig{
- Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8"},
- Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local.", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"},
- },
- opts: PodValidationOptions{
- AllowExpandedDNSConfig: true,
- },
- expectedError: false,
+ opts: PodValidationOptions{
+ AllowExpandedDNSConfig: true,
},
- {
- desc: "valid: 256 characters in search path list(legacy)",
- dnsConfig: &core.PodDNSConfig{
- // We can have 256 - (6 - 1) = 251 characters in total for 6 search paths.
- Searches: []string{
- generateTestSearchPathFunc(1),
- generateTestSearchPathFunc(50),
- generateTestSearchPathFunc(50),
- generateTestSearchPathFunc(50),
- generateTestSearchPathFunc(50),
- generateTestSearchPathFunc(50),
- },
+ expectedError: false,
+ }, {
+ desc: "valid: 256 characters in search path list(legacy)",
+ dnsConfig: &core.PodDNSConfig{
+ // We can have 256 - (6 - 1) = 251 characters in total for 6 search paths.
+ Searches: []string{
+ generateTestSearchPathFunc(1),
+ generateTestSearchPathFunc(50),
+ generateTestSearchPathFunc(50),
+ generateTestSearchPathFunc(50),
+ generateTestSearchPathFunc(50),
+ generateTestSearchPathFunc(50),
},
- expectedError: false,
},
- {
- desc: "valid: 2048 characters in search path list",
- dnsConfig: &core.PodDNSConfig{
- // We can have 2048 - (32 - 1) = 2017 characters in total for 32 search paths.
- Searches: []string{
- generateTestSearchPathFunc(64),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- },
+ expectedError: false,
+ }, {
+ desc: "valid: 2048 characters in search path list",
+ dnsConfig: &core.PodDNSConfig{
+ // We can have 2048 - (32 - 1) = 2017 characters in total for 32 search paths.
+ Searches: []string{
+ generateTestSearchPathFunc(64),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
},
- opts: PodValidationOptions{
- AllowExpandedDNSConfig: true,
- },
- expectedError: false,
},
- {
- desc: "valid: ipv6 nameserver",
- dnsConfig: &core.PodDNSConfig{
- Nameservers: []string{"FE80::0202:B3FF:FE1E:8329"},
- },
- expectedError: false,
+ opts: PodValidationOptions{
+ AllowExpandedDNSConfig: true,
},
- {
- desc: "invalid: 4 nameservers",
- dnsConfig: &core.PodDNSConfig{
- Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8", "1.2.3.4"},
- },
- expectedError: true,
+ expectedError: false,
+ }, {
+ desc: "valid: ipv6 nameserver",
+ dnsConfig: &core.PodDNSConfig{
+ Nameservers: []string{"FE80::0202:B3FF:FE1E:8329"},
},
- {
- desc: "invalid: 7 search paths(legacy)",
- dnsConfig: &core.PodDNSConfig{
- Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local", "exceeded"},
- },
- expectedError: true,
+ expectedError: false,
+ }, {
+ desc: "invalid: 4 nameservers",
+ dnsConfig: &core.PodDNSConfig{
+ Nameservers: []string{"127.0.0.1", "10.0.0.10", "8.8.8.8", "1.2.3.4"},
},
- {
- desc: "invalid: 33 search paths",
- dnsConfig: &core.PodDNSConfig{
- Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local.", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33"},
- },
- opts: PodValidationOptions{
- AllowExpandedDNSConfig: true,
- },
- expectedError: true,
+ expectedError: true,
+ }, {
+ desc: "invalid: 7 search paths(legacy)",
+ dnsConfig: &core.PodDNSConfig{
+ Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local", "exceeded"},
},
- {
- desc: "invalid: 257 characters in search path list",
- dnsConfig: &core.PodDNSConfig{
- // We can have 256 - (6 - 1) = 251 characters in total for 6 search paths.
- Searches: []string{
- generateTestSearchPathFunc(2),
- generateTestSearchPathFunc(50),
- generateTestSearchPathFunc(50),
- generateTestSearchPathFunc(50),
- generateTestSearchPathFunc(50),
- generateTestSearchPathFunc(50),
- },
- },
- expectedError: true,
+ expectedError: true,
+ }, {
+ desc: "invalid: 33 search paths",
+ dnsConfig: &core.PodDNSConfig{
+ Searches: []string{"custom", "mydomain.com", "local", "cluster.local", "svc.cluster.local", "default.svc.cluster.local.", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33"},
},
- {
- desc: "invalid: 2049 characters in search path list",
- dnsConfig: &core.PodDNSConfig{
- // We can have 2048 - (32 - 1) = 2017 characters in total for 32 search paths.
- Searches: []string{
- generateTestSearchPathFunc(65),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- generateTestSearchPathFunc(63),
- },
- },
- opts: PodValidationOptions{
- AllowExpandedDNSConfig: true,
- },
- expectedError: true,
+ opts: PodValidationOptions{
+ AllowExpandedDNSConfig: true,
},
- {
- desc: "invalid search path",
- dnsConfig: &core.PodDNSConfig{
- Searches: []string{"custom?"},
+ expectedError: true,
+ }, {
+ desc: "invalid: 257 characters in search path list",
+ dnsConfig: &core.PodDNSConfig{
+ // We can have 256 - (6 - 1) = 251 characters in total for 6 search paths.
+ Searches: []string{
+ generateTestSearchPathFunc(2),
+ generateTestSearchPathFunc(50),
+ generateTestSearchPathFunc(50),
+ generateTestSearchPathFunc(50),
+ generateTestSearchPathFunc(50),
+ generateTestSearchPathFunc(50),
},
- expectedError: true,
},
- {
- desc: "invalid nameserver",
- dnsConfig: &core.PodDNSConfig{
- Nameservers: []string{"invalid"},
+ expectedError: true,
+ }, {
+ desc: "invalid: 2049 characters in search path list",
+ dnsConfig: &core.PodDNSConfig{
+ // We can have 2048 - (32 - 1) = 2017 characters in total for 32 search paths.
+ Searches: []string{
+ generateTestSearchPathFunc(65),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
+ generateTestSearchPathFunc(63),
},
- expectedError: true,
},
- {
- desc: "invalid empty option name",
- dnsConfig: &core.PodDNSConfig{
- Options: []core.PodDNSConfigOption{
- {Value: &testOptionValue},
- },
+ opts: PodValidationOptions{
+ AllowExpandedDNSConfig: true,
+ },
+ expectedError: true,
+ }, {
+ desc: "invalid search path",
+ dnsConfig: &core.PodDNSConfig{
+ Searches: []string{"custom?"},
+ },
+ expectedError: true,
+ }, {
+ desc: "invalid nameserver",
+ dnsConfig: &core.PodDNSConfig{
+ Nameservers: []string{"invalid"},
+ },
+ expectedError: true,
+ }, {
+ desc: "invalid empty option name",
+ dnsConfig: &core.PodDNSConfig{
+ Options: []core.PodDNSConfigOption{
+ {Value: &testOptionValue},
},
- expectedError: true,
},
- {
- desc: "invalid: DNSNone with 0 nameserver",
- dnsConfig: &core.PodDNSConfig{
- Searches: []string{"custom"},
- },
- dnsPolicy: &testDNSNone,
- expectedError: true,
+ expectedError: true,
+ }, {
+ desc: "invalid: DNSNone with 0 nameserver",
+ dnsConfig: &core.PodDNSConfig{
+ Searches: []string{"custom"},
},
+ dnsPolicy: &testDNSNone,
+ expectedError: true,
+ },
}
for _, tc := range testCases {
@@ -9137,30 +8546,22 @@ func TestValidatePodReadinessGates(t *testing.T) {
successCases := []struct {
desc string
readinessGates []core.PodReadinessGate
- }{
- {
- "no gate",
- []core.PodReadinessGate{},
- },
- {
- "one readiness gate",
- []core.PodReadinessGate{
- {
- ConditionType: core.PodConditionType("example.com/condition"),
- },
- },
- },
- {
- "two readiness gates",
- []core.PodReadinessGate{
- {
- ConditionType: core.PodConditionType("example.com/condition1"),
- },
- {
- ConditionType: core.PodConditionType("example.com/condition2"),
- },
- },
- },
+ }{{
+ "no gate",
+ []core.PodReadinessGate{},
+ }, {
+ "one readiness gate",
+ []core.PodReadinessGate{{
+ ConditionType: core.PodConditionType("example.com/condition"),
+ }},
+ }, {
+ "two readiness gates",
+ []core.PodReadinessGate{{
+ ConditionType: core.PodConditionType("example.com/condition1"),
+ }, {
+ ConditionType: core.PodConditionType("example.com/condition2"),
+ }},
+ },
}
for _, tc := range successCases {
if errs := validateReadinessGates(tc.readinessGates, field.NewPath("field")); len(errs) != 0 {
@@ -9171,15 +8572,12 @@ func TestValidatePodReadinessGates(t *testing.T) {
errorCases := []struct {
desc string
readinessGates []core.PodReadinessGate
- }{
- {
- "invalid condition type",
- []core.PodReadinessGate{
- {
- ConditionType: core.PodConditionType("invalid/condition/type"),
- },
- },
- },
+ }{{
+ "invalid condition type",
+ []core.PodReadinessGate{{
+ ConditionType: core.PodConditionType("invalid/condition/type"),
+ }},
+ },
}
for _, tc := range errorCases {
if errs := validateReadinessGates(tc.readinessGates, field.NewPath("field")); len(errs) == 0 {
@@ -9192,46 +8590,34 @@ func TestValidatePodConditions(t *testing.T) {
successCases := []struct {
desc string
podConditions []core.PodCondition
- }{
- {
- "no condition",
- []core.PodCondition{},
- },
- {
- "one system condition",
- []core.PodCondition{
- {
- Type: core.PodReady,
- Status: core.ConditionTrue,
- },
- },
- },
- {
- "one system condition and one custom condition",
- []core.PodCondition{
- {
- Type: core.PodReady,
- Status: core.ConditionTrue,
- },
- {
- Type: core.PodConditionType("example.com/condition"),
- Status: core.ConditionFalse,
- },
- },
- },
- {
- "two custom condition",
- []core.PodCondition{
- {
- Type: core.PodConditionType("foobar"),
- Status: core.ConditionTrue,
- },
- {
- Type: core.PodConditionType("example.com/condition"),
- Status: core.ConditionFalse,
- },
- },
- },
+ }{{
+ "no condition",
+ []core.PodCondition{},
+ }, {
+ "one system condition",
+ []core.PodCondition{{
+ Type: core.PodReady,
+ Status: core.ConditionTrue,
+ }},
+ }, {
+ "one system condition and one custom condition",
+ []core.PodCondition{{
+ Type: core.PodReady,
+ Status: core.ConditionTrue,
+ }, {
+ Type: core.PodConditionType("example.com/condition"),
+ Status: core.ConditionFalse,
+ }},
+ }, {
+ "two custom condition",
+ []core.PodCondition{{
+ Type: core.PodConditionType("foobar"),
+ Status: core.ConditionTrue,
+ }, {
+ Type: core.PodConditionType("example.com/condition"),
+ Status: core.ConditionFalse,
+ }},
+ },
}
for _, tc := range successCases {
@@ -9243,20 +8629,16 @@ func TestValidatePodConditions(t *testing.T) {
errorCases := []struct {
desc string
podConditions []core.PodCondition
- }{
- {
- "one system condition and a invalid custom condition",
- []core.PodCondition{
- {
- Type: core.PodReady,
- Status: core.ConditionStatus("True"),
- },
- {
- Type: core.PodConditionType("invalid/custom/condition"),
- Status: core.ConditionStatus("True"),
- },
- },
- },
+ }{{
+ "one system condition and a invalid custom condition",
+ []core.PodCondition{{
+ Type: core.PodReady,
+ Status: core.ConditionStatus("True"),
+ }, {
+ Type: core.PodConditionType("invalid/custom/condition"),
+ Status: core.ConditionStatus("True"),
+ }},
+ },
}
for _, tc := range errorCases {
if errs := validatePodConditions(tc.podConditions, field.NewPath("field")); len(errs) == 0 {
@@ -9637,28 +9019,24 @@ func TestValidatePodSpec(t *testing.T) {
DNSPolicy: core.DNSClusterFirst,
},
"disallowed resources resize policy for init containers": {
- InitContainers: []core.Container{
- {
- Name: "initctr",
- Image: "initimage",
- ResizePolicy: []core.ContainerResizePolicy{
- {ResourceName: "cpu", RestartPolicy: "NotRequired"},
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ InitContainers: []core.Container{{
+ Name: "initctr",
+ Image: "initimage",
+ ResizePolicy: []core.ContainerResizePolicy{
+ {ResourceName: "cpu", RestartPolicy: "NotRequired"},
},
- },
- Containers: []core.Container{
- {
- Name: "ctr",
- Image: "image",
- ResizePolicy: []core.ContainerResizePolicy{
- {ResourceName: "cpu", RestartPolicy: "NotRequired"},
- },
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
+ Containers: []core.Container{{
+ Name: "ctr",
+ Image: "image",
+ ResizePolicy: []core.ContainerResizePolicy{
+ {ResourceName: "cpu", RestartPolicy: "NotRequired"},
},
- },
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -9755,39 +9133,29 @@ func TestValidatePod(t *testing.T) {
&core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "key2",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"value1", "value2"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"host1"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "key2",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"value1", "value2"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"host1"},
+ }},
+ }},
},
- PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
- {
- Weight: 10,
- Preference: core.NodeSelectorTerm{
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "foo",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"bar"},
- },
- },
- },
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{{
+ Weight: 10,
+ Preference: core.NodeSelectorTerm{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "foo",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"bar"},
+ }},
},
- },
+ }},
},
},
),
@@ -9815,20 +9183,16 @@ func TestValidatePod(t *testing.T) {
&core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{},
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{},
+ }},
},
- PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
- {
- Weight: 10,
- Preference: core.NodeSelectorTerm{
- MatchExpressions: []core.NodeSelectorRequirement{},
- },
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{{
+ Weight: 10,
+ Preference: core.NodeSelectorTerm{
+ MatchExpressions: []core.NodeSelectorRequirement{},
},
- },
+ }},
},
},
),
@@ -9853,48 +9217,38 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAffinity: &core.PodAffinity{
- RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
- {
+ RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{{
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpIn,
+ Values: []string{"value1", "value2"},
+ }},
+ },
+ TopologyKey: "zone",
+ Namespaces: []string{"ns"},
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key",
+ Operator: metav1.LabelSelectorOpIn,
+ Values: []string{"value1", "value2"},
+ }},
+ },
+ }},
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{{
+ Weight: 10,
+ PodAffinityTerm: core.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpIn,
- Values: []string{"value1", "value2"},
- },
- },
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpNotIn,
+ Values: []string{"value1", "value2"},
+ }},
},
- TopologyKey: "zone",
Namespaces: []string{"ns"},
- NamespaceSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key",
- Operator: metav1.LabelSelectorOpIn,
- Values: []string{"value1", "value2"},
- },
- },
- },
+ TopologyKey: "region",
},
- },
- PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
- {
- Weight: 10,
- PodAffinityTerm: core.PodAffinityTerm{
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpNotIn,
- Values: []string{"value1", "value2"},
- },
- },
- },
- Namespaces: []string{"ns"},
- TopologyKey: "region",
- },
- },
- },
+ }},
},
}),
},
@@ -9918,37 +9272,29 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAntiAffinity: &core.PodAntiAffinity{
- RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
- {
+ RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{{
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpExists,
+ }},
+ },
+ TopologyKey: "zone",
+ Namespaces: []string{"ns"},
+ }},
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{{
+ Weight: 10,
+ PodAffinityTerm: core.PodAffinityTerm{
LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpExists,
- },
- },
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpDoesNotExist,
+ }},
},
- TopologyKey: "zone",
Namespaces: []string{"ns"},
+ TopologyKey: "region",
},
- },
- PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
- {
- Weight: 10,
- PodAffinityTerm: core.PodAffinityTerm{
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpDoesNotExist,
- },
- },
- },
- Namespaces: []string{"ns"},
- TopologyKey: "region",
- },
- },
- },
+ }},
},
}),
},
@@ -10204,42 +9550,36 @@ func TestValidatePod(t *testing.T) {
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
SecurityContext: &core.PodSecurityContext{
- Sysctls: []core.Sysctl{
- {
- Name: "kernel.shmmni",
- Value: "32768",
- },
- {
- Name: "kernel.shmmax",
- Value: "1000000000",
- },
- {
- Name: "knet.ipv4.route.min_pmtu",
- Value: "1000",
- },
- },
+ Sysctls: []core.Sysctl{{
+ Name: "kernel.shmmni",
+ Value: "32768",
+ }, {
+ Name: "kernel.shmmax",
+ Value: "1000000000",
+ }, {
+ Name: "knet.ipv4.route.min_pmtu",
+ Value: "1000",
+ }},
},
},
},
"valid extended resources for init container": {
ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
Spec: core.PodSpec{
- InitContainers: []core.Container{
- {
- Name: "valid-extended",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("10"),
- },
- Limits: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("10"),
- },
+ InitContainers: []core.Container{{
+ Name: "valid-extended",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("10"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("10"),
},
- TerminationMessagePolicy: "File",
},
- },
+ TerminationMessagePolicy: "File",
+ }},
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
@@ -10249,22 +9589,20 @@ func TestValidatePod(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{Name: "valid-extended", Namespace: "ns"},
Spec: core.PodSpec{
InitContainers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
- Containers: []core.Container{
- {
- Name: "valid-extended",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("10"),
- },
- Limits: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("10"),
- },
+ Containers: []core.Container{{
+ Name: "valid-extended",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("10"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("10"),
},
- TerminationMessagePolicy: "File",
},
- },
+ TerminationMessagePolicy: "File",
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -10276,24 +9614,20 @@ func TestValidatePod(t *testing.T) {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- Volumes: []core.Volume{
- {
- Name: "projected-volume",
- VolumeSource: core.VolumeSource{
- Projected: &core.ProjectedVolumeSource{
- Sources: []core.VolumeProjection{
- {
- ServiceAccountToken: &core.ServiceAccountTokenProjection{
- Audience: "foo-audience",
- ExpirationSeconds: 6000,
- Path: "foo-path",
- },
- },
+ Volumes: []core.Volume{{
+ Name: "projected-volume",
+ VolumeSource: core.VolumeSource{
+ Projected: &core.ProjectedVolumeSource{
+ Sources: []core.VolumeProjection{{
+ ServiceAccountToken: &core.ServiceAccountTokenProjection{
+ Audience: "foo-audience",
+ ExpirationSeconds: 6000,
+ Path: "foo-path",
},
- },
+ }},
},
},
- },
+ }},
},
},
"ephemeral volume + PVC, no conflict between them": {
@@ -10417,15 +9751,11 @@ func TestValidatePod(t *testing.T) {
Spec: validPodSpec(&core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "key1",
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "key1",
+ }},
+ }},
},
},
}),
@@ -10441,16 +9771,12 @@ func TestValidatePod(t *testing.T) {
Spec: validPodSpec(&core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "invalid key ___@#",
- Operator: core.NodeSelectorOpExists,
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "invalid key ___@#",
+ Operator: core.NodeSelectorOpExists,
+ }},
+ }},
},
},
}),
@@ -10466,17 +9792,13 @@ func TestValidatePod(t *testing.T) {
Spec: validPodSpec(&core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"host1", "host2"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"host1", "host2"},
+ }},
+ }},
},
},
}),
@@ -10492,16 +9814,12 @@ func TestValidatePod(t *testing.T) {
Spec: validPodSpec(&core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpExists,
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpExists,
+ }},
+ }},
},
},
}),
@@ -10517,17 +9835,13 @@ func TestValidatePod(t *testing.T) {
Spec: validPodSpec(&core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.namespace",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"ns1"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.namespace",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"ns1"},
+ }},
+ }},
},
},
}),
@@ -10542,20 +9856,16 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
NodeAffinity: &core.NodeAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
- {
- Weight: 199,
- Preference: core.NodeSelectorTerm{
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "foo",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"bar"},
- },
- },
- },
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{{
+ Weight: 199,
+ Preference: core.NodeSelectorTerm{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "foo",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"bar"},
+ }},
},
- },
+ }},
},
}),
},
@@ -10585,24 +9895,20 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAffinity: &core.PodAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
- {
- Weight: 109,
- PodAffinityTerm: core.PodAffinityTerm{
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpNotIn,
- Values: []string{"value1", "value2"},
- },
- },
- },
- Namespaces: []string{"ns"},
- TopologyKey: "region",
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{{
+ Weight: 109,
+ PodAffinityTerm: core.PodAffinityTerm{
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpNotIn,
+ Values: []string{"value1", "value2"},
+ }},
},
+ Namespaces: []string{"ns"},
+ TopologyKey: "region",
},
- },
+ }},
},
}),
},
@@ -10616,24 +9922,20 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAntiAffinity: &core.PodAntiAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
- {
- Weight: 10,
- PodAffinityTerm: core.PodAffinityTerm{
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpExists,
- Values: []string{"value1", "value2"},
- },
- },
- },
- Namespaces: []string{"ns"},
- TopologyKey: "region",
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{{
+ Weight: 10,
+ PodAffinityTerm: core.PodAffinityTerm{
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpExists,
+ Values: []string{"value1", "value2"},
+ }},
},
+ Namespaces: []string{"ns"},
+ TopologyKey: "region",
},
- },
+ }},
},
}),
},
@@ -10647,23 +9949,19 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAntiAffinity: &core.PodAntiAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
- {
- Weight: 10,
- PodAffinityTerm: core.PodAffinityTerm{
- NamespaceSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpIn,
- },
- },
- },
- Namespaces: []string{"ns"},
- TopologyKey: "region",
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{{
+ Weight: 10,
+ PodAffinityTerm: core.PodAffinityTerm{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpIn,
+ }},
},
+ Namespaces: []string{"ns"},
+ TopologyKey: "region",
},
- },
+ }},
},
}),
},
@@ -10677,24 +9975,20 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAntiAffinity: &core.PodAntiAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
- {
- Weight: 10,
- PodAffinityTerm: core.PodAffinityTerm{
- NamespaceSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpExists,
- Values: []string{"value1", "value2"},
- },
- },
- },
- Namespaces: []string{"ns"},
- TopologyKey: "region",
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{{
+ Weight: 10,
+ PodAffinityTerm: core.PodAffinityTerm{
+ NamespaceSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpExists,
+ Values: []string{"value1", "value2"},
+ }},
},
+ Namespaces: []string{"ns"},
+ TopologyKey: "region",
},
- },
+ }},
},
}),
},
@@ -10708,23 +10002,19 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAffinity: &core.PodAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
- {
- Weight: 10,
- PodAffinityTerm: core.PodAffinityTerm{
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpExists,
- },
- },
- },
- Namespaces: []string{"INVALID_NAMESPACE"},
- TopologyKey: "region",
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{{
+ Weight: 10,
+ PodAffinityTerm: core.PodAffinityTerm{
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpExists,
+ }},
},
+ Namespaces: []string{"INVALID_NAMESPACE"},
+ TopologyKey: "region",
},
- },
+ }},
},
}),
},
@@ -10738,20 +10028,16 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAffinity: &core.PodAffinity{
- RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
- {
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpIn,
- Values: []string{"value1", "value2"},
- },
- },
- },
- Namespaces: []string{"ns"},
+ RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{{
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpIn,
+ Values: []string{"value1", "value2"},
+ }},
},
- },
+ Namespaces: []string{"ns"},
+ }},
},
}),
},
@@ -10765,20 +10051,16 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAntiAffinity: &core.PodAntiAffinity{
- RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{
- {
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpIn,
- Values: []string{"value1", "value2"},
- },
- },
- },
- Namespaces: []string{"ns"},
+ RequiredDuringSchedulingIgnoredDuringExecution: []core.PodAffinityTerm{{
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpIn,
+ Values: []string{"value1", "value2"},
+ }},
},
- },
+ Namespaces: []string{"ns"},
+ }},
},
}),
},
@@ -10792,23 +10074,19 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAffinity: &core.PodAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
- {
- Weight: 10,
- PodAffinityTerm: core.PodAffinityTerm{
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpNotIn,
- Values: []string{"value1", "value2"},
- },
- },
- },
- Namespaces: []string{"ns"},
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{{
+ Weight: 10,
+ PodAffinityTerm: core.PodAffinityTerm{
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpNotIn,
+ Values: []string{"value1", "value2"},
+ }},
},
+ Namespaces: []string{"ns"},
},
- },
+ }},
},
}),
},
@@ -10822,23 +10100,19 @@ func TestValidatePod(t *testing.T) {
},
Spec: validPodSpec(&core.Affinity{
PodAntiAffinity: &core.PodAntiAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{
- {
- Weight: 10,
- PodAffinityTerm: core.PodAffinityTerm{
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key2",
- Operator: metav1.LabelSelectorOpNotIn,
- Values: []string{"value1", "value2"},
- },
- },
- },
- Namespaces: []string{"ns"},
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.WeightedPodAffinityTerm{{
+ Weight: 10,
+ PodAffinityTerm: core.PodAffinityTerm{
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key2",
+ Operator: metav1.LabelSelectorOpNotIn,
+ Values: []string{"value1", "value2"},
+ }},
},
+ Namespaces: []string{"ns"},
},
- },
+ }},
},
}),
},
@@ -11067,21 +10341,19 @@ func TestValidatePod(t *testing.T) {
spec: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "invalid",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("invalid-name"): resource.MustParse("2"),
- },
- Limits: core.ResourceList{
- core.ResourceName("invalid-name"): resource.MustParse("2"),
- },
+ Containers: []core.Container{{
+ Name: "invalid",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("invalid-name"): resource.MustParse("2"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName("invalid-name"): resource.MustParse("2"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -11092,21 +10364,19 @@ func TestValidatePod(t *testing.T) {
spec: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "invalid",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("2"),
- },
- Limits: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("1"),
- },
+ Containers: []core.Container{{
+ Name: "invalid",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("2"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("1"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -11117,18 +10387,16 @@ func TestValidatePod(t *testing.T) {
spec: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "invalid",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("2"),
- },
+ Containers: []core.Container{{
+ Name: "invalid",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("2"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -11139,18 +10407,16 @@ func TestValidatePod(t *testing.T) {
spec: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "invalid",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("500m"),
- },
+ Containers: []core.Container{{
+ Name: "invalid",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("500m"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -11161,18 +10427,16 @@ func TestValidatePod(t *testing.T) {
spec: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
- InitContainers: []core.Container{
- {
- Name: "invalid",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("500m"),
- },
+ InitContainers: []core.Container{{
+ Name: "invalid",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("500m"),
},
},
- },
+ }},
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
@@ -11184,21 +10448,19 @@ func TestValidatePod(t *testing.T) {
spec: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "invalid",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("5"),
- },
- Limits: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("2.5"),
- },
+ Containers: []core.Container{{
+ Name: "invalid",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("5"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("2.5"),
},
},
- },
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -11209,21 +10471,19 @@ func TestValidatePod(t *testing.T) {
spec: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: "ns"},
Spec: core.PodSpec{
- InitContainers: []core.Container{
- {
- Name: "invalid",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- Resources: core.ResourceRequirements{
- Requests: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("2.5"),
- },
- Limits: core.ResourceList{
- core.ResourceName("example.com/a"): resource.MustParse("2.5"),
- },
+ InitContainers: []core.Container{{
+ Name: "invalid",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ Resources: core.ResourceRequirements{
+ Requests: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("2.5"),
+ },
+ Limits: core.ResourceList{
+ core.ResourceName("example.com/a"): resource.MustParse("2.5"),
},
},
- },
+ }},
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
@@ -11260,24 +10520,20 @@ func TestValidatePod(t *testing.T) {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- Volumes: []core.Volume{
- {
- Name: "projected-volume",
- VolumeSource: core.VolumeSource{
- Projected: &core.ProjectedVolumeSource{
- Sources: []core.VolumeProjection{
- {
- ServiceAccountToken: &core.ServiceAccountTokenProjection{
- Audience: "foo-audience",
- ExpirationSeconds: 6000,
- Path: "foo-path",
- },
- },
+ Volumes: []core.Volume{{
+ Name: "projected-volume",
+ VolumeSource: core.VolumeSource{
+ Projected: &core.ProjectedVolumeSource{
+ Sources: []core.VolumeProjection{{
+ ServiceAccountToken: &core.ServiceAccountTokenProjection{
+ Audience: "foo-audience",
+ ExpirationSeconds: 6000,
+ Path: "foo-path",
},
- },
+ }},
},
},
- },
+ }},
},
},
},
@@ -11373,61 +10629,57 @@ func TestValidatePodCreateWithSchedulingGates(t *testing.T) {
pod *core.Pod
featureEnabled bool
wantFieldErrors field.ErrorList
- }{
- {
- name: "create a Pod with nodeName and schedulingGates, feature disabled",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
- Spec: core.PodSpec{
- NodeName: "node",
- SchedulingGates: []core.PodSchedulingGate{
- {Name: "foo"},
- },
+ }{{
+ name: "create a Pod with nodeName and schedulingGates, feature disabled",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
+ Spec: core.PodSpec{
+ NodeName: "node",
+ SchedulingGates: []core.PodSchedulingGate{
+ {Name: "foo"},
},
},
- featureEnabled: false,
- wantFieldErrors: []*field.Error{field.Forbidden(fldPath.Child("nodeName"), "cannot be set until all schedulingGates have been cleared")},
},
- {
- name: "create a Pod with nodeName and schedulingGates, feature enabled",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
- Spec: core.PodSpec{
- NodeName: "node",
- SchedulingGates: []core.PodSchedulingGate{
- {Name: "foo"},
- },
+ featureEnabled: false,
+ wantFieldErrors: []*field.Error{field.Forbidden(fldPath.Child("nodeName"), "cannot be set until all schedulingGates have been cleared")},
+ }, {
+ name: "create a Pod with nodeName and schedulingGates, feature enabled",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
+ Spec: core.PodSpec{
+ NodeName: "node",
+ SchedulingGates: []core.PodSchedulingGate{
+ {Name: "foo"},
},
},
- featureEnabled: true,
- wantFieldErrors: []*field.Error{field.Forbidden(fldPath.Child("nodeName"), "cannot be set until all schedulingGates have been cleared")},
},
- {
- name: "create a Pod with schedulingGates, feature disabled",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
- Spec: core.PodSpec{
- SchedulingGates: []core.PodSchedulingGate{
- {Name: "foo"},
- },
+ featureEnabled: true,
+ wantFieldErrors: []*field.Error{field.Forbidden(fldPath.Child("nodeName"), "cannot be set until all schedulingGates have been cleared")},
+ }, {
+ name: "create a Pod with schedulingGates, feature disabled",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
+ Spec: core.PodSpec{
+ SchedulingGates: []core.PodSchedulingGate{
+ {Name: "foo"},
},
},
- featureEnabled: false,
- wantFieldErrors: nil,
},
- {
- name: "create a Pod with schedulingGates, feature enabled",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
- Spec: core.PodSpec{
- SchedulingGates: []core.PodSchedulingGate{
- {Name: "foo"},
- },
+ featureEnabled: false,
+ wantFieldErrors: nil,
+ }, {
+ name: "create a Pod with schedulingGates, feature enabled",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{Name: "pod", Namespace: "ns"},
+ Spec: core.PodSpec{
+ SchedulingGates: []core.PodSchedulingGate{
+ {Name: "foo"},
},
},
- featureEnabled: true,
- wantFieldErrors: nil,
},
+ featureEnabled: true,
+ wantFieldErrors: nil,
+ },
}
for _, tt := range tests {
@@ -11464,8 +10716,7 @@ func TestValidatePodUpdate(t *testing.T) {
err string
test string
}{
- {new: core.Pod{}, old: core.Pod{}, err: "", test: "nothing"},
- {
+ {new: core.Pod{}, old: core.Pod{}, err: "", test: "nothing"}, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
},
@@ -11474,8 +10725,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "metadata.name",
test: "ids",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -11494,8 +10744,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "labels",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -11514,95 +10763,76 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "annotations",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Image: "foo:V1",
- },
- },
+ Containers: []core.Container{{
+ Image: "foo:V1",
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Image: "foo:V2",
- },
- {
- Image: "bar:V2",
- },
- },
+ Containers: []core.Container{{
+ Image: "foo:V2",
+ }, {
+ Image: "bar:V2",
+ }},
},
},
err: "may not add or remove containers",
test: "less containers",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Image: "foo:V1",
- },
- {
- Image: "bar:V2",
- },
- },
+ Containers: []core.Container{{
+ Image: "foo:V1",
+ }, {
+ Image: "bar:V2",
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Image: "foo:V2",
- },
- },
+ Containers: []core.Container{{
+ Image: "foo:V2",
+ }},
},
},
err: "may not add or remove containers",
test: "more containers",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
Spec: core.PodSpec{
- InitContainers: []core.Container{
- {
- Image: "foo:V1",
- },
- },
+ InitContainers: []core.Container{{
+ Image: "foo:V1",
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- InitContainers: []core.Container{
- {
- Image: "foo:V2",
- },
- {
- Image: "bar:V2",
- },
- },
+ InitContainers: []core.Container{{
+ Image: "foo:V2",
+ }, {
+ Image: "bar:V2",
+ }},
},
},
err: "may not add or remove containers",
test: "more init containers",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
@@ -11613,8 +10843,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "metadata.deletionTimestamp",
test: "deletion timestamp removed",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now},
Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
@@ -11625,8 +10854,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "metadata.deletionTimestamp",
test: "deletion timestamp added",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &grace},
Spec: core.PodSpec{Containers: []core.Container{{Image: "foo:V1"}}},
@@ -11637,137 +10865,114 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "metadata.deletionGracePeriodSeconds",
test: "deletion grace period seconds changed",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- Image: "foo:V1",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- },
- },
+ Containers: []core.Container{{
+ Name: "container",
+ Image: "foo:V1",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- Image: "foo:V2",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- },
- },
+ Containers: []core.Container{{
+ Name: "container",
+ Image: "foo:V2",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ }},
},
},
err: "",
test: "image change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- InitContainers: []core.Container{
- {
- Name: "container",
- Image: "foo:V1",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- },
- },
+ InitContainers: []core.Container{{
+ Name: "container",
+ Image: "foo:V1",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- InitContainers: []core.Container{
- {
- Name: "container",
- Image: "foo:V2",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- },
- },
+ InitContainers: []core.Container{{
+ Name: "container",
+ Image: "foo:V2",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ }},
},
},
err: "",
test: "init container image change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- },
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- Image: "foo:V2",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- },
- },
+ Containers: []core.Container{{
+ Name: "container",
+ Image: "foo:V2",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ }},
},
},
err: "spec.containers[0].image",
test: "image change to empty",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- InitContainers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- },
- },
+ InitContainers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- InitContainers: []core.Container{
- {
- Name: "container",
- Image: "foo:V2",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- },
- },
+ InitContainers: []core.Container{{
+ Name: "container",
+ Image: "foo:V2",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ }},
},
},
err: "spec.initContainers[0].image",
test: "init container image change to empty",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- EphemeralContainers: []core.EphemeralContainer{
- {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "ephemeral",
- Image: "busybox",
- },
+ EphemeralContainers: []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "ephemeral",
+ Image: "busybox",
},
- },
+ }},
},
},
old: core.Pod{
@@ -11776,8 +10981,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: pod updates may not change fields other than",
test: "ephemeralContainer changes are not allowed via normal pod update",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{},
},
@@ -11786,8 +10990,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "activeDeadlineSeconds no change, nil",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
@@ -11800,8 +11003,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "activeDeadlineSeconds no change, set",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
@@ -11810,8 +11012,7 @@ func TestValidatePodUpdate(t *testing.T) {
old: core.Pod{},
err: "",
test: "activeDeadlineSeconds change to positive from nil",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
ActiveDeadlineSeconds: &activeDeadlineSecondsPositive,
@@ -11824,8 +11025,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "activeDeadlineSeconds change to smaller positive",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
ActiveDeadlineSeconds: &activeDeadlineSecondsLarger,
@@ -11849,8 +11049,7 @@ func TestValidatePodUpdate(t *testing.T) {
old: core.Pod{},
err: "spec.activeDeadlineSeconds",
test: "activeDeadlineSeconds change to negative from nil",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
ActiveDeadlineSeconds: &activeDeadlineSecondsNegative,
@@ -11863,8 +11062,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.activeDeadlineSeconds",
test: "activeDeadlineSeconds change to negative from positive",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
ActiveDeadlineSeconds: &activeDeadlineSecondsZero,
@@ -11877,8 +11075,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.activeDeadlineSeconds",
test: "activeDeadlineSeconds change to zero from positive",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
ActiveDeadlineSeconds: &activeDeadlineSecondsZero,
@@ -11887,8 +11084,7 @@ func TestValidatePodUpdate(t *testing.T) {
old: core.Pod{},
err: "spec.activeDeadlineSeconds",
test: "activeDeadlineSeconds change to zero from nil",
- },
- {
+ }, {
new: core.Pod{},
old: core.Pod{
Spec: core.PodSpec{
@@ -11897,599 +11093,516 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.activeDeadlineSeconds",
test: "activeDeadlineSeconds change to nil from positive",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResources("200m", "0", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("200m", "0", "1Gi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResources("100m", "0", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("100m", "0", "1Gi"),
},
- },
+ }},
},
},
err: "",
test: "cpu limit change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V1",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V1",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("100m", "200Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("100m", "200Mi"),
},
- },
+ }},
},
},
err: "",
test: "memory limit change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V1",
- Resources: core.ResourceRequirements{
- Limits: getResources("100m", "100Mi", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V1",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("100m", "100Mi", "1Gi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResources("100m", "100Mi", "2Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("100m", "100Mi", "2Gi"),
},
- },
+ }},
},
},
err: "Forbidden: pod updates may not change fields other than",
test: "storage limit change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V1",
- Resources: core.ResourceRequirements{
- Requests: getResourceLimits("100m", "0"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V1",
+ Resources: core.ResourceRequirements{
+ Requests: getResourceLimits("100m", "0"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Requests: getResourceLimits("200m", "0"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Requests: getResourceLimits("200m", "0"),
},
- },
+ }},
},
},
err: "",
test: "cpu request change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V1",
- Resources: core.ResourceRequirements{
- Requests: getResourceLimits("0", "200Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V1",
+ Resources: core.ResourceRequirements{
+ Requests: getResourceLimits("0", "200Mi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Requests: getResourceLimits("0", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Requests: getResourceLimits("0", "100Mi"),
},
- },
+ }},
},
},
err: "",
test: "memory request change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V1",
- Resources: core.ResourceRequirements{
- Requests: getResources("100m", "0", "2Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V1",
+ Resources: core.ResourceRequirements{
+ Requests: getResources("100m", "0", "2Gi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Requests: getResources("100m", "0", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Requests: getResources("100m", "0", "1Gi"),
},
- },
+ }},
},
},
err: "Forbidden: pod updates may not change fields other than",
test: "storage request change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V1",
- Resources: core.ResourceRequirements{
- Limits: getResources("200m", "400Mi", "1Gi"),
- Requests: getResources("200m", "400Mi", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V1",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("200m", "400Mi", "1Gi"),
+ Requests: getResources("200m", "400Mi", "1Gi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V1",
- Resources: core.ResourceRequirements{
- Limits: getResources("100m", "100Mi", "1Gi"),
- Requests: getResources("100m", "100Mi", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V1",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("100m", "100Mi", "1Gi"),
+ Requests: getResources("100m", "100Mi", "1Gi"),
},
- },
+ }},
},
},
err: "",
test: "Pod QoS unchanged, guaranteed -> guaranteed",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V1",
- Resources: core.ResourceRequirements{
- Limits: getResources("200m", "200Mi", "2Gi"),
- Requests: getResources("100m", "100Mi", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V1",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("200m", "200Mi", "2Gi"),
+ Requests: getResources("100m", "100Mi", "1Gi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V1",
- Resources: core.ResourceRequirements{
- Limits: getResources("400m", "400Mi", "2Gi"),
- Requests: getResources("200m", "200Mi", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V1",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("400m", "400Mi", "2Gi"),
+ Requests: getResources("200m", "200Mi", "1Gi"),
},
- },
+ }},
},
},
err: "",
test: "Pod QoS unchanged, burstable -> burstable",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("200m", "200Mi"),
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("200m", "200Mi"),
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
err: "",
test: "Pod QoS unchanged, burstable -> burstable, add limits",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("200m", "200Mi"),
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("200m", "200Mi"),
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
err: "",
test: "Pod QoS unchanged, burstable -> burstable, remove limits",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResources("400m", "", "1Gi"),
- Requests: getResources("300m", "", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("400m", "", "1Gi"),
+ Requests: getResources("300m", "", "1Gi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResources("200m", "500Mi", "1Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("200m", "500Mi", "1Gi"),
},
- },
+ }},
},
},
err: "",
test: "Pod QoS unchanged, burstable -> burstable, add requests",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResources("400m", "500Mi", "2Gi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("400m", "500Mi", "2Gi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResources("200m", "300Mi", "2Gi"),
- Requests: getResourceLimits("100m", "200Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResources("200m", "300Mi", "2Gi"),
+ Requests: getResourceLimits("100m", "200Mi"),
},
- },
+ }},
},
},
err: "",
test: "Pod QoS unchanged, burstable -> burstable, remove requests",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("200m", "200Mi"),
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("200m", "200Mi"),
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("100m", "100Mi"),
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("100m", "100Mi"),
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
err: "Pod QoS is immutable",
test: "Pod QoS change, guaranteed -> burstable",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("100m", "100Mi"),
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("100m", "100Mi"),
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
err: "Pod QoS is immutable",
test: "Pod QoS change, burstable -> guaranteed",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("200m", "200Mi"),
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("200m", "200Mi"),
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- },
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ }},
},
},
err: "Pod QoS is immutable",
test: "Pod QoS change, besteffort -> burstable",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- },
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "pod"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "container",
- TerminationMessagePolicy: "File",
- ImagePullPolicy: "Always",
- Image: "foo:V2",
- Resources: core.ResourceRequirements{
- Limits: getResourceLimits("200m", "200Mi"),
- Requests: getResourceLimits("100m", "100Mi"),
- },
+ Containers: []core.Container{{
+ Name: "container",
+ TerminationMessagePolicy: "File",
+ ImagePullPolicy: "Always",
+ Image: "foo:V2",
+ Resources: core.ResourceRequirements{
+ Limits: getResourceLimits("200m", "200Mi"),
+ Requests: getResourceLimits("100m", "100Mi"),
},
- },
+ }},
},
},
err: "Pod QoS is immutable",
test: "Pod QoS change, burstable -> besteffort",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Image: "foo:V1",
- },
- },
+ Containers: []core.Container{{
+ Image: "foo:V1",
+ }},
SecurityContext: &core.PodSecurityContext{
FSGroupChangePolicy: &validfsGroupChangePolicy,
},
@@ -12498,11 +11611,9 @@ func TestValidatePodUpdate(t *testing.T) {
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Image: "foo:V2",
- },
- },
+ Containers: []core.Container{{
+ Image: "foo:V2",
+ }},
SecurityContext: &core.PodSecurityContext{
FSGroupChangePolicy: nil,
},
@@ -12510,38 +11621,32 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec: Forbidden: pod updates may not change fields",
test: "fsGroupChangePolicy change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Image: "foo:V1",
- Ports: []core.ContainerPort{
- {HostPort: 8080, ContainerPort: 80},
- },
+ Containers: []core.Container{{
+ Image: "foo:V1",
+ Ports: []core.ContainerPort{
+ {HostPort: 8080, ContainerPort: 80},
},
- },
+ }},
},
},
old: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Image: "foo:V2",
- Ports: []core.ContainerPort{
- {HostPort: 8000, ContainerPort: 80},
- },
+ Containers: []core.Container{{
+ Image: "foo:V2",
+ Ports: []core.ContainerPort{
+ {HostPort: 8000, ContainerPort: 80},
},
- },
+ }},
},
},
err: "spec: Forbidden: pod updates may not change fields",
test: "port change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12560,8 +11665,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "bad label change",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12582,8 +11686,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.tolerations: Forbidden",
test: "existing toleration value modified in pod spec updates",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12604,8 +11707,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.tolerations: Forbidden",
test: "existing toleration value modified in pod spec updates with modified tolerationSeconds",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12625,8 +11727,7 @@ func TestValidatePodUpdate(t *testing.T) {
}},
err: "",
test: "modified tolerationSeconds in existing toleration value in pod spec updates",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12646,8 +11747,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.tolerations: Forbidden",
test: "toleration modified in updates to an unscheduled pod",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12668,8 +11768,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "tolerations unmodified in updates to a scheduled pod",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12692,8 +11791,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "added valid new toleration to existing tolerations in pod spec updates",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{
NodeName: "node1",
@@ -12711,32 +11809,27 @@ func TestValidatePodUpdate(t *testing.T) {
}},
err: "spec.tolerations[1].effect",
test: "added invalid new toleration to existing tolerations in pod spec updates",
- },
- {
+ }, {
new: core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{NodeName: "foo"}},
old: core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
err: "spec: Forbidden: pod updates may not change fields",
test: "removed nodeName from pod spec",
- },
- {
+ }, {
new: core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: ""}}, Spec: core.PodSpec{NodeName: "foo"}},
old: core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{NodeName: "foo"}},
err: "metadata.annotations[kubernetes.io/config.mirror]",
test: "added mirror pod annotation",
- },
- {
+ }, {
new: core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: core.PodSpec{NodeName: "foo"}},
old: core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: ""}}, Spec: core.PodSpec{NodeName: "foo"}},
err: "metadata.annotations[kubernetes.io/config.mirror]",
test: "removed mirror pod annotation",
- },
- {
+ }, {
new: core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: "foo"}}, Spec: core.PodSpec{NodeName: "foo"}},
old: core.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Annotations: map[string]string{core.MirrorPodAnnotationKey: "bar"}}, Spec: core.PodSpec{NodeName: "foo"}},
err: "metadata.annotations[kubernetes.io/config.mirror]",
test: "changed mirror pod annotation",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12757,8 +11850,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec: Forbidden: pod updates",
test: "changed priority class name",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12779,8 +11871,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec: Forbidden: pod updates",
test: "removed priority class name",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12799,8 +11890,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "update termination grace period seconds",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12819,8 +11909,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec: Forbidden: pod updates",
test: "update termination grace period seconds not 1",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12841,8 +11930,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "pod OS changing from Linux to Windows, IdentifyPodOS featuregate set",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12863,8 +11951,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.securityContext.seLinuxOptions: Forbidden",
test: "pod OS changing from Linux to Windows, IdentifyPodOS featuregate set, we'd get SELinux errors as well",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12881,8 +11968,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "invalid PodOS update, IdentifyPodOS featuregate set",
- },
- {
+ }, {
new: core.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
@@ -12901,8 +11987,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: pod updates may not change fields other than ",
test: "update pod spec OS to a valid value, featuregate disabled",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
SchedulingGates: []core.PodSchedulingGate{{Name: "foo"}},
@@ -12911,8 +11996,7 @@ func TestValidatePodUpdate(t *testing.T) {
old: core.Pod{},
err: "Forbidden: only deletion is allowed, but found new scheduling gate 'foo'",
test: "update pod spec schedulingGates: add new scheduling gate",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
SchedulingGates: []core.PodSchedulingGate{{Name: "bar"}},
@@ -12925,8 +12009,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: only deletion is allowed, but found new scheduling gate 'bar'",
test: "update pod spec schedulingGates: mutating an existing scheduling gate",
- },
- {
+ }, {
new: core.Pod{
Spec: core.PodSpec{
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
@@ -12939,8 +12022,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: only deletion is allowed, but found new scheduling gate 'baz'",
test: "update pod spec schedulingGates: mutating an existing scheduling gate along with deletion",
- },
- {
+ }, {
new: core.Pod{},
old: core.Pod{
Spec: core.PodSpec{
@@ -12949,8 +12031,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "",
test: "update pod spec schedulingGates: legal deletion",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
@@ -12966,8 +12047,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "node selector is immutable when AllowMutableNodeSelector is false",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
@@ -12985,8 +12065,7 @@ func TestValidatePodUpdate(t *testing.T) {
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "adding node selector is allowed for gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
NodeSelector: map[string]string{
@@ -13007,8 +12086,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "adding node selector is not allowed for non-gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
NodeSelector: map[string]string{
@@ -13027,8 +12105,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.nodeSelector: Invalid value:",
test: "removing node selector is not allowed for gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
NodeSelector: map[string]string{
@@ -13042,8 +12119,7 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "removing node selector is not allowed for non-gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
NodeSelector: map[string]string{
@@ -13064,8 +12140,7 @@ func TestValidatePodUpdate(t *testing.T) {
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "old pod spec has scheduling gate, new pod spec does not, and node selector is added",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
NodeSelector: map[string]string{
@@ -13087,24 +12162,19 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.nodeSelector: Invalid value:",
test: "modifying value of existing node selector is not allowed",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13117,29 +12187,22 @@ func TestValidatePodUpdate(t *testing.T) {
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
// Add 1 MatchExpression and 1 MatchField.
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- {
- Key: "expr2",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo2"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }, {
+ Key: "expr2",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo2"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13150,24 +12213,19 @@ func TestValidatePodUpdate(t *testing.T) {
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "addition to nodeAffinity is allowed for gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13187,24 +12245,19 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:",
test: "old RequiredDuringSchedulingIgnoredDuringExecution is non-nil, new RequiredDuringSchedulingIgnoredDuringExecution is nil, pod is gated",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13216,29 +12269,22 @@ func TestValidatePodUpdate(t *testing.T) {
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
// Add 1 MatchExpression and 1 MatchField.
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- {
- Key: "expr2",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo2"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }, {
+ Key: "expr2",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo2"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13249,24 +12295,19 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "Forbidden: pod updates may not change fields other than `spec.containers[*].image",
test: "addition to nodeAffinity is not allowed for non-gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13279,29 +12320,22 @@ func TestValidatePodUpdate(t *testing.T) {
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
// Add 1 MatchExpression and 1 MatchField.
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- {
- Key: "expr2",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo2"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }, {
+ Key: "expr2",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo2"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13311,24 +12345,19 @@ func TestValidatePodUpdate(t *testing.T) {
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "old pod spec has scheduling gate, new pod spec does not, and node affinity addition occurs",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13340,17 +12369,13 @@ func TestValidatePodUpdate(t *testing.T) {
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13362,31 +12387,24 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "nodeAffinity deletion from MatchExpressions not allowed",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13399,17 +12417,13 @@ func TestValidatePodUpdate(t *testing.T) {
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
// Add 1 MatchExpression and 1 MatchField.
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13421,31 +12435,24 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "nodeAffinity deletion from MatchFields not allowed",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13458,24 +12465,18 @@ func TestValidatePodUpdate(t *testing.T) {
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
// Add 1 MatchExpression and 1 MatchField.
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"bar"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"bar"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13487,31 +12488,24 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "nodeAffinity modification of item in MatchExpressions not allowed",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13523,24 +12517,18 @@ func TestValidatePodUpdate(t *testing.T) {
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"bar"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"bar"},
+ }},
+ }},
},
},
},
@@ -13552,31 +12540,24 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0]: Invalid value:",
test: "nodeAffinity modification of item in MatchFields not allowed",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13588,40 +12569,29 @@ func TestValidatePodUpdate(t *testing.T) {
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"bar"},
- },
- },
- },
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo2"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"bar2"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"bar"},
+ }},
+ }, {
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo2"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"bar2"},
+ }},
+ }},
},
},
},
@@ -13633,26 +12603,21 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:",
test: "nodeSelectorTerms addition on gated pod should fail",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
- {
- Weight: 1.0,
- Preference: core.NodeSelectorTerm{
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{{
+ Weight: 1.0,
+ Preference: core.NodeSelectorTerm{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
},
- },
+ }},
},
},
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
@@ -13662,20 +12627,16 @@ func TestValidatePodUpdate(t *testing.T) {
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
- {
- Weight: 1.0,
- Preference: core.NodeSelectorTerm{
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo2"},
- },
- },
- },
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{{
+ Weight: 1.0,
+ Preference: core.NodeSelectorTerm{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo2"},
+ }},
},
- },
+ }},
},
},
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
@@ -13685,26 +12646,21 @@ func TestValidatePodUpdate(t *testing.T) {
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "preferredDuringSchedulingIgnoredDuringExecution can modified for gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
- {
- Weight: 1.0,
- Preference: core.NodeSelectorTerm{
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{{
+ Weight: 1.0,
+ Preference: core.NodeSelectorTerm{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
},
- },
+ }},
},
},
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
@@ -13714,32 +12670,25 @@ func TestValidatePodUpdate(t *testing.T) {
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
- {
- Weight: 1.0,
- Preference: core.NodeSelectorTerm{
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- {
- Key: "expr2",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo2"},
- },
- },
- MatchFields: []core.NodeSelectorRequirement{
- {
- Key: "metadata.name",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"bar"},
- },
- },
- },
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{{
+ Weight: 1.0,
+ Preference: core.NodeSelectorTerm{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }, {
+ Key: "expr2",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo2"},
+ }},
+ MatchFields: []core.NodeSelectorRequirement{{
+ Key: "metadata.name",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"bar"},
+ }},
},
- },
+ }},
},
},
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
@@ -13749,26 +12698,21 @@ func TestValidatePodUpdate(t *testing.T) {
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "preferredDuringSchedulingIgnoredDuringExecution can have additions for gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
- {
- Weight: 1.0,
- Preference: core.NodeSelectorTerm{
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{{
+ Weight: 1.0,
+ Preference: core.NodeSelectorTerm{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
},
- },
+ }},
},
},
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
@@ -13783,24 +12727,19 @@ func TestValidatePodUpdate(t *testing.T) {
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "preferredDuringSchedulingIgnoredDuringExecution can have removals for gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13818,26 +12757,21 @@ func TestValidatePodUpdate(t *testing.T) {
},
err: "spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms: Invalid value:",
test: "new node affinity is nil",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
- PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{
- {
- Weight: 1.0,
- Preference: core.NodeSelectorTerm{
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
+ PreferredDuringSchedulingIgnoredDuringExecution: []core.PreferredSchedulingTerm{{
+ Weight: 1.0,
+ Preference: core.NodeSelectorTerm{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
},
- },
+ }},
},
},
SchedulingGates: []core.PodSchedulingGate{{Name: "baz"}},
@@ -13852,8 +12786,7 @@ func TestValidatePodUpdate(t *testing.T) {
AllowMutableNodeSelectorAndNodeAffinity: true,
},
test: "preferredDuringSchedulingIgnoredDuringExecution can have removals for gated pods",
- },
- {
+ }, {
old: core.Pod{
Spec: core.PodSpec{
Affinity: &core.Affinity{
@@ -13873,17 +12806,13 @@ func TestValidatePodUpdate(t *testing.T) {
Affinity: &core.Affinity{
NodeAffinity: &core.NodeAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: &core.NodeSelector{
- NodeSelectorTerms: []core.NodeSelectorTerm{
- {
- MatchExpressions: []core.NodeSelectorRequirement{
- {
- Key: "expr",
- Operator: core.NodeSelectorOpIn,
- Values: []string{"foo"},
- },
- },
- },
- },
+ NodeSelectorTerms: []core.NodeSelectorTerm{{
+ MatchExpressions: []core.NodeSelectorRequirement{{
+ Key: "expr",
+ Operator: core.NodeSelectorOpIn,
+ Values: []string{"foo"},
+ }},
+ }},
},
},
},
@@ -13944,503 +12873,493 @@ func TestValidatePodStatusUpdate(t *testing.T) {
old core.Pod
err string
test string
- }{
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: core.PodSpec{
- NodeName: "node1",
- },
- Status: core.PodStatus{
- NominatedNodeName: "node1",
- },
+ }{{
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: core.PodSpec{
- NodeName: "node1",
- },
- Status: core.PodStatus{},
+ Spec: core.PodSpec{
+ NodeName: "node1",
+ },
+ Status: core.PodStatus{
+ NominatedNodeName: "node1",
},
- "",
- "removed nominatedNodeName",
},
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: core.PodSpec{
- NodeName: "node1",
- },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: core.PodSpec{
- NodeName: "node1",
- },
- Status: core.PodStatus{
- NominatedNodeName: "node1",
- },
+ Spec: core.PodSpec{
+ NodeName: "node1",
},
- "",
- "add valid nominatedNodeName",
+ Status: core.PodStatus{},
},
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: core.PodSpec{
- NodeName: "node1",
- },
- Status: core.PodStatus{
- NominatedNodeName: "Node1",
- },
+ "",
+ "removed nominatedNodeName",
+ }, {
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: core.PodSpec{
- NodeName: "node1",
- },
+ Spec: core.PodSpec{
+ NodeName: "node1",
},
- "nominatedNodeName",
- "Add invalid nominatedNodeName",
},
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: core.PodSpec{
- NodeName: "node1",
- },
- Status: core.PodStatus{
- NominatedNodeName: "node1",
- },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Spec: core.PodSpec{
- NodeName: "node1",
- },
- Status: core.PodStatus{
- NominatedNodeName: "node2",
- },
+ Spec: core.PodSpec{
+ NodeName: "node1",
+ },
+ Status: core.PodStatus{
+ NominatedNodeName: "node1",
},
- "",
- "Update nominatedNodeName",
},
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- InitContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "alpine",
- Name: "init",
- Ready: false,
- Started: proto.Bool(false),
- State: core.ContainerState{
- Waiting: &core.ContainerStateWaiting{
- Reason: "PodInitializing",
- },
- },
- }},
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- Name: "main",
- Ready: false,
- Started: proto.Bool(false),
- State: core.ContainerState{
- Waiting: &core.ContainerStateWaiting{
- Reason: "PodInitializing",
- },
- },
- }},
- },
+ "",
+ "add valid nominatedNodeName",
+ }, {
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
+ Spec: core.PodSpec{
+ NodeName: "node1",
+ },
+ Status: core.PodStatus{
+ NominatedNodeName: "Node1",
},
- "",
- "Container statuses pending",
},
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- InitContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "init",
- Ready: true,
- State: core.ContainerState{
- Terminated: &core.ContainerStateTerminated{
- ContainerID: "docker://numbers",
- Reason: "Completed",
- },
- },
- }},
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "nginx",
- Ready: true,
- Started: proto.Bool(true),
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- InitContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "alpine",
- Name: "init",
- Ready: false,
- State: core.ContainerState{
- Waiting: &core.ContainerStateWaiting{
- Reason: "PodInitializing",
- },
- },
- }},
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- Name: "main",
- Ready: false,
- Started: proto.Bool(false),
- State: core.ContainerState{
- Waiting: &core.ContainerStateWaiting{
- Reason: "PodInitializing",
- },
- },
- }},
- },
+ Spec: core.PodSpec{
+ NodeName: "node1",
},
- "",
- "Container statuses running",
},
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "nginx",
- Ready: true,
- Started: proto.Bool(true),
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- EphemeralContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "busybox",
- Name: "debug",
- Ready: false,
- State: core.ContainerState{
- Waiting: &core.ContainerStateWaiting{
- Reason: "PodInitializing",
- },
- },
- }},
- },
+ "nominatedNodeName",
+ "Add invalid nominatedNodeName",
+ }, {
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "nginx",
- Ready: true,
- Started: proto.Bool(true),
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- },
+ Spec: core.PodSpec{
+ NodeName: "node1",
+ },
+ Status: core.PodStatus{
+ NominatedNodeName: "node1",
},
- "",
- "Container statuses add ephemeral container",
},
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "nginx",
- Ready: true,
- Started: proto.Bool(true),
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- EphemeralContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "busybox",
- ImageID: "docker-pullable://busybox@sha256:d0gf00d",
- Name: "debug",
- Ready: false,
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "nginx",
- Ready: true,
- Started: proto.Bool(true),
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- EphemeralContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "busybox",
- Name: "debug",
- Ready: false,
- State: core.ContainerState{
- Waiting: &core.ContainerStateWaiting{
- Reason: "PodInitializing",
- },
- },
- }},
- },
+ Spec: core.PodSpec{
+ NodeName: "node1",
+ },
+ Status: core.PodStatus{
+ NominatedNodeName: "node2",
},
- "",
- "Container statuses ephemeral container running",
},
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "nginx",
- Ready: true,
- Started: proto.Bool(true),
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- EphemeralContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "busybox",
- ImageID: "docker-pullable://busybox@sha256:d0gf00d",
- Name: "debug",
- Ready: false,
- State: core.ContainerState{
- Terminated: &core.ContainerStateTerminated{
- ContainerID: "docker://numbers",
- Reason: "Completed",
- StartedAt: metav1.NewTime(time.Now()),
- FinishedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- },
+ "",
+ "Update nominatedNodeName",
+ }, {
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "nginx",
- Ready: true,
- Started: proto.Bool(true),
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
+ Status: core.PodStatus{
+ InitContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "alpine",
+ Name: "init",
+ Ready: false,
+ Started: proto.Bool(false),
+ State: core.ContainerState{
+ Waiting: &core.ContainerStateWaiting{
+ Reason: "PodInitializing",
},
- }},
- EphemeralContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "busybox",
- ImageID: "docker-pullable://busybox@sha256:d0gf00d",
- Name: "debug",
- Ready: false,
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
+ },
+ }},
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ Name: "main",
+ Ready: false,
+ Started: proto.Bool(false),
+ State: core.ContainerState{
+ Waiting: &core.ContainerStateWaiting{
+ Reason: "PodInitializing",
},
- }},
- },
+ },
+ }},
},
- "",
- "Container statuses ephemeral container exited",
},
- {
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- InitContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "init",
- Ready: true,
- State: core.ContainerState{
- Terminated: &core.ContainerStateTerminated{
- ContainerID: "docker://numbers",
- Reason: "Completed",
- },
- },
- }},
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "nginx",
- Ready: true,
- Started: proto.Bool(true),
- State: core.ContainerState{
- Terminated: &core.ContainerStateTerminated{
- ContainerID: "docker://numbers",
- Reason: "Completed",
- StartedAt: metav1.NewTime(time.Now()),
- FinishedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- EphemeralContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "busybox",
- ImageID: "docker-pullable://busybox@sha256:d0gf00d",
- Name: "debug",
- Ready: false,
- State: core.ContainerState{
- Terminated: &core.ContainerStateTerminated{
- ContainerID: "docker://numbers",
- Reason: "Completed",
- StartedAt: metav1.NewTime(time.Now()),
- FinishedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
},
- core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Name: "foo",
- },
- Status: core.PodStatus{
- InitContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "init",
- Ready: true,
- State: core.ContainerState{
- Terminated: &core.ContainerStateTerminated{
- ContainerID: "docker://numbers",
- Reason: "Completed",
- },
- },
- }},
- ContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "nginx:alpine",
- ImageID: "docker-pullable://nginx@sha256:d0gf00d",
- Name: "nginx",
- Ready: true,
- Started: proto.Bool(true),
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- EphemeralContainerStatuses: []core.ContainerStatus{{
- ContainerID: "docker://numbers",
- Image: "busybox",
- ImageID: "docker-pullable://busybox@sha256:d0gf00d",
- Name: "debug",
- Ready: false,
- State: core.ContainerState{
- Running: &core.ContainerStateRunning{
- StartedAt: metav1.NewTime(time.Now()),
- },
- },
- }},
- },
- },
- "",
- "Container statuses all containers terminated",
},
+ "",
+ "Container statuses pending",
+ }, {
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ InitContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "init",
+ Ready: true,
+ State: core.ContainerState{
+ Terminated: &core.ContainerStateTerminated{
+ ContainerID: "docker://numbers",
+ Reason: "Completed",
+ },
+ },
+ }},
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "nginx",
+ Ready: true,
+ Started: proto.Bool(true),
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ },
+ },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ InitContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "alpine",
+ Name: "init",
+ Ready: false,
+ State: core.ContainerState{
+ Waiting: &core.ContainerStateWaiting{
+ Reason: "PodInitializing",
+ },
+ },
+ }},
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ Name: "main",
+ Ready: false,
+ Started: proto.Bool(false),
+ State: core.ContainerState{
+ Waiting: &core.ContainerStateWaiting{
+ Reason: "PodInitializing",
+ },
+ },
+ }},
+ },
+ },
+ "",
+ "Container statuses running",
+ }, {
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "nginx",
+ Ready: true,
+ Started: proto.Bool(true),
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ EphemeralContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "busybox",
+ Name: "debug",
+ Ready: false,
+ State: core.ContainerState{
+ Waiting: &core.ContainerStateWaiting{
+ Reason: "PodInitializing",
+ },
+ },
+ }},
+ },
+ },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "nginx",
+ Ready: true,
+ Started: proto.Bool(true),
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ },
+ },
+ "",
+ "Container statuses add ephemeral container",
+ }, {
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "nginx",
+ Ready: true,
+ Started: proto.Bool(true),
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ EphemeralContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "busybox",
+ ImageID: "docker-pullable://busybox@sha256:d0gf00d",
+ Name: "debug",
+ Ready: false,
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ },
+ },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "nginx",
+ Ready: true,
+ Started: proto.Bool(true),
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ EphemeralContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "busybox",
+ Name: "debug",
+ Ready: false,
+ State: core.ContainerState{
+ Waiting: &core.ContainerStateWaiting{
+ Reason: "PodInitializing",
+ },
+ },
+ }},
+ },
+ },
+ "",
+ "Container statuses ephemeral container running",
+ }, {
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "nginx",
+ Ready: true,
+ Started: proto.Bool(true),
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ EphemeralContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "busybox",
+ ImageID: "docker-pullable://busybox@sha256:d0gf00d",
+ Name: "debug",
+ Ready: false,
+ State: core.ContainerState{
+ Terminated: &core.ContainerStateTerminated{
+ ContainerID: "docker://numbers",
+ Reason: "Completed",
+ StartedAt: metav1.NewTime(time.Now()),
+ FinishedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ },
+ },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "nginx",
+ Ready: true,
+ Started: proto.Bool(true),
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ EphemeralContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "busybox",
+ ImageID: "docker-pullable://busybox@sha256:d0gf00d",
+ Name: "debug",
+ Ready: false,
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ },
+ },
+ "",
+ "Container statuses ephemeral container exited",
+ }, {
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ InitContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "init",
+ Ready: true,
+ State: core.ContainerState{
+ Terminated: &core.ContainerStateTerminated{
+ ContainerID: "docker://numbers",
+ Reason: "Completed",
+ },
+ },
+ }},
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "nginx",
+ Ready: true,
+ Started: proto.Bool(true),
+ State: core.ContainerState{
+ Terminated: &core.ContainerStateTerminated{
+ ContainerID: "docker://numbers",
+ Reason: "Completed",
+ StartedAt: metav1.NewTime(time.Now()),
+ FinishedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ EphemeralContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "busybox",
+ ImageID: "docker-pullable://busybox@sha256:d0gf00d",
+ Name: "debug",
+ Ready: false,
+ State: core.ContainerState{
+ Terminated: &core.ContainerStateTerminated{
+ ContainerID: "docker://numbers",
+ Reason: "Completed",
+ StartedAt: metav1.NewTime(time.Now()),
+ FinishedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ },
+ },
+ core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ },
+ Status: core.PodStatus{
+ InitContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "init",
+ Ready: true,
+ State: core.ContainerState{
+ Terminated: &core.ContainerStateTerminated{
+ ContainerID: "docker://numbers",
+ Reason: "Completed",
+ },
+ },
+ }},
+ ContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "nginx:alpine",
+ ImageID: "docker-pullable://nginx@sha256:d0gf00d",
+ Name: "nginx",
+ Ready: true,
+ Started: proto.Bool(true),
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ EphemeralContainerStatuses: []core.ContainerStatus{{
+ ContainerID: "docker://numbers",
+ Image: "busybox",
+ ImageID: "docker-pullable://busybox@sha256:d0gf00d",
+ Name: "debug",
+ Ready: false,
+ State: core.ContainerState{
+ Running: &core.ContainerStateRunning{
+ StartedAt: metav1.NewTime(time.Now()),
+ },
+ },
+ }},
+ },
+ },
+ "",
+ "Container statuses all containers terminated",
+ },
}
for _, test := range tests {
@@ -14548,313 +13467,300 @@ func TestValidatePodEphemeralContainersUpdate(t *testing.T) {
name string
new, old *core.Pod
err string
- }{
- {
- "no ephemeral containers",
- makePod([]core.EphemeralContainer{}),
- makePod([]core.EphemeralContainer{}),
- "",
- },
- {
- "No change in Ephemeral Containers",
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ }{{
+ "no ephemeral containers",
+ makePod([]core.EphemeralContainer{}),
+ makePod([]core.EphemeralContainer{}),
+ "",
+ }, {
+ "No change in Ephemeral Containers",
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ "",
+ }, {
+ "Ephemeral Container list order changes",
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ "",
+ }, {
+ "Add an Ephemeral Container",
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makePod([]core.EphemeralContainer{}),
+ "",
+ }, {
+ "Add two Ephemeral Containers",
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger1",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makePod([]core.EphemeralContainer{}),
+ "",
+ }, {
+ "Add to an existing Ephemeral Containers",
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ "",
+ }, {
+ "Add to an existing Ephemeral Containers, list order changes",
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger3",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ "",
+ }, {
+ "Remove an Ephemeral Container",
+ makePod([]core.EphemeralContainer{}),
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ "may not be removed",
+ }, {
+ "Replace an Ephemeral Container",
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "firstone",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "thentheother",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ "may not be removed",
+ }, {
+ "Change an Ephemeral Containers",
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger1",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makePod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger1",
+ Image: "debian",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }, {
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger2",
+ Image: "busybox",
+ ImagePullPolicy: "IfNotPresent",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ "may not be changed",
+ }, {
+ "Ephemeral container with potential conflict with regular containers, but conflict not present",
+ makeWindowsHostPod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger1",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: proto.Bool(true),
+ },
},
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makeWindowsHostPod(nil),
+ "",
+ }, {
+ "Ephemeral container with potential conflict with regular containers, and conflict is present",
+ makeWindowsHostPod([]core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ Name: "debugger1",
+ Image: "image",
+ ImagePullPolicy: "IfNotPresent",
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: proto.Bool(false),
+ },
},
- }}),
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- "",
- },
- {
- "Ephemeral Container list order changes",
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- "",
- },
- {
- "Add an Ephemeral Container",
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- makePod([]core.EphemeralContainer{}),
- "",
- },
- {
- "Add two Ephemeral Containers",
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger1",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- makePod([]core.EphemeralContainer{}),
- "",
- },
- {
- "Add to an existing Ephemeral Containers",
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- "",
- },
- {
- "Add to an existing Ephemeral Containers, list order changes",
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger3",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- "",
- },
- {
- "Remove an Ephemeral Container",
- makePod([]core.EphemeralContainer{}),
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- "may not be removed",
- },
- {
- "Replace an Ephemeral Container",
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "firstone",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "thentheother",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- "may not be removed",
- },
- {
- "Change an Ephemeral Containers",
- makePod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger1",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- makePod([]core.EphemeralContainer{{
+ TerminationMessagePolicy: "File",
+ },
+ }}),
+ makeWindowsHostPod(nil),
+ "spec.ephemeralContainers[0].securityContext.windowsOptions.hostProcess: Invalid value: false: pod hostProcess value must be identical",
+ }, {
+ "Add ephemeral container to static pod",
+ func() *core.Pod {
+ p := makePod(nil)
+ p.Spec.NodeName = "some-name"
+ p.ObjectMeta.Annotations = map[string]string{
+ core.MirrorPodAnnotationKey: "foo",
+ }
+ p.Spec.EphemeralContainers = []core.EphemeralContainer{{
EphemeralContainerCommon: core.EphemeralContainerCommon{
Name: "debugger1",
Image: "debian",
ImagePullPolicy: "IfNotPresent",
TerminationMessagePolicy: "File",
},
- }, {
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger2",
- Image: "busybox",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}),
- "may not be changed",
- },
- {
- "Ephemeral container with potential conflict with regular containers, but conflict not present",
- makeWindowsHostPod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger1",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: proto.Bool(true),
- },
- },
- TerminationMessagePolicy: "File",
- },
- }}),
- makeWindowsHostPod(nil),
- "",
- },
- {
- "Ephemeral container with potential conflict with regular containers, and conflict is present",
- makeWindowsHostPod([]core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger1",
- Image: "image",
- ImagePullPolicy: "IfNotPresent",
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: proto.Bool(false),
- },
- },
- TerminationMessagePolicy: "File",
- },
- }}),
- makeWindowsHostPod(nil),
- "spec.ephemeralContainers[0].securityContext.windowsOptions.hostProcess: Invalid value: false: pod hostProcess value must be identical",
- },
- {
- "Add ephemeral container to static pod",
- func() *core.Pod {
- p := makePod(nil)
- p.Spec.NodeName = "some-name"
- p.ObjectMeta.Annotations = map[string]string{
- core.MirrorPodAnnotationKey: "foo",
- }
- p.Spec.EphemeralContainers = []core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- Name: "debugger1",
- Image: "debian",
- ImagePullPolicy: "IfNotPresent",
- TerminationMessagePolicy: "File",
- },
- }}
- return p
- }(),
- func() *core.Pod {
- p := makePod(nil)
- p.Spec.NodeName = "some-name"
- p.ObjectMeta.Annotations = map[string]string{
- core.MirrorPodAnnotationKey: "foo",
- }
- return p
- }(),
- "Forbidden: static pods do not support ephemeral containers",
- },
+ }}
+ return p
+ }(),
+ func() *core.Pod {
+ p := makePod(nil)
+ p.Spec.NodeName = "some-name"
+ p.ObjectMeta.Annotations = map[string]string{
+ core.MirrorPodAnnotationKey: "foo",
+ }
+ return p
+ }(),
+ "Forbidden: static pods do not support ephemeral containers",
+ },
}
for _, tc := range tests {
@@ -14883,669 +13789,591 @@ func TestValidateServiceCreate(t *testing.T) {
tweakSvc func(svc *core.Service) // given a basic valid service, each test case can customize it
numErrs int
featureGates []featuregate.Feature
- }{
- {
- name: "missing namespace",
- tweakSvc: func(s *core.Service) {
- s.Namespace = ""
- },
- numErrs: 1,
- },
- {
- name: "invalid namespace",
- tweakSvc: func(s *core.Service) {
- s.Namespace = "-123"
- },
- numErrs: 1,
- },
- {
- name: "missing name",
- tweakSvc: func(s *core.Service) {
- s.Name = ""
- },
- numErrs: 1,
- },
- {
- name: "invalid name",
- tweakSvc: func(s *core.Service) {
- s.Name = "-123"
- },
- numErrs: 1,
- },
- {
- name: "too long name",
- tweakSvc: func(s *core.Service) {
- s.Name = strings.Repeat("a", 64)
- },
- numErrs: 1,
- },
- {
- name: "invalid generateName",
- tweakSvc: func(s *core.Service) {
- s.GenerateName = "-123"
- },
- numErrs: 1,
- },
- {
- name: "too long generateName",
- tweakSvc: func(s *core.Service) {
- s.GenerateName = strings.Repeat("a", 64)
- },
- numErrs: 1,
- },
- {
- name: "invalid label",
- tweakSvc: func(s *core.Service) {
- s.Labels["NoUppercaseOrSpecialCharsLike=Equals"] = "bar"
- },
- numErrs: 1,
- },
- {
- name: "invalid annotation",
- tweakSvc: func(s *core.Service) {
- s.Annotations["NoSpecialCharsLike=Equals"] = "bar"
- },
- numErrs: 1,
- },
- {
- name: "nil selector",
- tweakSvc: func(s *core.Service) {
- s.Spec.Selector = nil
- },
- numErrs: 0,
- },
- {
- name: "invalid selector",
- tweakSvc: func(s *core.Service) {
- s.Spec.Selector["NoSpecialCharsLike=Equals"] = "bar"
- },
- numErrs: 1,
- },
- {
- name: "missing session affinity",
- tweakSvc: func(s *core.Service) {
- s.Spec.SessionAffinity = ""
- },
- numErrs: 1,
- },
- {
- name: "missing type",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = ""
- },
- numErrs: 1,
- },
- {
- name: "missing ports",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports = nil
- },
- numErrs: 1,
- },
- {
- name: "missing ports but headless",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports = nil
- s.Spec.ClusterIP = core.ClusterIPNone
- s.Spec.ClusterIPs = []string{core.ClusterIPNone}
- },
- numErrs: 0,
- },
- {
- name: "empty port[0] name",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Name = ""
- },
- numErrs: 0,
- },
- {
- name: "empty port[1] name",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 1,
- },
- {
- name: "empty multi-port port[0] name",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Name = ""
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 1,
- },
- {
- name: "invalid port name",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Name = "INVALID"
- },
- numErrs: 1,
- },
- {
- name: "missing protocol",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Protocol = ""
- },
- numErrs: 1,
- },
- {
- name: "invalid protocol",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Protocol = "INVALID"
- },
- numErrs: 1,
- },
- {
- name: "invalid cluster ip",
- tweakSvc: func(s *core.Service) {
- s.Spec.ClusterIP = "invalid"
- s.Spec.ClusterIPs = []string{"invalid"}
- },
- numErrs: 1,
- },
- {
- name: "missing port",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Port = 0
- },
- numErrs: 1,
- },
- {
- name: "invalid port",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Port = 65536
- },
- numErrs: 1,
- },
- {
- name: "invalid TargetPort int",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].TargetPort = intstr.FromInt32(65536)
- },
- numErrs: 1,
- },
- {
- name: "valid port headless",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Port = 11722
- s.Spec.Ports[0].TargetPort = intstr.FromInt32(11722)
- s.Spec.ClusterIP = core.ClusterIPNone
- s.Spec.ClusterIPs = []string{core.ClusterIPNone}
- },
- numErrs: 0,
- },
- {
- name: "invalid port headless 1",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Port = 11722
- s.Spec.Ports[0].TargetPort = intstr.FromInt32(11721)
- s.Spec.ClusterIP = core.ClusterIPNone
- s.Spec.ClusterIPs = []string{core.ClusterIPNone}
- },
- // in the v1 API, targetPorts on headless services were tolerated.
- // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
- // numErrs: 1,
- numErrs: 0,
- },
- {
- name: "invalid port headless 2",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Port = 11722
- s.Spec.Ports[0].TargetPort = intstr.FromString("target")
- s.Spec.ClusterIP = core.ClusterIPNone
- s.Spec.ClusterIPs = []string{core.ClusterIPNone}
- },
- // in the v1 API, targetPorts on headless services were tolerated.
- // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
- // numErrs: 1,
- numErrs: 0,
- },
- {
- name: "invalid publicIPs localhost",
- tweakSvc: func(s *core.Service) {
- s.Spec.ExternalIPs = []string{"127.0.0.1"}
- },
- numErrs: 1,
- },
- {
- name: "invalid publicIPs unspecified",
- tweakSvc: func(s *core.Service) {
- s.Spec.ExternalIPs = []string{"0.0.0.0"}
- },
- numErrs: 1,
- },
- {
- name: "invalid publicIPs loopback",
- tweakSvc: func(s *core.Service) {
- s.Spec.ExternalIPs = []string{"127.0.0.1"}
- },
- numErrs: 1,
- },
- {
- name: "invalid publicIPs host",
- tweakSvc: func(s *core.Service) {
- s.Spec.ExternalIPs = []string{"myhost.mydomain"}
- },
- numErrs: 1,
- },
- {
- name: "dup port name",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Name = "p"
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 1,
- },
- {
- name: "valid load balancer protocol UDP 1",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.Ports[0].Protocol = "UDP"
- },
- numErrs: 0,
- },
- {
- name: "valid load balancer protocol UDP 2",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.Ports[0] = core.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt32(12345)}
- },
- numErrs: 0,
- },
- {
- name: "load balancer with mix protocol",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 0,
- },
- {
- name: "valid 1",
- tweakSvc: func(s *core.Service) {
- // do nothing
- },
- numErrs: 0,
- },
- {
- name: "valid 2",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].Protocol = "UDP"
- s.Spec.Ports[0].TargetPort = intstr.FromInt32(12345)
- },
- numErrs: 0,
- },
- {
- name: "valid 3",
- tweakSvc: func(s *core.Service) {
- s.Spec.Ports[0].TargetPort = intstr.FromString("http")
- },
- numErrs: 0,
- },
- {
- name: "valid cluster ip - none ",
- tweakSvc: func(s *core.Service) {
- s.Spec.ClusterIP = core.ClusterIPNone
- s.Spec.ClusterIPs = []string{core.ClusterIPNone}
- },
- numErrs: 0,
- },
- {
- name: "valid cluster ip - empty",
- tweakSvc: func(s *core.Service) {
- s.Spec.ClusterIPs = nil
- s.Spec.Ports[0].TargetPort = intstr.FromString("http")
- },
- numErrs: 0,
- },
- {
- name: "valid type - clusterIP",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeClusterIP
- },
- numErrs: 0,
- },
- {
- name: "valid type - loadbalancer",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- },
- numErrs: 0,
- },
- {
- name: "valid type - loadbalancer with allocateLoadBalancerNodePorts=false",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(false)
- },
- numErrs: 0,
- },
- {
- name: "invalid type - missing AllocateLoadBalancerNodePorts for loadbalancer type",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- },
- numErrs: 1,
- },
- {
- name: "valid type loadbalancer 2 ports",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 0,
- },
- {
- name: "valid external load balancer 2 ports",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 0,
- },
- {
- name: "duplicate nodeports",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(1)})
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(2)})
- },
- numErrs: 1,
- },
- {
- name: "duplicate nodeports (different protocols)",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(1)})
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 2, Protocol: "UDP", NodePort: 1, TargetPort: intstr.FromInt32(2)})
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 3, Protocol: "SCTP", NodePort: 1, TargetPort: intstr.FromInt32(3)})
- },
- numErrs: 0,
- },
- {
- name: "invalid duplicate ports (with same protocol)",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeClusterIP
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)})
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(80)})
- },
- numErrs: 1,
- },
- {
- name: "valid duplicate ports (with different protocols)",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeClusterIP
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)})
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt32(80)})
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 12345, Protocol: "SCTP", TargetPort: intstr.FromInt32(8088)})
- },
- numErrs: 0,
- },
- {
- name: "valid type - cluster",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeClusterIP
- },
- numErrs: 0,
- },
- {
- name: "valid type - nodeport",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- },
- numErrs: 0,
- },
- {
- name: "valid type - loadbalancer with allocateLoadBalancerNodePorts=true",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- },
- numErrs: 0,
- },
- {
- name: "valid type loadbalancer 2 ports",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 0,
- },
- {
- name: "valid type loadbalancer with NodePort",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 0,
- },
- {
- name: "valid type=NodePort service with NodePort",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 0,
- },
- {
- name: "valid type=NodePort service without NodePort",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 0,
- },
- {
- name: "valid cluster service without NodePort",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeClusterIP
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 0,
- },
- {
- name: "invalid cluster service with NodePort",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeClusterIP
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 1,
- },
- {
- name: "invalid public service with duplicate NodePort",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p1", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(1)})
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p2", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(2)})
- },
- numErrs: 1,
- },
- {
- name: "valid type=LoadBalancer",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 0,
- },
- {
- // For now we open firewalls, and its insecure if we open 10250, remove this
- // when we have better protections in place.
- name: "invalid port type=LoadBalancer",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "kubelet", Port: 10250, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
- },
- numErrs: 1,
- },
- {
- name: "valid LoadBalancer source range annotation",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "1.2.3.4/8, 5.6.7.8/16"
- },
- numErrs: 0,
- },
- {
- name: "empty LoadBalancer source range annotation",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = ""
- },
- numErrs: 0,
- },
- {
- name: "invalid LoadBalancer source range annotation (hostname)",
- tweakSvc: func(s *core.Service) {
- s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "foo.bar"
- },
- numErrs: 2,
- },
- {
- name: "invalid LoadBalancer source range annotation (invalid CIDR)",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "1.2.3.4/33"
- },
- numErrs: 1,
- },
- {
- name: "invalid source range for non LoadBalancer type service",
- tweakSvc: func(s *core.Service) {
- s.Spec.LoadBalancerSourceRanges = []string{"1.2.3.4/8", "5.6.7.8/16"}
- },
- numErrs: 1,
- },
- {
- name: "valid LoadBalancer source range",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.LoadBalancerSourceRanges = []string{"1.2.3.4/8", "5.6.7.8/16"}
- },
- numErrs: 0,
- },
- {
- name: "empty LoadBalancer source range",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.LoadBalancerSourceRanges = []string{" "}
- },
- numErrs: 1,
- },
- {
- name: "invalid LoadBalancer source range",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.LoadBalancerSourceRanges = []string{"foo.bar"}
- },
- numErrs: 1,
- },
- {
- name: "valid ExternalName",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeExternalName
- s.Spec.ExternalName = "foo.bar.example.com"
- },
- numErrs: 0,
- },
- {
- name: "valid ExternalName (trailing dot)",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeExternalName
- s.Spec.ExternalName = "foo.bar.example.com."
- },
- numErrs: 0,
- },
- {
- name: "invalid ExternalName clusterIP (valid IP)",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeExternalName
- s.Spec.ClusterIP = "1.2.3.4"
- s.Spec.ClusterIPs = []string{"1.2.3.4"}
- s.Spec.ExternalName = "foo.bar.example.com"
- },
- numErrs: 1,
- },
- {
- name: "invalid ExternalName clusterIP (None)",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeExternalName
- s.Spec.ClusterIP = "None"
- s.Spec.ClusterIPs = []string{"None"}
- s.Spec.ExternalName = "foo.bar.example.com"
- },
- numErrs: 1,
- },
- {
- name: "invalid ExternalName (not a DNS name)",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeExternalName
- s.Spec.ExternalName = "-123"
- },
- numErrs: 1,
- },
- {
- name: "LoadBalancer type cannot have None ClusterIP",
- tweakSvc: func(s *core.Service) {
- s.Spec.ClusterIP = "None"
- s.Spec.ClusterIPs = []string{"None"}
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- },
- numErrs: 1,
- },
- {
- name: "invalid node port with clusterIP None",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(1)})
- s.Spec.ClusterIP = "None"
- s.Spec.ClusterIPs = []string{"None"}
- },
- numErrs: 1,
- },
+ }{{
+ name: "missing namespace",
+ tweakSvc: func(s *core.Service) {
+ s.Namespace = ""
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid namespace",
+ tweakSvc: func(s *core.Service) {
+ s.Namespace = "-123"
+ },
+ numErrs: 1,
+ }, {
+ name: "missing name",
+ tweakSvc: func(s *core.Service) {
+ s.Name = ""
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid name",
+ tweakSvc: func(s *core.Service) {
+ s.Name = "-123"
+ },
+ numErrs: 1,
+ }, {
+ name: "too long name",
+ tweakSvc: func(s *core.Service) {
+ s.Name = strings.Repeat("a", 64)
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid generateName",
+ tweakSvc: func(s *core.Service) {
+ s.GenerateName = "-123"
+ },
+ numErrs: 1,
+ }, {
+ name: "too long generateName",
+ tweakSvc: func(s *core.Service) {
+ s.GenerateName = strings.Repeat("a", 64)
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid label",
+ tweakSvc: func(s *core.Service) {
+ s.Labels["NoUppercaseOrSpecialCharsLike=Equals"] = "bar"
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid annotation",
+ tweakSvc: func(s *core.Service) {
+ s.Annotations["NoSpecialCharsLike=Equals"] = "bar"
+ },
+ numErrs: 1,
+ }, {
+ name: "nil selector",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Selector = nil
+ },
+ numErrs: 0,
+ }, {
+ name: "invalid selector",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Selector["NoSpecialCharsLike=Equals"] = "bar"
+ },
+ numErrs: 1,
+ }, {
+ name: "missing session affinity",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.SessionAffinity = ""
+ },
+ numErrs: 1,
+ }, {
+ name: "missing type",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = ""
+ },
+ numErrs: 1,
+ }, {
+ name: "missing ports",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports = nil
+ },
+ numErrs: 1,
+ }, {
+ name: "missing ports but headless",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports = nil
+ s.Spec.ClusterIP = core.ClusterIPNone
+ s.Spec.ClusterIPs = []string{core.ClusterIPNone}
+ },
+ numErrs: 0,
+ }, {
+ name: "empty port[0] name",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Name = ""
+ },
+ numErrs: 0,
+ }, {
+ name: "empty port[1] name",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 1,
+ }, {
+ name: "empty multi-port port[0] name",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Name = ""
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p", Protocol: "TCP", Port: 12345, TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid port name",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Name = "INVALID"
+ },
+ numErrs: 1,
+ }, {
+ name: "missing protocol",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Protocol = ""
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid protocol",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Protocol = "INVALID"
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid cluster ip",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.ClusterIP = "invalid"
+ s.Spec.ClusterIPs = []string{"invalid"}
+ },
+ numErrs: 1,
+ }, {
+ name: "missing port",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Port = 0
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid port",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Port = 65536
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid TargetPort int",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].TargetPort = intstr.FromInt32(65536)
+ },
+ numErrs: 1,
+ }, {
+ name: "valid port headless",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Port = 11722
+ s.Spec.Ports[0].TargetPort = intstr.FromInt32(11722)
+ s.Spec.ClusterIP = core.ClusterIPNone
+ s.Spec.ClusterIPs = []string{core.ClusterIPNone}
+ },
+ numErrs: 0,
+ }, {
+ name: "invalid port headless 1",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Port = 11722
+ s.Spec.Ports[0].TargetPort = intstr.FromInt32(11721)
+ s.Spec.ClusterIP = core.ClusterIPNone
+ s.Spec.ClusterIPs = []string{core.ClusterIPNone}
+ },
+ // in the v1 API, targetPorts on headless services were tolerated.
+ // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
+ // numErrs: 1,
+ numErrs: 0,
+ }, {
+ name: "invalid port headless 2",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Port = 11722
+ s.Spec.Ports[0].TargetPort = intstr.FromString("target")
+ s.Spec.ClusterIP = core.ClusterIPNone
+ s.Spec.ClusterIPs = []string{core.ClusterIPNone}
+ },
+ // in the v1 API, targetPorts on headless services were tolerated.
+ // once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
+ // numErrs: 1,
+ numErrs: 0,
+ }, {
+ name: "invalid publicIPs localhost",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.ExternalIPs = []string{"127.0.0.1"}
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid publicIPs unspecified",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.ExternalIPs = []string{"0.0.0.0"}
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid publicIPs loopback",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.ExternalIPs = []string{"127.0.0.1"}
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid publicIPs host",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.ExternalIPs = []string{"myhost.mydomain"}
+ },
+ numErrs: 1,
+ }, {
+ name: "dup port name",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Name = "p"
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 1,
+ }, {
+ name: "valid load balancer protocol UDP 1",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.Ports[0].Protocol = "UDP"
+ },
+ numErrs: 0,
+ }, {
+ name: "valid load balancer protocol UDP 2",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.Ports[0] = core.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt32(12345)}
+ },
+ numErrs: 0,
+ }, {
+ name: "load balancer with mix protocol",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 0,
+ }, {
+ name: "valid 1",
+ tweakSvc: func(s *core.Service) {
+ // do nothing
+ },
+ numErrs: 0,
+ }, {
+ name: "valid 2",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].Protocol = "UDP"
+ s.Spec.Ports[0].TargetPort = intstr.FromInt32(12345)
+ },
+ numErrs: 0,
+ }, {
+ name: "valid 3",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Ports[0].TargetPort = intstr.FromString("http")
+ },
+ numErrs: 0,
+ }, {
+ name: "valid cluster ip - none ",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.ClusterIP = core.ClusterIPNone
+ s.Spec.ClusterIPs = []string{core.ClusterIPNone}
+ },
+ numErrs: 0,
+ }, {
+ name: "valid cluster ip - empty",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.ClusterIPs = nil
+ s.Spec.Ports[0].TargetPort = intstr.FromString("http")
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type - clusterIP",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeClusterIP
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type - loadbalancer",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type - loadbalancer with allocateLoadBalancerNodePorts=false",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(false)
+ },
+ numErrs: 0,
+ }, {
+ name: "invalid type - missing AllocateLoadBalancerNodePorts for loadbalancer type",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ },
+ numErrs: 1,
+ }, {
+ name: "valid type loadbalancer 2 ports",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 0,
+ }, {
+ name: "valid external load balancer 2 ports",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 0,
+ }, {
+ name: "duplicate nodeports",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(1)})
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(2)})
+ },
+ numErrs: 1,
+ }, {
+ name: "duplicate nodeports (different protocols)",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(1)})
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 2, Protocol: "UDP", NodePort: 1, TargetPort: intstr.FromInt32(2)})
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 3, Protocol: "SCTP", NodePort: 1, TargetPort: intstr.FromInt32(3)})
+ },
+ numErrs: 0,
+ }, {
+ name: "invalid duplicate ports (with same protocol)",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeClusterIP
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)})
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(80)})
+ },
+ numErrs: 1,
+ }, {
+ name: "valid duplicate ports (with different protocols)",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeClusterIP
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(8080)})
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt32(80)})
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 12345, Protocol: "SCTP", TargetPort: intstr.FromInt32(8088)})
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type - cluster",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeClusterIP
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type - nodeport",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type - loadbalancer with allocateLoadBalancerNodePorts=true",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type loadbalancer 2 ports",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type loadbalancer with NodePort",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type=NodePort service with NodePort",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 0,
+ }, {
+ name: "valid type=NodePort service without NodePort",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 0,
+ }, {
+ name: "valid cluster service without NodePort",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeClusterIP
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 0,
+ }, {
+ name: "invalid cluster service with NodePort",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeClusterIP
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", NodePort: 12345, TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid public service with duplicate NodePort",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p1", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(1)})
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "p2", Port: 2, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(2)})
+ },
+ numErrs: 1,
+ }, {
+ name: "valid type=LoadBalancer",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 0,
+ }, {
+ // For now we open firewalls, and its insecure if we open 10250, remove this
+ // when we have better protections in place.
+ name: "invalid port type=LoadBalancer",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "kubelet", Port: 10250, Protocol: "TCP", TargetPort: intstr.FromInt32(12345)})
+ },
+ numErrs: 1,
+ }, {
+ name: "valid LoadBalancer source range annotation",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "1.2.3.4/8, 5.6.7.8/16"
+ },
+ numErrs: 0,
+ }, {
+ name: "empty LoadBalancer source range annotation",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = ""
+ },
+ numErrs: 0,
+ }, {
+ name: "invalid LoadBalancer source range annotation (hostname)",
+ tweakSvc: func(s *core.Service) {
+ s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "foo.bar"
+ },
+ numErrs: 2,
+ }, {
+ name: "invalid LoadBalancer source range annotation (invalid CIDR)",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Annotations[core.AnnotationLoadBalancerSourceRangesKey] = "1.2.3.4/33"
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid source range for non LoadBalancer type service",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.LoadBalancerSourceRanges = []string{"1.2.3.4/8", "5.6.7.8/16"}
+ },
+ numErrs: 1,
+ }, {
+ name: "valid LoadBalancer source range",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.LoadBalancerSourceRanges = []string{"1.2.3.4/8", "5.6.7.8/16"}
+ },
+ numErrs: 0,
+ }, {
+ name: "empty LoadBalancer source range",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.LoadBalancerSourceRanges = []string{" "}
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid LoadBalancer source range",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.LoadBalancerSourceRanges = []string{"foo.bar"}
+ },
+ numErrs: 1,
+ }, {
+ name: "valid ExternalName",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeExternalName
+ s.Spec.ExternalName = "foo.bar.example.com"
+ },
+ numErrs: 0,
+ }, {
+ name: "valid ExternalName (trailing dot)",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeExternalName
+ s.Spec.ExternalName = "foo.bar.example.com."
+ },
+ numErrs: 0,
+ }, {
+ name: "invalid ExternalName clusterIP (valid IP)",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeExternalName
+ s.Spec.ClusterIP = "1.2.3.4"
+ s.Spec.ClusterIPs = []string{"1.2.3.4"}
+ s.Spec.ExternalName = "foo.bar.example.com"
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid ExternalName clusterIP (None)",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeExternalName
+ s.Spec.ClusterIP = "None"
+ s.Spec.ClusterIPs = []string{"None"}
+ s.Spec.ExternalName = "foo.bar.example.com"
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid ExternalName (not a DNS name)",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeExternalName
+ s.Spec.ExternalName = "-123"
+ },
+ numErrs: 1,
+ }, {
+ name: "LoadBalancer type cannot have None ClusterIP",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.ClusterIP = "None"
+ s.Spec.ClusterIPs = []string{"None"}
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ },
+ numErrs: 1,
+ }, {
+ name: "invalid node port with clusterIP None",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt32(1)})
+ s.Spec.ClusterIP = "None"
+ s.Spec.ClusterIPs = []string{"None"}
+ },
+ numErrs: 1,
+ },
// ESIPP section begins.
{
name: "invalid externalTraffic field",
@@ -15555,15 +14383,13 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ExternalTrafficPolicy = "invalid"
},
numErrs: 1,
- },
- {
+ }, {
name: "nil internalTraffic field when feature gate is on",
tweakSvc: func(s *core.Service) {
s.Spec.InternalTrafficPolicy = nil
},
numErrs: 1,
- },
- {
+ }, {
name: "internalTrafficPolicy field nil when type is ExternalName",
tweakSvc: func(s *core.Service) {
s.Spec.InternalTrafficPolicy = nil
@@ -15571,8 +14397,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ExternalName = "foo.bar.com"
},
numErrs: 0,
- },
- {
+ }, {
// Typically this should fail validation, but in v1.22 we have existing clusters
// that may have allowed internalTrafficPolicy when Type=ExternalName.
// This test case ensures we don't break compatibility for internalTrafficPolicy
@@ -15585,32 +14410,28 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ExternalName = "foo.bar.com"
},
numErrs: 0,
- },
- {
+ }, {
name: "invalid internalTraffic field",
tweakSvc: func(s *core.Service) {
invalid := core.ServiceInternalTrafficPolicy("invalid")
s.Spec.InternalTrafficPolicy = &invalid
},
numErrs: 1,
- },
- {
+ }, {
name: "internalTrafficPolicy field set to Cluster",
tweakSvc: func(s *core.Service) {
cluster := core.ServiceInternalTrafficPolicyCluster
s.Spec.InternalTrafficPolicy = &cluster
},
numErrs: 0,
- },
- {
+ }, {
name: "internalTrafficPolicy field set to Local",
tweakSvc: func(s *core.Service) {
local := core.ServiceInternalTrafficPolicyLocal
s.Spec.InternalTrafficPolicy = &local
},
numErrs: 0,
- },
- {
+ }, {
name: "negative healthCheckNodePort field",
tweakSvc: func(s *core.Service) {
s.Spec.Type = core.ServiceTypeLoadBalancer
@@ -15619,8 +14440,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.HealthCheckNodePort = -1
},
numErrs: 1,
- },
- {
+ }, {
name: "negative healthCheckNodePort field",
tweakSvc: func(s *core.Service) {
s.Spec.Type = core.ServiceTypeLoadBalancer
@@ -15643,8 +14463,7 @@ func TestValidateServiceCreate(t *testing.T) {
}
},
numErrs: 1,
- },
- {
+ }, {
name: "sessionAffinityConfig can't be set when session affinity is None",
tweakSvc: func(s *core.Service) {
s.Spec.Type = core.ServiceTypeLoadBalancer
@@ -15667,8 +14486,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{invalidServiceIPFamily}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, service with invalid ipFamilies (2nd)",
tweakSvc: func(s *core.Service) {
invalidServiceIPFamily := core.IPFamily("not-a-valid-ip-family")
@@ -15676,16 +14494,14 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, invalidServiceIPFamily}
},
numErrs: 1,
- },
- {
+ }, {
name: "IPFamilyPolicy(singleStack) is set for two families",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &singleStack
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 0, // this validated in alloc code.
- },
- {
+ }, {
name: "valid, IPFamilyPolicy(preferDualStack) is set for two families (note: alloc sets families)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &preferDualStack
@@ -15701,53 +14517,46 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol, core.IPv4Protocol}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, service with same ip families",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
s.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol, core.IPv6Protocol}
},
numErrs: 1,
- },
- {
+ }, {
name: "valid, nil service ipFamilies",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilies = nil
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, service with valid ipFamilies (v4)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, service with valid ipFamilies (v6)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, service with valid ipFamilies(v4,v6)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, service with valid ipFamilies(v6,v4)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
s.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol, core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, service preferred dual stack with single family",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &preferDualStack
@@ -15763,8 +14572,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"garbage-ip"}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, garbage ips",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15772,8 +14580,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"garbage-ip", "garbage-second-ip"}
},
numErrs: 2,
- },
- {
+ }, {
name: "invalid, garbage first ip",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15781,8 +14588,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"garbage-ip", "2001::1"}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, garbage second ip",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15790,8 +14596,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"2001::1", "garbage-ip"}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, NONE + IP",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15799,8 +14604,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"None", "2001::1"}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, IP + NONE",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15808,8 +14612,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"2001::1", "None"}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, EMPTY STRING + IP",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15817,8 +14620,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"", "2001::1"}
},
numErrs: 2,
- },
- {
+ }, {
name: "invalid, IP + EMPTY STRING",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15826,8 +14628,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"2001::1", ""}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, same ip family (v6)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15836,8 +14637,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 2,
- },
- {
+ }, {
name: "invalid, same ip family (v4)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15847,8 +14647,7 @@ func TestValidateServiceCreate(t *testing.T) {
},
numErrs: 2,
- },
- {
+ }, {
name: "invalid, more than two ips",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15857,8 +14656,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 1,
- },
- {
+ }, {
name: " multi ip, dualstack not set (request for downgrade)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &singleStack
@@ -15867,8 +14665,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, headless-no-selector + multi family + gate off",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15878,8 +14675,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.Selector = nil
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, multi ip, single ipfamilies preferDualStack",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &preferDualStack
@@ -15899,8 +14695,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "invalid, families don't match (v4=>v6)",
tweakSvc: func(s *core.Service) {
s.Spec.ClusterIP = "10.0.0.1"
@@ -15908,8 +14703,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, families don't match (v6=>v4)",
tweakSvc: func(s *core.Service) {
s.Spec.ClusterIP = "2001::1"
@@ -15917,8 +14711,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 1,
- },
- {
+ }, {
name: "valid. no field set",
tweakSvc: func(s *core.Service) {
},
@@ -15933,8 +14726,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"10.0.0.1"}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, single family",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &singleStack
@@ -15942,8 +14734,7 @@ func TestValidateServiceCreate(t *testing.T) {
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, single ip + single family",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &singleStack
@@ -15953,8 +14744,7 @@ func TestValidateServiceCreate(t *testing.T) {
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, single ip + single family (dual stack requested)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &preferDualStack
@@ -15964,8 +14754,7 @@ func TestValidateServiceCreate(t *testing.T) {
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, single ip, multi ipfamilies",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15974,8 +14763,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, multi ips, multi ipfamilies (4,6)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15984,8 +14772,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, ips, multi ipfamilies (6,4)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -15994,8 +14781,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol, core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, multi ips (6,4)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -16003,16 +14789,14 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"2001::1", "10.0.0.1"}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, multi ipfamilies (6,4)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
s.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol, core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, multi ips (4,6)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -16020,16 +14804,14 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"10.0.0.1", "2001::1"}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, multi ipfamilies (4,6)",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
s.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid, dual stack",
tweakSvc: func(s *core.Service) {
s.Spec.IPFamilyPolicy = &requireDualStack
@@ -16048,8 +14830,7 @@ func TestValidateServiceCreate(t *testing.T) {
}}
},
numErrs: 0,
- },
- {
+ }, {
name: `valid custom appProtocol`,
tweakSvc: func(s *core.Service) {
s.Spec.Ports = []core.ServicePort{{
@@ -16060,8 +14841,7 @@ func TestValidateServiceCreate(t *testing.T) {
}}
},
numErrs: 0,
- },
- {
+ }, {
name: `invalid appProtocol`,
tweakSvc: func(s *core.Service) {
s.Spec.Ports = []core.ServicePort{{
@@ -16082,23 +14862,20 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.ClusterIPs = []string{"10.0.0.1", "2001::1"}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid cluster ip != clusterIP in single ip service",
tweakSvc: func(s *core.Service) {
s.Spec.ClusterIP = "10.0.0.10"
s.Spec.ClusterIPs = []string{"10.0.0.1"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Use AllocateLoadBalancerNodePorts when type is not LoadBalancer",
tweakSvc: func(s *core.Service) {
s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
},
numErrs: 1,
- },
- {
+ }, {
name: "valid LoadBalancerClass when type is LoadBalancer",
tweakSvc: func(s *core.Service) {
s.Spec.Type = core.ServiceTypeLoadBalancer
@@ -16107,8 +14884,7 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.LoadBalancerClass = utilpointer.String("test.com/test-load-balancer-class")
},
numErrs: 0,
- },
- {
+ }, {
name: "invalid LoadBalancerClass",
tweakSvc: func(s *core.Service) {
s.Spec.Type = core.ServiceTypeLoadBalancer
@@ -16117,16 +14893,14 @@ func TestValidateServiceCreate(t *testing.T) {
s.Spec.LoadBalancerClass = utilpointer.String("Bad/LoadBalancerClass")
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid: set LoadBalancerClass when type is not LoadBalancer",
tweakSvc: func(s *core.Service) {
s.Spec.Type = core.ServiceTypeClusterIP
s.Spec.LoadBalancerClass = utilpointer.String("test.com/test-load-balancer-class")
},
numErrs: 1,
- },
- {
+ }, {
name: "topology annotations are mismatched",
tweakSvc: func(s *core.Service) {
s.Annotations[core.DeprecatedAnnotationTopologyAwareHints] = "original"
@@ -16156,74 +14930,66 @@ func TestValidateServiceExternalTrafficPolicy(t *testing.T) {
name string
tweakSvc func(svc *core.Service) // Given a basic valid service, each test case can customize it.
numErrs int
- }{
- {
- name: "valid loadBalancer service with externalTrafficPolicy and healthCheckNodePort set",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyLocal
- s.Spec.HealthCheckNodePort = 34567
- },
- numErrs: 0,
+ }{{
+ name: "valid loadBalancer service with externalTrafficPolicy and healthCheckNodePort set",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyLocal
+ s.Spec.HealthCheckNodePort = 34567
},
- {
- name: "valid nodePort service with externalTrafficPolicy set",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyLocal
- },
- numErrs: 0,
+ numErrs: 0,
+ }, {
+ name: "valid nodePort service with externalTrafficPolicy set",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyLocal
},
- {
- name: "valid clusterIP service with none of externalTrafficPolicy and healthCheckNodePort set",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeClusterIP
- },
- numErrs: 0,
+ numErrs: 0,
+ }, {
+ name: "valid clusterIP service with none of externalTrafficPolicy and healthCheckNodePort set",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeClusterIP
},
- {
- name: "cannot set healthCheckNodePort field on loadBalancer service with externalTrafficPolicy!=Local",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- s.Spec.HealthCheckNodePort = 34567
- },
- numErrs: 1,
+ numErrs: 0,
+ }, {
+ name: "cannot set healthCheckNodePort field on loadBalancer service with externalTrafficPolicy!=Local",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
+ s.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ s.Spec.HealthCheckNodePort = 34567
},
- {
- name: "cannot set healthCheckNodePort field on nodePort service",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyLocal
- s.Spec.HealthCheckNodePort = 34567
- },
- numErrs: 1,
+ numErrs: 1,
+ }, {
+ name: "cannot set healthCheckNodePort field on nodePort service",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyLocal
+ s.Spec.HealthCheckNodePort = 34567
},
- {
- name: "cannot set externalTrafficPolicy or healthCheckNodePort fields on clusterIP service",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeClusterIP
- s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyLocal
- s.Spec.HealthCheckNodePort = 34567
- },
- numErrs: 2,
+ numErrs: 1,
+ }, {
+ name: "cannot set externalTrafficPolicy or healthCheckNodePort fields on clusterIP service",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeClusterIP
+ s.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyLocal
+ s.Spec.HealthCheckNodePort = 34567
},
- {
- name: "externalTrafficPolicy is required on NodePort service",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeNodePort
- },
- numErrs: 1,
+ numErrs: 2,
+ }, {
+ name: "externalTrafficPolicy is required on NodePort service",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeNodePort
},
- {
- name: "externalTrafficPolicy is required on LoadBalancer service",
- tweakSvc: func(s *core.Service) {
- s.Spec.Type = core.ServiceTypeLoadBalancer
- },
- numErrs: 1,
+ numErrs: 1,
+ }, {
+ name: "externalTrafficPolicy is required on LoadBalancer service",
+ tweakSvc: func(s *core.Service) {
+ s.Spec.Type = core.ServiceTypeLoadBalancer
},
+ numErrs: 1,
+ },
}
for _, tc := range testCases {
@@ -16247,97 +15013,87 @@ func TestValidateReplicationControllerStatus(t *testing.T) {
observedGeneration int64
expectedErr bool
- }{
- {
- name: "valid status",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 2,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: false,
- },
- {
- name: "invalid replicas",
- replicas: -1,
- fullyLabeledReplicas: 3,
- readyReplicas: 2,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid fullyLabeledReplicas",
- replicas: 3,
- fullyLabeledReplicas: -1,
- readyReplicas: 2,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid readyReplicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: -1,
- availableReplicas: 1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid availableReplicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 3,
- availableReplicas: -1,
- observedGeneration: 2,
- expectedErr: true,
- },
- {
- name: "invalid observedGeneration",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 3,
- availableReplicas: 3,
- observedGeneration: -1,
- expectedErr: true,
- },
- {
- name: "fullyLabeledReplicas greater than replicas",
- replicas: 3,
- fullyLabeledReplicas: 4,
- readyReplicas: 3,
- availableReplicas: 3,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "readyReplicas greater than replicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 4,
- availableReplicas: 3,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "availableReplicas greater than replicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 3,
- availableReplicas: 4,
- observedGeneration: 1,
- expectedErr: true,
- },
- {
- name: "availableReplicas greater than readyReplicas",
- replicas: 3,
- fullyLabeledReplicas: 3,
- readyReplicas: 2,
- availableReplicas: 3,
- observedGeneration: 1,
- expectedErr: true,
- },
+ }{{
+ name: "valid status",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 2,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: false,
+ }, {
+ name: "invalid replicas",
+ replicas: -1,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 2,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid fullyLabeledReplicas",
+ replicas: 3,
+ fullyLabeledReplicas: -1,
+ readyReplicas: 2,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid readyReplicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: -1,
+ availableReplicas: 1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid availableReplicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 3,
+ availableReplicas: -1,
+ observedGeneration: 2,
+ expectedErr: true,
+ }, {
+ name: "invalid observedGeneration",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 3,
+ availableReplicas: 3,
+ observedGeneration: -1,
+ expectedErr: true,
+ }, {
+ name: "fullyLabeledReplicas greater than replicas",
+ replicas: 3,
+ fullyLabeledReplicas: 4,
+ readyReplicas: 3,
+ availableReplicas: 3,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "readyReplicas greater than replicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 4,
+ availableReplicas: 3,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "availableReplicas greater than replicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 3,
+ availableReplicas: 4,
+ observedGeneration: 1,
+ expectedErr: true,
+ }, {
+ name: "availableReplicas greater than readyReplicas",
+ replicas: 3,
+ fullyLabeledReplicas: 3,
+ readyReplicas: 2,
+ availableReplicas: 3,
+ observedGeneration: 1,
+ expectedErr: true,
+ },
}
for _, test := range tests {
@@ -16373,30 +15129,29 @@ func TestValidateReplicationControllerStatusUpdate(t *testing.T) {
old core.ReplicationController
update core.ReplicationController
}
- successCases := []rcUpdateTest{
- {
- old: core.ReplicationController{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: core.ReplicationControllerSpec{
- Selector: validSelector,
- Template: &validPodTemplate.Template,
- },
- Status: core.ReplicationControllerStatus{
- Replicas: 2,
- },
+ successCases := []rcUpdateTest{{
+ old: core.ReplicationController{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: core.ReplicationControllerSpec{
+ Selector: validSelector,
+ Template: &validPodTemplate.Template,
},
- update: core.ReplicationController{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: core.ReplicationControllerSpec{
- Replicas: 3,
- Selector: validSelector,
- Template: &validPodTemplate.Template,
- },
- Status: core.ReplicationControllerStatus{
- Replicas: 4,
- },
+ Status: core.ReplicationControllerStatus{
+ Replicas: 2,
},
},
+ update: core.ReplicationController{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: core.ReplicationControllerSpec{
+ Replicas: 3,
+ Selector: validSelector,
+ Template: &validPodTemplate.Template,
+ },
+ Status: core.ReplicationControllerStatus{
+ Replicas: 4,
+ },
+ },
+ },
}
for _, successCase := range successCases {
successCase.old.ObjectMeta.ResourceVersion = "1"
@@ -16481,41 +15236,39 @@ func TestValidateReplicationControllerUpdate(t *testing.T) {
old core.ReplicationController
update core.ReplicationController
}
- successCases := []rcUpdateTest{
- {
- old: core.ReplicationController{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: core.ReplicationControllerSpec{
- Selector: validSelector,
- Template: &validPodTemplate.Template,
- },
- },
- update: core.ReplicationController{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: core.ReplicationControllerSpec{
- Replicas: 3,
- Selector: validSelector,
- Template: &validPodTemplate.Template,
- },
+ successCases := []rcUpdateTest{{
+ old: core.ReplicationController{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: core.ReplicationControllerSpec{
+ Selector: validSelector,
+ Template: &validPodTemplate.Template,
},
},
- {
- old: core.ReplicationController{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: core.ReplicationControllerSpec{
- Selector: validSelector,
- Template: &validPodTemplate.Template,
- },
- },
- update: core.ReplicationController{
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: core.ReplicationControllerSpec{
- Replicas: 1,
- Selector: validSelector,
- Template: &readWriteVolumePodTemplate.Template,
- },
+ update: core.ReplicationController{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: core.ReplicationControllerSpec{
+ Replicas: 3,
+ Selector: validSelector,
+ Template: &validPodTemplate.Template,
},
},
+ }, {
+ old: core.ReplicationController{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: core.ReplicationControllerSpec{
+ Selector: validSelector,
+ Template: &validPodTemplate.Template,
+ },
+ },
+ update: core.ReplicationController{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: core.ReplicationControllerSpec{
+ Replicas: 1,
+ Selector: validSelector,
+ Template: &readWriteVolumePodTemplate.Template,
+ },
+ },
+ },
}
for _, successCase := range successCases {
successCase.old.ObjectMeta.ResourceVersion = "1"
@@ -16640,29 +15393,26 @@ func TestValidateReplicationController(t *testing.T) {
},
},
}
- successCases := []core.ReplicationController{
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
- Spec: core.ReplicationControllerSpec{
- Selector: validSelector,
- Template: &validPodTemplate.Template,
- },
+ successCases := []core.ReplicationController{{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
+ Spec: core.ReplicationControllerSpec{
+ Selector: validSelector,
+ Template: &validPodTemplate.Template,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
- Spec: core.ReplicationControllerSpec{
- Selector: validSelector,
- Template: &validPodTemplate.Template,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
+ Spec: core.ReplicationControllerSpec{
+ Selector: validSelector,
+ Template: &validPodTemplate.Template,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
- Spec: core.ReplicationControllerSpec{
- Replicas: 1,
- Selector: validSelector,
- Template: &readWriteVolumePodTemplate.Template,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
+ Spec: core.ReplicationControllerSpec{
+ Replicas: 1,
+ Selector: validSelector,
+ Template: &readWriteVolumePodTemplate.Template,
},
+ },
}
for _, successCase := range successCases {
if errs := ValidateReplicationController(&successCase, PodValidationOptions{}); len(errs) != 0 {
@@ -16841,80 +15591,75 @@ func TestValidateReplicationController(t *testing.T) {
func TestValidateNode(t *testing.T) {
validSelector := map[string]string{"a": "b"}
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
- successCases := []core.Node{
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "abc",
- Labels: validSelector,
+ successCases := []core.Node{{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "abc",
+ Labels: validSelector,
+ },
+ Status: core.NodeStatus{
+ Addresses: []core.NodeAddress{
+ {Type: core.NodeExternalIP, Address: "something"},
},
- Status: core.NodeStatus{
- Addresses: []core.NodeAddress{
- {Type: core.NodeExternalIP, Address: "something"},
- },
- Capacity: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("my.org/gpu"): resource.MustParse("10"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("10Gi"),
- core.ResourceName("hugepages-1Gi"): resource.MustParse("0"),
- },
+ Capacity: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("my.org/gpu"): resource.MustParse("10"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("10Gi"),
+ core.ResourceName("hugepages-1Gi"): resource.MustParse("0"),
},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "abc",
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "abc",
+ },
+ Status: core.NodeStatus{
+ Addresses: []core.NodeAddress{
+ {Type: core.NodeExternalIP, Address: "something"},
},
- Status: core.NodeStatus{
- Addresses: []core.NodeAddress{
- {Type: core.NodeExternalIP, Address: "something"},
- },
- Capacity: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
- },
+ Capacity: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "abc",
- Labels: validSelector,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "abc",
+ Labels: validSelector,
+ },
+ Status: core.NodeStatus{
+ Addresses: []core.NodeAddress{
+ {Type: core.NodeExternalIP, Address: "something"},
},
- Status: core.NodeStatus{
- Addresses: []core.NodeAddress{
- {Type: core.NodeExternalIP, Address: "something"},
- },
- Capacity: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- core.ResourceName("my.org/gpu"): resource.MustParse("10"),
- core.ResourceName("hugepages-2Mi"): resource.MustParse("10Gi"),
- core.ResourceName("hugepages-1Gi"): resource.MustParse("10Gi"),
- },
+ Capacity: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
+ core.ResourceName("my.org/gpu"): resource.MustParse("10"),
+ core.ResourceName("hugepages-2Mi"): resource.MustParse("10Gi"),
+ core.ResourceName("hugepages-1Gi"): resource.MustParse("10Gi"),
},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "dedicated-node1",
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "dedicated-node1",
+ },
+ Status: core.NodeStatus{
+ Addresses: []core.NodeAddress{
+ {Type: core.NodeExternalIP, Address: "something"},
},
- Status: core.NodeStatus{
- Addresses: []core.NodeAddress{
- {Type: core.NodeExternalIP, Address: "something"},
- },
- Capacity: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
- },
- },
- Spec: core.NodeSpec{
- // Add a valid taint to a node
- Taints: []core.Taint{{Key: "GPU", Value: "true", Effect: "NoSchedule"}},
+ Capacity: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "abc",
- Annotations: map[string]string{
- core.PreferAvoidPodsAnnotationKey: `
+ Spec: core.NodeSpec{
+ // Add a valid taint to a node
+ Taints: []core.Taint{{Key: "GPU", Value: "true", Effect: "NoSchedule"}},
+ },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "abc",
+ Annotations: map[string]string{
+ core.PreferAvoidPodsAnnotationKey: `
{
"preferAvoidPods": [
{
@@ -16932,35 +15677,34 @@ func TestValidateNode(t *testing.T) {
}
]
}`,
- },
- },
- Status: core.NodeStatus{
- Addresses: []core.NodeAddress{
- {Type: core.NodeExternalIP, Address: "something"},
- },
- Capacity: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
- },
},
},
- {
- ObjectMeta: metav1.ObjectMeta{
- Name: "abc",
+ Status: core.NodeStatus{
+ Addresses: []core.NodeAddress{
+ {Type: core.NodeExternalIP, Address: "something"},
},
- Status: core.NodeStatus{
- Addresses: []core.NodeAddress{
- {Type: core.NodeExternalIP, Address: "something"},
- },
- Capacity: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
- },
- },
- Spec: core.NodeSpec{
- PodCIDRs: []string{"192.168.0.0/16"},
+ Capacity: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
},
},
+ }, {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "abc",
+ },
+ Status: core.NodeStatus{
+ Addresses: []core.NodeAddress{
+ {Type: core.NodeExternalIP, Address: "something"},
+ },
+ Capacity: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("0"),
+ },
+ },
+ Spec: core.NodeSpec{
+ PodCIDRs: []string{"192.168.0.0/16"},
+ },
+ },
}
for _, successCase := range successCases {
if errs := ValidateNode(&successCase); len(errs) != 0 {
@@ -17639,179 +16383,161 @@ func TestValidateServiceUpdate(t *testing.T) {
name string
tweakSvc func(oldSvc, newSvc *core.Service) // given basic valid services, each test case can customize them
numErrs int
- }{
- {
- name: "no change",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- // do nothing
- },
- numErrs: 0,
+ }{{
+ name: "no change",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ // do nothing
},
- {
- name: "change name",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Name += "2"
- },
- numErrs: 1,
+ numErrs: 0,
+ }, {
+ name: "change name",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Name += "2"
},
- {
- name: "change namespace",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Namespace += "2"
- },
- numErrs: 1,
+ numErrs: 1,
+ }, {
+ name: "change namespace",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Namespace += "2"
},
- {
- name: "change label valid",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Labels["key"] = "other-value"
- },
- numErrs: 0,
+ numErrs: 1,
+ }, {
+ name: "change label valid",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Labels["key"] = "other-value"
},
- {
- name: "add label",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Labels["key2"] = "value2"
- },
- numErrs: 0,
+ numErrs: 0,
+ }, {
+ name: "add label",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Labels["key2"] = "value2"
},
- {
- name: "change cluster IP",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- oldSvc.Spec.ClusterIP = "1.2.3.4"
- oldSvc.Spec.ClusterIPs = []string{"1.2.3.4"}
+ numErrs: 0,
+ }, {
+ name: "change cluster IP",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ oldSvc.Spec.ClusterIP = "1.2.3.4"
+ oldSvc.Spec.ClusterIPs = []string{"1.2.3.4"}
- newSvc.Spec.ClusterIP = "8.6.7.5"
- newSvc.Spec.ClusterIPs = []string{"8.6.7.5"}
- },
- numErrs: 1,
+ newSvc.Spec.ClusterIP = "8.6.7.5"
+ newSvc.Spec.ClusterIPs = []string{"8.6.7.5"}
},
- {
- name: "remove cluster IP",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- oldSvc.Spec.ClusterIP = "1.2.3.4"
- oldSvc.Spec.ClusterIPs = []string{"1.2.3.4"}
+ numErrs: 1,
+ }, {
+ name: "remove cluster IP",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ oldSvc.Spec.ClusterIP = "1.2.3.4"
+ oldSvc.Spec.ClusterIPs = []string{"1.2.3.4"}
- newSvc.Spec.ClusterIP = ""
- newSvc.Spec.ClusterIPs = nil
- },
- numErrs: 1,
+ newSvc.Spec.ClusterIP = ""
+ newSvc.Spec.ClusterIPs = nil
},
- {
- name: "change affinity",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Spec.SessionAffinity = "ClientIP"
- newSvc.Spec.SessionAffinityConfig = &core.SessionAffinityConfig{
- ClientIP: &core.ClientIPConfig{
- TimeoutSeconds: utilpointer.Int32(90),
- },
- }
- },
- numErrs: 0,
+ numErrs: 1,
+ }, {
+ name: "change affinity",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Spec.SessionAffinity = "ClientIP"
+ newSvc.Spec.SessionAffinityConfig = &core.SessionAffinityConfig{
+ ClientIP: &core.ClientIPConfig{
+ TimeoutSeconds: utilpointer.Int32(90),
+ },
+ }
},
- {
- name: "remove affinity",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Spec.SessionAffinity = ""
- },
- numErrs: 1,
+ numErrs: 0,
+ }, {
+ name: "remove affinity",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Spec.SessionAffinity = ""
},
- {
- name: "change type",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Spec.Type = core.ServiceTypeLoadBalancer
- newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- },
- numErrs: 0,
+ numErrs: 1,
+ }, {
+ name: "change type",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Spec.Type = core.ServiceTypeLoadBalancer
+ newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
},
- {
- name: "remove type",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Spec.Type = ""
- },
- numErrs: 1,
+ numErrs: 0,
+ }, {
+ name: "remove type",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Spec.Type = ""
},
- {
- name: "change type -> nodeport",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Spec.Type = core.ServiceTypeNodePort
- newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- },
- numErrs: 0,
+ numErrs: 1,
+ }, {
+ name: "change type -> nodeport",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Spec.Type = core.ServiceTypeNodePort
+ newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
},
- {
- name: "add loadBalancerSourceRanges",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
- oldSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- newSvc.Spec.Type = core.ServiceTypeLoadBalancer
- newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- newSvc.Spec.LoadBalancerSourceRanges = []string{"10.0.0.0/8"}
- },
- numErrs: 0,
+ numErrs: 0,
+ }, {
+ name: "add loadBalancerSourceRanges",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
+ oldSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ newSvc.Spec.Type = core.ServiceTypeLoadBalancer
+ newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ newSvc.Spec.LoadBalancerSourceRanges = []string{"10.0.0.0/8"}
},
- {
- name: "update loadBalancerSourceRanges",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
- oldSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- oldSvc.Spec.LoadBalancerSourceRanges = []string{"10.0.0.0/8"}
- newSvc.Spec.Type = core.ServiceTypeLoadBalancer
- newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- newSvc.Spec.LoadBalancerSourceRanges = []string{"10.100.0.0/16"}
- },
- numErrs: 0,
+ numErrs: 0,
+ }, {
+ name: "update loadBalancerSourceRanges",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
+ oldSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ oldSvc.Spec.LoadBalancerSourceRanges = []string{"10.0.0.0/8"}
+ newSvc.Spec.Type = core.ServiceTypeLoadBalancer
+ newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
+ newSvc.Spec.LoadBalancerSourceRanges = []string{"10.100.0.0/16"}
},
- {
- name: "LoadBalancer type cannot have None ClusterIP",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- newSvc.Spec.ClusterIP = "None"
- newSvc.Spec.ClusterIPs = []string{"None"}
- newSvc.Spec.Type = core.ServiceTypeLoadBalancer
- newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
- newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
- },
- numErrs: 1,
+ numErrs: 0,
+ }, {
+ name: "LoadBalancer type cannot have None ClusterIP",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ newSvc.Spec.ClusterIP = "None"
+ newSvc.Spec.ClusterIPs = []string{"None"}
+ newSvc.Spec.Type = core.ServiceTypeLoadBalancer
+ newSvc.Spec.ExternalTrafficPolicy = core.ServiceExternalTrafficPolicyCluster
+ newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
},
- {
- name: "`None` ClusterIP can NOT be changed",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- oldSvc.Spec.Type = core.ServiceTypeClusterIP
- newSvc.Spec.Type = core.ServiceTypeClusterIP
+ numErrs: 1,
+ }, {
+ name: "`None` ClusterIP can NOT be changed",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ oldSvc.Spec.Type = core.ServiceTypeClusterIP
+ newSvc.Spec.Type = core.ServiceTypeClusterIP
- oldSvc.Spec.ClusterIP = "None"
- oldSvc.Spec.ClusterIPs = []string{"None"}
+ oldSvc.Spec.ClusterIP = "None"
+ oldSvc.Spec.ClusterIPs = []string{"None"}
- newSvc.Spec.ClusterIP = "1.2.3.4"
- newSvc.Spec.ClusterIPs = []string{"1.2.3.4"}
- },
- numErrs: 1,
+ newSvc.Spec.ClusterIP = "1.2.3.4"
+ newSvc.Spec.ClusterIPs = []string{"1.2.3.4"}
},
- {
- name: "`None` ClusterIP can NOT be removed",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- oldSvc.Spec.ClusterIP = "None"
- oldSvc.Spec.ClusterIPs = []string{"None"}
+ numErrs: 1,
+ }, {
+ name: "`None` ClusterIP can NOT be removed",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ oldSvc.Spec.ClusterIP = "None"
+ oldSvc.Spec.ClusterIPs = []string{"None"}
- newSvc.Spec.ClusterIP = ""
- newSvc.Spec.ClusterIPs = nil
- },
- numErrs: 1,
+ newSvc.Spec.ClusterIP = ""
+ newSvc.Spec.ClusterIPs = nil
},
- {
- name: "ClusterIP can NOT be changed to None",
- tweakSvc: func(oldSvc, newSvc *core.Service) {
- oldSvc.Spec.ClusterIP = "1.2.3.4"
- oldSvc.Spec.ClusterIPs = []string{"1.2.3.4"}
+ numErrs: 1,
+ }, {
+ name: "ClusterIP can NOT be changed to None",
+ tweakSvc: func(oldSvc, newSvc *core.Service) {
+ oldSvc.Spec.ClusterIP = "1.2.3.4"
+ oldSvc.Spec.ClusterIPs = []string{"1.2.3.4"}
- newSvc.Spec.ClusterIP = "None"
- newSvc.Spec.ClusterIPs = []string{"None"}
- },
- numErrs: 1,
+ newSvc.Spec.ClusterIP = "None"
+ newSvc.Spec.ClusterIPs = []string{"None"}
},
+ numErrs: 1,
+ },
{
name: "Service with ClusterIP type cannot change its set ClusterIP",
@@ -17826,8 +16552,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Service with ClusterIP type can change its empty ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -17839,8 +16564,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with ClusterIP type cannot change its set ClusterIP when changing type to NodePort",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -17854,8 +16578,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Service with ClusterIP type can change its empty ClusterIP when changing type to NodePort",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -17869,8 +16592,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with ClusterIP type cannot change its ClusterIP when changing type to LoadBalancer",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -17885,8 +16607,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Service with ClusterIP type can change its empty ClusterIP when changing type to LoadBalancer",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -17901,8 +16622,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with LoadBalancer type can change its AllocateLoadBalancerNodePorts from true to false",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -17912,8 +16632,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(false)
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with LoadBalancer type can change its AllocateLoadBalancerNodePorts from false to true",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -17923,8 +16642,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with NodePort type cannot change its set ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeNodePort
@@ -17938,8 +16656,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Service with NodePort type can change its empty ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeNodePort
@@ -17953,8 +16670,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with NodePort type cannot change its set ClusterIP when changing type to ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeNodePort
@@ -17967,8 +16683,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Service with NodePort type can change its empty ClusterIP when changing type to ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeNodePort
@@ -17981,8 +16696,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with NodePort type cannot change its set ClusterIP when changing type to LoadBalancer",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeNodePort
@@ -17997,8 +16711,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Service with NodePort type can change its empty ClusterIP when changing type to LoadBalancer",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeNodePort
@@ -18013,8 +16726,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with LoadBalancer type cannot change its set ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18030,8 +16742,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Service with LoadBalancer type can change its empty ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18047,8 +16758,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with LoadBalancer type cannot change its set ClusterIP when changing type to ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18062,8 +16772,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Service with LoadBalancer type can change its empty ClusterIP when changing type to ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18077,8 +16786,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with LoadBalancer type cannot change its set ClusterIP when changing type to NodePort",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18093,8 +16801,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 1,
- },
- {
+ }, {
name: "Service with LoadBalancer type can change its empty ClusterIP when changing type to NodePort",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18109,8 +16816,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with ExternalName type can change its empty ClusterIP when changing type to ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeExternalName
@@ -18123,8 +16829,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "Service with ExternalName type can change its set ClusterIP when changing type to ClusterIP",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeExternalName
@@ -18137,8 +16842,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.ClusterIPs = []string{"1.2.3.5"}
},
numErrs: 0,
- },
- {
+ }, {
name: "invalid node port with clusterIP None",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeNodePort
@@ -18164,8 +16868,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.Type = core.ServiceTypeClusterIP
},
numErrs: 0,
- },
- {
+ }, {
name: "invalid: convert to ExternalName",
tweakSvc: func(oldSvc, newSvc *core.Service) {
singleStack := core.IPFamilyPolicySingleStack
@@ -18190,8 +16893,7 @@ func TestValidateServiceUpdate(t *testing.T) {
},
numErrs: 3,
- },
- {
+ }, {
name: "valid: convert to ExternalName",
tweakSvc: func(oldSvc, newSvc *core.Service) {
singleStack := core.IPFamilyPolicySingleStack
@@ -18217,8 +16919,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "same ServiceIPFamily, change IPFamilyPolicy to singleStack",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -18230,8 +16931,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "same ServiceIPFamily, change IPFamilyPolicy singleStack => requireDualStack",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -18269,8 +16969,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.Type = core.ServiceTypeExternalName
},
numErrs: 0,
- },
- {
+ }, {
name: "setting ipfamily from nil to v4",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.IPFamilies = nil
@@ -18279,8 +16978,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "setting ipfamily from nil to v6",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.IPFamilies = nil
@@ -18289,8 +16987,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "change primary ServiceIPFamily",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "1.2.3.4"
@@ -18322,8 +17019,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid: upgrade to dual stack with preferDualStack",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "1.2.3.4"
@@ -18358,8 +17054,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid: upgrade to dual stack, with specific secondary ip",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "1.2.3.4"
@@ -18375,8 +17070,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid: downgrade from dual to single",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "1.2.3.4"
@@ -18392,8 +17086,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid: change families for a headless service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "None"
@@ -18409,8 +17102,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol, core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid: upgrade a headless service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "None"
@@ -18426,8 +17118,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol, core.IPv4Protocol}
},
numErrs: 0,
- },
- {
+ }, {
name: "valid: downgrade a headless service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "None"
@@ -18461,8 +17152,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv6Protocol, core.IPv4Protocol}
},
numErrs: 4,
- },
- {
+ }, {
name: "invalid change first ip, in dualstack service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIPs = []string{"1.2.3.4", "2001::1"}
@@ -18478,8 +17168,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid, change second ip in dualstack service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "1.2.3.4"
@@ -18495,8 +17184,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 1,
- },
- {
+ }, {
name: "downgrade keeping the families",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "1.2.3.4"
@@ -18512,8 +17200,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 0, // families and ips are trimmed in strategy
- },
- {
+ }, {
name: "invalid, downgrade without changing to singleStack",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "1.2.3.4"
@@ -18529,8 +17216,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 2,
- },
- {
+ }, {
name: "invalid, downgrade and change primary ip",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "1.2.3.4"
@@ -18546,8 +17232,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol}
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid: upgrade to dual stack and change primary",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.ClusterIP = "1.2.3.4"
@@ -18564,31 +17249,27 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.IPFamilies = []core.IPFamily{core.IPv4Protocol, core.IPv6Protocol}
},
numErrs: 1,
- },
- {
+ }, {
name: "update to valid app protocol",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt32(3000), Protocol: "TCP"}}
newSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt32(3000), Protocol: "TCP", AppProtocol: utilpointer.String("https")}}
},
numErrs: 0,
- },
- {
+ }, {
name: "update to invalid app protocol",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt32(3000), Protocol: "TCP"}}
newSvc.Spec.Ports = []core.ServicePort{{Name: "a", Port: 443, TargetPort: intstr.FromInt32(3000), Protocol: "TCP", AppProtocol: utilpointer.String("~https")}}
},
numErrs: 1,
- },
- {
+ }, {
name: "Set AllocateLoadBalancerNodePorts when type is not LoadBalancer",
tweakSvc: func(oldSvc, newSvc *core.Service) {
newSvc.Spec.AllocateLoadBalancerNodePorts = utilpointer.Bool(true)
},
numErrs: 1,
- },
- {
+ }, {
name: "update LoadBalancer type of service without change LoadBalancerClass",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18601,8 +17282,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-old")
},
numErrs: 0,
- },
- {
+ }, {
name: "invalid: change LoadBalancerClass when update service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18615,8 +17295,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-new")
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid: unset LoadBalancerClass when update service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18629,8 +17308,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = nil
},
numErrs: 1,
- },
- {
+ }, {
name: "invalid: set LoadBalancerClass when update service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18643,8 +17321,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-new")
},
numErrs: 1,
- },
- {
+ }, {
name: "update to LoadBalancer type of service with valid LoadBalancerClass",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -18655,8 +17332,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-load-balancer-class")
},
numErrs: 0,
- },
- {
+ }, {
name: "update to LoadBalancer type of service without LoadBalancerClass",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -18667,8 +17343,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = nil
},
numErrs: 0,
- },
- {
+ }, {
name: "invalid: set invalid LoadBalancerClass when update service to LoadBalancer",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -18679,8 +17354,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("Bad/LoadBalancerclass")
},
numErrs: 2,
- },
- {
+ }, {
name: "invalid: set LoadBalancerClass when update service to non LoadBalancer type of service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeClusterIP
@@ -18689,8 +17363,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-load-balancer-class")
},
numErrs: 2,
- },
- {
+ }, {
name: "invalid: set LoadBalancerClass when update service to non LoadBalancer type of service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeExternalName
@@ -18699,8 +17372,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-load-balancer-class")
},
numErrs: 3,
- },
- {
+ }, {
name: "invalid: set LoadBalancerClass when update service to non LoadBalancer type of service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeNodePort
@@ -18710,8 +17382,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-load-balancer-class")
},
numErrs: 2,
- },
- {
+ }, {
name: "invalid: set LoadBalancerClass when update from LoadBalancer service to non LoadBalancer type of service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18722,8 +17393,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-load-balancer-class")
},
numErrs: 2,
- },
- {
+ }, {
name: "invalid: set LoadBalancerClass when update from LoadBalancer service to non LoadBalancer type of service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18734,8 +17404,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-load-balancer-class")
},
numErrs: 3,
- },
- {
+ }, {
name: "invalid: set LoadBalancerClass when update from LoadBalancer service to non LoadBalancer type of service",
tweakSvc: func(oldSvc, newSvc *core.Service) {
oldSvc.Spec.Type = core.ServiceTypeLoadBalancer
@@ -18747,8 +17416,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.LoadBalancerClass = utilpointer.String("test.com/test-load-balancer-class")
},
numErrs: 2,
- },
- {
+ }, {
name: "update internalTrafficPolicy from Cluster to Local",
tweakSvc: func(oldSvc, newSvc *core.Service) {
cluster := core.ServiceInternalTrafficPolicyCluster
@@ -18758,8 +17426,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.InternalTrafficPolicy = &local
},
numErrs: 0,
- },
- {
+ }, {
name: "update internalTrafficPolicy from Local to Cluster",
tweakSvc: func(oldSvc, newSvc *core.Service) {
local := core.ServiceInternalTrafficPolicyLocal
@@ -18769,8 +17436,7 @@ func TestValidateServiceUpdate(t *testing.T) {
newSvc.Spec.InternalTrafficPolicy = &cluster
},
numErrs: 0,
- },
- {
+ }, {
name: "topology annotations are mismatched",
tweakSvc: func(oldSvc, newSvc *core.Service) {
newSvc.Annotations[core.DeprecatedAnnotationTopologyAwareHints] = "original"
@@ -18869,28 +17535,24 @@ func TestValidateLimitRangeForLocalStorage(t *testing.T) {
testCases := []struct {
name string
spec core.LimitRangeSpec
- }{
- {
- name: "all-fields-valid",
- spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePod,
- Max: getLocalStorageResourceList("10000Mi"),
- Min: getLocalStorageResourceList("100Mi"),
- MaxLimitRequestRatio: getLocalStorageResourceList(""),
- },
- {
- Type: core.LimitTypeContainer,
- Max: getLocalStorageResourceList("10000Mi"),
- Min: getLocalStorageResourceList("100Mi"),
- Default: getLocalStorageResourceList("500Mi"),
- DefaultRequest: getLocalStorageResourceList("200Mi"),
- MaxLimitRequestRatio: getLocalStorageResourceList(""),
- },
- },
- },
+ }{{
+ name: "all-fields-valid",
+ spec: core.LimitRangeSpec{
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePod,
+ Max: getLocalStorageResourceList("10000Mi"),
+ Min: getLocalStorageResourceList("100Mi"),
+ MaxLimitRequestRatio: getLocalStorageResourceList(""),
+ }, {
+ Type: core.LimitTypeContainer,
+ Max: getLocalStorageResourceList("10000Mi"),
+ Min: getLocalStorageResourceList("100Mi"),
+ Default: getLocalStorageResourceList("500Mi"),
+ DefaultRequest: getLocalStorageResourceList("200Mi"),
+ MaxLimitRequestRatio: getLocalStorageResourceList(""),
+ }},
},
+ },
}
for _, testCase := range testCases {
@@ -18905,100 +17567,80 @@ func TestValidateLimitRange(t *testing.T) {
successCases := []struct {
name string
spec core.LimitRangeSpec
- }{
- {
- name: "all-fields-valid",
- spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePod,
- Max: getResourceList("100m", "10000Mi"),
- Min: getResourceList("5m", "100Mi"),
- MaxLimitRequestRatio: getResourceList("10", ""),
- },
- {
- Type: core.LimitTypeContainer,
- Max: getResourceList("100m", "10000Mi"),
- Min: getResourceList("5m", "100Mi"),
- Default: getResourceList("50m", "500Mi"),
- DefaultRequest: getResourceList("10m", "200Mi"),
- MaxLimitRequestRatio: getResourceList("10", ""),
- },
- {
- Type: core.LimitTypePersistentVolumeClaim,
- Max: getStorageResourceList("10Gi"),
- Min: getStorageResourceList("5Gi"),
- },
- },
- },
+ }{{
+ name: "all-fields-valid",
+ spec: core.LimitRangeSpec{
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePod,
+ Max: getResourceList("100m", "10000Mi"),
+ Min: getResourceList("5m", "100Mi"),
+ MaxLimitRequestRatio: getResourceList("10", ""),
+ }, {
+ Type: core.LimitTypeContainer,
+ Max: getResourceList("100m", "10000Mi"),
+ Min: getResourceList("5m", "100Mi"),
+ Default: getResourceList("50m", "500Mi"),
+ DefaultRequest: getResourceList("10m", "200Mi"),
+ MaxLimitRequestRatio: getResourceList("10", ""),
+ }, {
+ Type: core.LimitTypePersistentVolumeClaim,
+ Max: getStorageResourceList("10Gi"),
+ Min: getStorageResourceList("5Gi"),
+ }},
},
- {
- name: "pvc-min-only",
- spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePersistentVolumeClaim,
- Min: getStorageResourceList("5Gi"),
- },
- },
- },
+ }, {
+ name: "pvc-min-only",
+ spec: core.LimitRangeSpec{
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePersistentVolumeClaim,
+ Min: getStorageResourceList("5Gi"),
+ }},
},
- {
- name: "pvc-max-only",
- spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePersistentVolumeClaim,
- Max: getStorageResourceList("10Gi"),
- },
- },
- },
+ }, {
+ name: "pvc-max-only",
+ spec: core.LimitRangeSpec{
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePersistentVolumeClaim,
+ Max: getStorageResourceList("10Gi"),
+ }},
},
- {
- name: "all-fields-valid-big-numbers",
- spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypeContainer,
- Max: getResourceList("100m", "10000T"),
- Min: getResourceList("5m", "100Mi"),
- Default: getResourceList("50m", "500Mi"),
- DefaultRequest: getResourceList("10m", "200Mi"),
- MaxLimitRequestRatio: getResourceList("10", ""),
- },
- },
- },
+ }, {
+ name: "all-fields-valid-big-numbers",
+ spec: core.LimitRangeSpec{
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypeContainer,
+ Max: getResourceList("100m", "10000T"),
+ Min: getResourceList("5m", "100Mi"),
+ Default: getResourceList("50m", "500Mi"),
+ DefaultRequest: getResourceList("10m", "200Mi"),
+ MaxLimitRequestRatio: getResourceList("10", ""),
+ }},
},
- {
- name: "thirdparty-fields-all-valid-standard-container-resources",
- spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: "thirdparty.com/foo",
- Max: getResourceList("100m", "10000T"),
- Min: getResourceList("5m", "100Mi"),
- Default: getResourceList("50m", "500Mi"),
- DefaultRequest: getResourceList("10m", "200Mi"),
- MaxLimitRequestRatio: getResourceList("10", ""),
- },
- },
- },
+ }, {
+ name: "thirdparty-fields-all-valid-standard-container-resources",
+ spec: core.LimitRangeSpec{
+ Limits: []core.LimitRangeItem{{
+ Type: "thirdparty.com/foo",
+ Max: getResourceList("100m", "10000T"),
+ Min: getResourceList("5m", "100Mi"),
+ Default: getResourceList("50m", "500Mi"),
+ DefaultRequest: getResourceList("10m", "200Mi"),
+ MaxLimitRequestRatio: getResourceList("10", ""),
+ }},
},
- {
- name: "thirdparty-fields-all-valid-storage-resources",
- spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: "thirdparty.com/foo",
- Max: getStorageResourceList("10000T"),
- Min: getStorageResourceList("100Mi"),
- Default: getStorageResourceList("500Mi"),
- DefaultRequest: getStorageResourceList("200Mi"),
- MaxLimitRequestRatio: getStorageResourceList(""),
- },
- },
- },
+ }, {
+ name: "thirdparty-fields-all-valid-storage-resources",
+ spec: core.LimitRangeSpec{
+ Limits: []core.LimitRangeItem{{
+ Type: "thirdparty.com/foo",
+ Max: getStorageResourceList("10000T"),
+ Min: getStorageResourceList("100Mi"),
+ Default: getStorageResourceList("500Mi"),
+ DefaultRequest: getStorageResourceList("200Mi"),
+ MaxLimitRequestRatio: getStorageResourceList(""),
+ }},
},
+ },
}
for _, successCase := range successCases {
@@ -19030,156 +17672,131 @@ func TestValidateLimitRange(t *testing.T) {
},
"duplicate-limit-type": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePod,
- Max: getResourceList("100m", "10000m"),
- Min: getResourceList("0m", "100m"),
- },
- {
- Type: core.LimitTypePod,
- Min: getResourceList("0m", "100m"),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePod,
+ Max: getResourceList("100m", "10000m"),
+ Min: getResourceList("0m", "100m"),
+ }, {
+ Type: core.LimitTypePod,
+ Min: getResourceList("0m", "100m"),
+ }},
}},
"",
},
"default-limit-type-pod": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePod,
- Max: getResourceList("100m", "10000m"),
- Min: getResourceList("0m", "100m"),
- Default: getResourceList("10m", "100m"),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePod,
+ Max: getResourceList("100m", "10000m"),
+ Min: getResourceList("0m", "100m"),
+ Default: getResourceList("10m", "100m"),
+ }},
}},
"may not be specified when `type` is 'Pod'",
},
"default-request-limit-type-pod": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePod,
- Max: getResourceList("100m", "10000m"),
- Min: getResourceList("0m", "100m"),
- DefaultRequest: getResourceList("10m", "100m"),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePod,
+ Max: getResourceList("100m", "10000m"),
+ Min: getResourceList("0m", "100m"),
+ DefaultRequest: getResourceList("10m", "100m"),
+ }},
}},
"may not be specified when `type` is 'Pod'",
},
"min value 100m is greater than max value 10m": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePod,
- Max: getResourceList("10m", ""),
- Min: getResourceList("100m", ""),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePod,
+ Max: getResourceList("10m", ""),
+ Min: getResourceList("100m", ""),
+ }},
}},
"min value 100m is greater than max value 10m",
},
"invalid spec default outside range": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypeContainer,
- Max: getResourceList("1", ""),
- Min: getResourceList("100m", ""),
- Default: getResourceList("2000m", ""),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypeContainer,
+ Max: getResourceList("1", ""),
+ Min: getResourceList("100m", ""),
+ Default: getResourceList("2000m", ""),
+ }},
}},
"default value 2 is greater than max value 1",
},
"invalid spec default request outside range": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypeContainer,
- Max: getResourceList("1", ""),
- Min: getResourceList("100m", ""),
- DefaultRequest: getResourceList("2000m", ""),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypeContainer,
+ Max: getResourceList("1", ""),
+ Min: getResourceList("100m", ""),
+ DefaultRequest: getResourceList("2000m", ""),
+ }},
}},
"default request value 2 is greater than max value 1",
},
"invalid spec default request more than default": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypeContainer,
- Max: getResourceList("2", ""),
- Min: getResourceList("100m", ""),
- Default: getResourceList("500m", ""),
- DefaultRequest: getResourceList("800m", ""),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypeContainer,
+ Max: getResourceList("2", ""),
+ Min: getResourceList("100m", ""),
+ Default: getResourceList("500m", ""),
+ DefaultRequest: getResourceList("800m", ""),
+ }},
}},
"default request value 800m is greater than default limit value 500m",
},
"invalid spec maxLimitRequestRatio less than 1": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePod,
- MaxLimitRequestRatio: getResourceList("800m", ""),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePod,
+ MaxLimitRequestRatio: getResourceList("800m", ""),
+ }},
}},
"ratio 800m is less than 1",
},
"invalid spec maxLimitRequestRatio greater than max/min": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypeContainer,
- Max: getResourceList("", "2Gi"),
- Min: getResourceList("", "512Mi"),
- MaxLimitRequestRatio: getResourceList("", "10"),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypeContainer,
+ Max: getResourceList("", "2Gi"),
+ Min: getResourceList("", "512Mi"),
+ MaxLimitRequestRatio: getResourceList("", "10"),
+ }},
}},
"ratio 10 is greater than max/min = 4.000000",
},
"invalid non standard limit type": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: "foo",
- Max: getStorageResourceList("10000T"),
- Min: getStorageResourceList("100Mi"),
- Default: getStorageResourceList("500Mi"),
- DefaultRequest: getStorageResourceList("200Mi"),
- MaxLimitRequestRatio: getStorageResourceList(""),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: "foo",
+ Max: getStorageResourceList("10000T"),
+ Min: getStorageResourceList("100Mi"),
+ Default: getStorageResourceList("500Mi"),
+ DefaultRequest: getStorageResourceList("200Mi"),
+ MaxLimitRequestRatio: getStorageResourceList(""),
+ }},
}},
"must be a standard limit type or fully qualified",
},
"min and max values missing, one required": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePersistentVolumeClaim,
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePersistentVolumeClaim,
+ }},
}},
"either minimum or maximum storage value is required, but neither was provided",
},
"invalid min greater than max": {
core.LimitRange{ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: core.LimitRangeSpec{
- Limits: []core.LimitRangeItem{
- {
- Type: core.LimitTypePersistentVolumeClaim,
- Min: getStorageResourceList("10Gi"),
- Max: getStorageResourceList("1Gi"),
- },
- },
+ Limits: []core.LimitRangeItem{{
+ Type: core.LimitTypePersistentVolumeClaim,
+ Min: getStorageResourceList("10Gi"),
+ Max: getStorageResourceList("1Gi"),
+ }},
}},
"min value 10Gi is greater than max value 1Gi",
},
@@ -19427,13 +18044,11 @@ func TestValidateResourceQuota(t *testing.T) {
scopeSelectorSpec := core.ResourceQuotaSpec{
ScopeSelector: &core.ScopeSelector{
- MatchExpressions: []core.ScopedResourceSelectorRequirement{
- {
- ScopeName: core.ResourceQuotaScopePriorityClass,
- Operator: core.ScopeSelectorOpIn,
- Values: []string{"cluster-services"},
- },
- },
+ MatchExpressions: []core.ScopedResourceSelectorRequirement{{
+ ScopeName: core.ResourceQuotaScopePriorityClass,
+ Operator: core.ScopeSelectorOpIn,
+ Values: []string{"cluster-services"},
+ }},
},
}
@@ -19488,13 +18103,11 @@ func TestValidateResourceQuota(t *testing.T) {
invalidCrossNamespaceAffinitySpec := core.ResourceQuotaSpec{
ScopeSelector: &core.ScopeSelector{
- MatchExpressions: []core.ScopedResourceSelectorRequirement{
- {
- ScopeName: core.ResourceQuotaScopeCrossNamespacePodAffinity,
- Operator: core.ScopeSelectorOpIn,
- Values: []string{"cluster-services"},
- },
- },
+ MatchExpressions: []core.ScopedResourceSelectorRequirement{{
+ ScopeName: core.ResourceQuotaScopeCrossNamespacePodAffinity,
+ Operator: core.ScopeSelectorOpIn,
+ Values: []string{"cluster-services"},
+ }},
},
}
@@ -19648,16 +18261,14 @@ func TestValidateResourceQuota(t *testing.T) {
func TestValidateNamespace(t *testing.T) {
validLabels := map[string]string{"a": "b"}
invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
- successCases := []core.Namespace{
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc", Labels: validLabels},
- },
- {
- ObjectMeta: metav1.ObjectMeta{Name: "abc-123"},
- Spec: core.NamespaceSpec{
- Finalizers: []core.FinalizerName{"example.com/something", "example.com/other"},
- },
+ successCases := []core.Namespace{{
+ ObjectMeta: metav1.ObjectMeta{Name: "abc", Labels: validLabels},
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: "abc-123"},
+ Spec: core.NamespaceSpec{
+ Finalizers: []core.FinalizerName{"example.com/something", "example.com/other"},
},
+ },
}
for _, successCase := range successCases {
if errs := ValidateNamespace(&successCase); len(errs) != 0 {
@@ -20043,49 +18654,42 @@ func TestValidateSecretUpdate(t *testing.T) {
oldSecret core.Secret
newSecret core.Secret
valid bool
- }{
- {
- name: "mark secret immutable",
- oldSecret: secret,
- newSecret: immutableSecret,
- valid: true,
- },
- {
- name: "revert immutable secret",
- oldSecret: immutableSecret,
- newSecret: secret,
- valid: false,
- },
- {
- name: "makr immutable secret mutable",
- oldSecret: immutableSecret,
- newSecret: mutableSecret,
- valid: false,
- },
- {
- name: "add data in secret",
- oldSecret: secret,
- newSecret: secretWithData,
- valid: true,
- },
- {
- name: "add data in immutable secret",
- oldSecret: immutableSecret,
- newSecret: immutableSecretWithData,
- valid: false,
- },
- {
- name: "change data in secret",
- oldSecret: secret,
- newSecret: secretWithChangedData,
- valid: true,
- },
- {
- name: "change data in immutable secret",
- oldSecret: immutableSecret,
- newSecret: immutableSecretWithChangedData,
- valid: false,
- },
+ }{{
+ name: "mark secret immutable",
+ oldSecret: secret,
+ newSecret: immutableSecret,
+ valid: true,
+ }, {
+ name: "revert immutable secret",
+ oldSecret: immutableSecret,
+ newSecret: secret,
+ valid: false,
+ }, {
+ name: "makr immutable secret mutable",
+ oldSecret: immutableSecret,
+ newSecret: mutableSecret,
+ valid: false,
+ }, {
+ name: "add data in secret",
+ oldSecret: secret,
+ newSecret: secretWithData,
+ valid: true,
+ }, {
+ name: "add data in immutable secret",
+ oldSecret: immutableSecret,
+ newSecret: immutableSecretWithData,
+ valid: false,
+ }, {
+ name: "change data in secret",
+ oldSecret: secret,
+ newSecret: secretWithChangedData,
+ valid: true,
+ }, {
+ name: "change data in immutable secret",
+ oldSecret: immutableSecret,
+ newSecret: immutableSecretWithChangedData,
+ valid: false,
+ },
}
for _, tc := range tests {
@@ -20241,16 +18845,13 @@ func TestValidateEndpointsCreate(t *testing.T) {
"simple endpoint": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}, {IP: "10.10.2.2"}},
- Ports: []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
- },
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
- Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}, {Name: "b", Port: 76, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}, {IP: "10.10.2.2"}},
+ Ports: []core.EndpointPort{{Name: "a", Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
+ }, {
+ Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
+ Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}, {Name: "b", Port: 76, Protocol: "TCP"}},
+ }},
},
},
"empty subsets": {
@@ -20261,33 +18862,27 @@ func TestValidateEndpointsCreate(t *testing.T) {
"no name required for singleton port": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
- Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
+ Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}},
+ }},
},
},
"valid appProtocol": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
- Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.String("HTTP")}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
+ Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP", AppProtocol: utilpointer.String("HTTP")}},
+ }},
},
},
"empty ports": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.3.3"}},
+ }},
},
},
}
@@ -20328,23 +18923,19 @@ func TestValidateEndpointsCreate(t *testing.T) {
"empty addresses": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
+ }},
},
errorType: "FieldValueRequired",
},
"invalid IP": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "[2001:0db8:85a3:0042:1000:8a2e:0370:7334]"}},
- Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "[2001:0db8:85a3:0042:1000:8a2e:0370:7334]"}},
+ Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
+ }},
},
errorType: "FieldValueInvalid",
errorDetail: "must be a valid IP address",
@@ -20352,24 +18943,20 @@ func TestValidateEndpointsCreate(t *testing.T) {
"Multiple ports, one without name": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
- Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
+ Ports: []core.EndpointPort{{Port: 8675, Protocol: "TCP"}, {Name: "b", Port: 309, Protocol: "TCP"}},
+ }},
},
errorType: "FieldValueRequired",
},
"Invalid port number": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
- Ports: []core.EndpointPort{{Name: "a", Port: 66000, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
+ Ports: []core.EndpointPort{{Name: "a", Port: 66000, Protocol: "TCP"}},
+ }},
},
errorType: "FieldValueInvalid",
errorDetail: "between",
@@ -20377,24 +18964,20 @@ func TestValidateEndpointsCreate(t *testing.T) {
"Invalid protocol": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
- Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "Protocol"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
+ Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "Protocol"}},
+ }},
},
errorType: "FieldValueNotSupported",
},
"Address missing IP": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{}},
- Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{}},
+ Ports: []core.EndpointPort{{Name: "a", Port: 93, Protocol: "TCP"}},
+ }},
},
errorType: "FieldValueInvalid",
errorDetail: "must be a valid IP address",
@@ -20402,12 +18985,10 @@ func TestValidateEndpointsCreate(t *testing.T) {
"Port missing number": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
- Ports: []core.EndpointPort{{Name: "a", Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
+ Ports: []core.EndpointPort{{Name: "a", Protocol: "TCP"}},
+ }},
},
errorType: "FieldValueInvalid",
errorDetail: "between",
@@ -20415,24 +18996,20 @@ func TestValidateEndpointsCreate(t *testing.T) {
"Port missing protocol": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
- Ports: []core.EndpointPort{{Name: "a", Port: 93}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
+ Ports: []core.EndpointPort{{Name: "a", Port: 93}},
+ }},
},
errorType: "FieldValueRequired",
},
"Address is loopback": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "127.0.0.1"}},
- Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "127.0.0.1"}},
+ Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
+ }},
},
errorType: "FieldValueInvalid",
errorDetail: "loopback",
@@ -20440,12 +19017,10 @@ func TestValidateEndpointsCreate(t *testing.T) {
"Address is link-local": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "169.254.169.254"}},
- Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "169.254.169.254"}},
+ Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
+ }},
},
errorType: "FieldValueInvalid",
errorDetail: "link-local",
@@ -20453,12 +19028,10 @@ func TestValidateEndpointsCreate(t *testing.T) {
"Address is link-local multicast": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "224.0.0.1"}},
- Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "224.0.0.1"}},
+ Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP"}},
+ }},
},
errorType: "FieldValueInvalid",
errorDetail: "link-local multicast",
@@ -20466,12 +19039,10 @@ func TestValidateEndpointsCreate(t *testing.T) {
"Invalid AppProtocol": {
endpoints: core.Endpoints{
ObjectMeta: metav1.ObjectMeta{Name: "mysvc", Namespace: "namespace"},
- Subsets: []core.EndpointSubset{
- {
- Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
- Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP", AppProtocol: utilpointer.String("lots-of[invalid]-{chars}")}},
- },
- },
+ Subsets: []core.EndpointSubset{{
+ Addresses: []core.EndpointAddress{{IP: "10.10.1.1"}},
+ Ports: []core.EndpointPort{{Name: "p", Port: 93, Protocol: "TCP", AppProtocol: utilpointer.String("lots-of[invalid]-{chars}")}},
+ }},
},
errorType: "FieldValueInvalid",
errorDetail: "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character",
@@ -20543,26 +19114,23 @@ func TestValidateWindowsSecurityContext(t *testing.T) {
expectError bool
errorMsg string
errorType field.ErrorType
- }{
- {
- name: "pod with SELinux Options",
- sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}}}}},
- expectError: true,
- errorMsg: "cannot be set for a windows pod",
- errorType: "FieldValueForbidden",
- },
- {
- name: "pod with SeccompProfile",
- sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SeccompProfile: &core.SeccompProfile{LocalhostProfile: utilpointer.String("dummy")}}}}},
- expectError: true,
- errorMsg: "cannot be set for a windows pod",
- errorType: "FieldValueForbidden",
- },
- {
- name: "pod with WindowsOptions, no error",
- sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{WindowsOptions: &core.WindowsSecurityContextOptions{RunAsUserName: utilpointer.String("dummy")}}}}},
- expectError: false,
- },
+ }{{
+ name: "pod with SELinux Options",
+ sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SELinuxOptions: &core.SELinuxOptions{Role: "dummy"}}}}},
+ expectError: true,
+ errorMsg: "cannot be set for a windows pod",
+ errorType: "FieldValueForbidden",
+ }, {
+ name: "pod with SeccompProfile",
+ sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{SeccompProfile: &core.SeccompProfile{LocalhostProfile: utilpointer.String("dummy")}}}}},
+ expectError: true,
+ errorMsg: "cannot be set for a windows pod",
+ errorType: "FieldValueForbidden",
+ }, {
+ name: "pod with WindowsOptions, no error",
+ sc: &core.PodSpec{Containers: []core.Container{{SecurityContext: &core.SecurityContext{WindowsOptions: &core.WindowsSecurityContextOptions{RunAsUserName: utilpointer.String("dummy")}}}}},
+ expectError: false,
+ },
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@@ -20776,64 +19344,58 @@ func TestValidateSchedulingGates(t *testing.T) {
name string
schedulingGates []core.PodSchedulingGate
wantFieldErrors field.ErrorList
- }{
- {
- name: "nil gates",
- schedulingGates: nil,
- wantFieldErrors: field.ErrorList{},
+ }{{
+ name: "nil gates",
+ schedulingGates: nil,
+ wantFieldErrors: field.ErrorList{},
+ }, {
+ name: "empty string in gates",
+ schedulingGates: []core.PodSchedulingGate{
+ {Name: "foo"},
+ {Name: ""},
},
- {
- name: "empty string in gates",
- schedulingGates: []core.PodSchedulingGate{
- {Name: "foo"},
- {Name: ""},
- },
- wantFieldErrors: field.ErrorList{
- field.Invalid(fieldPath.Index(1), "", "name part must be non-empty"),
- field.Invalid(fieldPath.Index(1), "", "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')"),
- },
+ wantFieldErrors: field.ErrorList{
+ field.Invalid(fieldPath.Index(1), "", "name part must be non-empty"),
+ field.Invalid(fieldPath.Index(1), "", "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')"),
},
- {
- name: "legal gates",
- schedulingGates: []core.PodSchedulingGate{
- {Name: "foo"},
- {Name: "bar"},
- },
- wantFieldErrors: field.ErrorList{},
+ }, {
+ name: "legal gates",
+ schedulingGates: []core.PodSchedulingGate{
+ {Name: "foo"},
+ {Name: "bar"},
},
- {
- name: "illegal gates",
- schedulingGates: []core.PodSchedulingGate{
- {Name: "foo"},
- {Name: "\nbar"},
- },
- wantFieldErrors: []*field.Error{field.Invalid(fieldPath.Index(1), "\nbar", "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')")},
+ wantFieldErrors: field.ErrorList{},
+ }, {
+ name: "illegal gates",
+ schedulingGates: []core.PodSchedulingGate{
+ {Name: "foo"},
+ {Name: "\nbar"},
},
- {
- name: "duplicated gates (single duplication)",
- schedulingGates: []core.PodSchedulingGate{
- {Name: "foo"},
- {Name: "bar"},
- {Name: "bar"},
- },
- wantFieldErrors: []*field.Error{field.Duplicate(fieldPath.Index(2), "bar")},
+ wantFieldErrors: []*field.Error{field.Invalid(fieldPath.Index(1), "\nbar", "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')")},
+ }, {
+ name: "duplicated gates (single duplication)",
+ schedulingGates: []core.PodSchedulingGate{
+ {Name: "foo"},
+ {Name: "bar"},
+ {Name: "bar"},
},
- {
- name: "duplicated gates (multiple duplications)",
- schedulingGates: []core.PodSchedulingGate{
- {Name: "foo"},
- {Name: "bar"},
- {Name: "foo"},
- {Name: "baz"},
- {Name: "foo"},
- {Name: "bar"},
- },
- wantFieldErrors: field.ErrorList{
- field.Duplicate(fieldPath.Index(2), "foo"),
- field.Duplicate(fieldPath.Index(4), "foo"),
- field.Duplicate(fieldPath.Index(5), "bar"),
- },
+ wantFieldErrors: []*field.Error{field.Duplicate(fieldPath.Index(2), "bar")},
+ }, {
+ name: "duplicated gates (multiple duplications)",
+ schedulingGates: []core.PodSchedulingGate{
+ {Name: "foo"},
+ {Name: "bar"},
+ {Name: "foo"},
+ {Name: "baz"},
+ {Name: "foo"},
+ {Name: "bar"},
},
+ wantFieldErrors: field.ErrorList{
+ field.Duplicate(fieldPath.Index(2), "foo"),
+ field.Duplicate(fieldPath.Index(4), "foo"),
+ field.Duplicate(fieldPath.Index(5), "bar"),
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -21224,61 +19786,52 @@ func TestValidateConfigMapUpdate(t *testing.T) {
newCfg core.ConfigMap
oldCfg core.ConfigMap
valid bool
- }{
- {
- name: "valid",
- newCfg: configMap,
- oldCfg: configMap,
- valid: true,
- },
- {
- name: "invalid",
- newCfg: noVersion,
- oldCfg: configMap,
- valid: false,
- },
- {
- name: "mark configmap immutable",
- oldCfg: configMap,
- newCfg: immutableConfigMap,
- valid: true,
- },
- {
- name: "revert immutable configmap",
- oldCfg: immutableConfigMap,
- newCfg: configMap,
- valid: false,
- },
- {
- name: "mark immutable configmap mutable",
- oldCfg: immutableConfigMap,
- newCfg: mutableConfigMap,
- valid: false,
- },
- {
- name: "add data in configmap",
- oldCfg: configMap,
- newCfg: configMapWithData,
- valid: true,
- },
- {
- name: "add data in immutable configmap",
- oldCfg: immutableConfigMap,
- newCfg: immutableConfigMapWithData,
- valid: false,
- },
- {
- name: "change data in configmap",
- oldCfg: configMap,
- newCfg: configMapWithChangedData,
- valid: true,
- },
- {
- name: "change data in immutable configmap",
- oldCfg: immutableConfigMap,
- newCfg: immutableConfigMapWithChangedData,
- valid: false,
- },
+ }{{
+ name: "valid",
+ newCfg: configMap,
+ oldCfg: configMap,
+ valid: true,
+ }, {
+ name: "invalid",
+ newCfg: noVersion,
+ oldCfg: configMap,
+ valid: false,
+ }, {
+ name: "mark configmap immutable",
+ oldCfg: configMap,
+ newCfg: immutableConfigMap,
+ valid: true,
+ }, {
+ name: "revert immutable configmap",
+ oldCfg: immutableConfigMap,
+ newCfg: configMap,
+ valid: false,
+ }, {
+ name: "mark immutable configmap mutable",
+ oldCfg: immutableConfigMap,
+ newCfg: mutableConfigMap,
+ valid: false,
+ }, {
+ name: "add data in configmap",
+ oldCfg: configMap,
+ newCfg: configMapWithData,
+ valid: true,
+ }, {
+ name: "add data in immutable configmap",
+ oldCfg: immutableConfigMap,
+ newCfg: immutableConfigMapWithData,
+ valid: false,
+ }, {
+ name: "change data in configmap",
+ oldCfg: configMap,
+ newCfg: configMapWithChangedData,
+ valid: true,
+ }, {
+ name: "change data in immutable configmap",
+ oldCfg: immutableConfigMap,
+ newCfg: immutableConfigMapWithChangedData,
+ valid: false,
+ },
}
for _, tc := range cases {
@@ -21451,15 +20004,13 @@ func newNodeNameEndpoint(nodeName string) *core.Endpoints {
Namespace: metav1.NamespaceDefault,
ResourceVersion: "1",
},
- Subsets: []core.EndpointSubset{
- {
- NotReadyAddresses: []core.EndpointAddress{},
- Ports: []core.EndpointPort{{Name: "https", Port: 443, Protocol: "TCP"}},
- Addresses: []core.EndpointAddress{
- {
- IP: "8.8.8.8",
- Hostname: "zookeeper1",
- NodeName: &nodeName}}}}}
+ Subsets: []core.EndpointSubset{{
+ NotReadyAddresses: []core.EndpointAddress{},
+ Ports: []core.EndpointPort{{Name: "https", Port: 443, Protocol: "TCP"}},
+ Addresses: []core.EndpointAddress{{
+ IP: "8.8.8.8",
+ Hostname: "zookeeper1",
+ NodeName: &nodeName}}}}}
return ep
}
@@ -21644,171 +20195,146 @@ func TestValidateWindowsSecurityContextOptions(t *testing.T) {
windowsOptions *core.WindowsSecurityContextOptions
expectedErrorSubstring string
- }{
- {
- testName: "a nil pointer",
+ }{{
+ testName: "a nil pointer",
+ }, {
+ testName: "an empty struct",
+ windowsOptions: &core.WindowsSecurityContextOptions{},
+ }, {
+ testName: "a valid input",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ GMSACredentialSpecName: toPtr("dummy-gmsa-crep-spec-name"),
+ GMSACredentialSpec: toPtr("dummy-gmsa-crep-spec-contents"),
},
- {
- testName: "an empty struct",
- windowsOptions: &core.WindowsSecurityContextOptions{},
+ }, {
+ testName: "a GMSA cred spec name that is not a valid resource name",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ // invalid because of the underscore
+ GMSACredentialSpecName: toPtr("not_a-valid-gmsa-crep-spec-name"),
},
- {
- testName: "a valid input",
- windowsOptions: &core.WindowsSecurityContextOptions{
- GMSACredentialSpecName: toPtr("dummy-gmsa-crep-spec-name"),
- GMSACredentialSpec: toPtr("dummy-gmsa-crep-spec-contents"),
- },
+ expectedErrorSubstring: dnsSubdomainLabelErrMsg,
+ }, {
+ testName: "empty GMSA cred spec contents",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ GMSACredentialSpec: toPtr(""),
},
- {
- testName: "a GMSA cred spec name that is not a valid resource name",
- windowsOptions: &core.WindowsSecurityContextOptions{
- // invalid because of the underscore
- GMSACredentialSpecName: toPtr("not_a-valid-gmsa-crep-spec-name"),
- },
- expectedErrorSubstring: dnsSubdomainLabelErrMsg,
+ expectedErrorSubstring: "gmsaCredentialSpec cannot be an empty string",
+ }, {
+ testName: "GMSA cred spec contents that are too long",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ GMSACredentialSpec: toPtr(strings.Repeat("a", maxGMSACredentialSpecLength+1)),
},
- {
- testName: "empty GMSA cred spec contents",
- windowsOptions: &core.WindowsSecurityContextOptions{
- GMSACredentialSpec: toPtr(""),
- },
- expectedErrorSubstring: "gmsaCredentialSpec cannot be an empty string",
+ expectedErrorSubstring: "gmsaCredentialSpec size must be under",
+ }, {
+ testName: "RunAsUserName is nil",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: nil,
},
- {
- testName: "GMSA cred spec contents that are too long",
- windowsOptions: &core.WindowsSecurityContextOptions{
- GMSACredentialSpec: toPtr(strings.Repeat("a", maxGMSACredentialSpecLength+1)),
- },
- expectedErrorSubstring: "gmsaCredentialSpec size must be under",
+ }, {
+ testName: "a valid RunAsUserName",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("Container. User"),
},
- {
- testName: "RunAsUserName is nil",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: nil,
- },
+ }, {
+ testName: "a valid RunAsUserName with NetBios Domain",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("Network Service\\Container. User"),
},
- {
- testName: "a valid RunAsUserName",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("Container. User"),
- },
+ }, {
+ testName: "a valid RunAsUserName with DNS Domain",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr(strings.Repeat("fOo", 20) + ".liSH\\Container. User"),
},
- {
- testName: "a valid RunAsUserName with NetBios Domain",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("Network Service\\Container. User"),
- },
+ }, {
+ testName: "a valid RunAsUserName with DNS Domain with a single character segment",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr(strings.Repeat("fOo", 20) + ".l\\Container. User"),
},
- {
- testName: "a valid RunAsUserName with DNS Domain",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr(strings.Repeat("fOo", 20) + ".liSH\\Container. User"),
- },
+ }, {
+ testName: "a valid RunAsUserName with a long single segment DNS Domain",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr(strings.Repeat("a", 42) + "\\Container. User"),
},
- {
- testName: "a valid RunAsUserName with DNS Domain with a single character segment",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr(strings.Repeat("fOo", 20) + ".l\\Container. User"),
- },
+ }, {
+ testName: "an empty RunAsUserName",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr(""),
},
- {
- testName: "a valid RunAsUserName with a long single segment DNS Domain",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr(strings.Repeat("a", 42) + "\\Container. User"),
- },
+ expectedErrorSubstring: "runAsUserName cannot be an empty string",
+ }, {
+ testName: "RunAsUserName containing a control character",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("Container\tUser"),
},
- {
- testName: "an empty RunAsUserName",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr(""),
- },
- expectedErrorSubstring: "runAsUserName cannot be an empty string",
+ expectedErrorSubstring: "runAsUserName cannot contain control characters",
+ }, {
+ testName: "RunAsUserName containing too many backslashes",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("Container\\Foo\\Lish"),
},
- {
- testName: "RunAsUserName containing a control character",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("Container\tUser"),
- },
- expectedErrorSubstring: "runAsUserName cannot contain control characters",
+ expectedErrorSubstring: "runAsUserName cannot contain more than one backslash",
+ }, {
+ testName: "RunAsUserName containing backslash but empty Domain",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("\\User"),
},
- {
- testName: "RunAsUserName containing too many backslashes",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("Container\\Foo\\Lish"),
- },
- expectedErrorSubstring: "runAsUserName cannot contain more than one backslash",
+ expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios nor the DNS format",
+ }, {
+ testName: "RunAsUserName containing backslash but empty User",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("Container\\"),
},
- {
- testName: "RunAsUserName containing backslash but empty Domain",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("\\User"),
- },
- expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios nor the DNS format",
+ expectedErrorSubstring: "runAsUserName's User cannot be empty",
+ }, {
+ testName: "RunAsUserName's NetBios Domain is too long",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("NetBios " + strings.Repeat("a", 8) + "\\user"),
},
- {
- testName: "RunAsUserName containing backslash but empty User",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("Container\\"),
- },
- expectedErrorSubstring: "runAsUserName's User cannot be empty",
+ expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios",
+ }, {
+ testName: "RunAsUserName's DNS Domain is too long",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ // even if this tests the max Domain length, the Domain should still be "valid".
+ RunAsUserName: toPtr(strings.Repeat(strings.Repeat("a", 63)+".", 4)[:253] + ".com\\user"),
},
- {
- testName: "RunAsUserName's NetBios Domain is too long",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("NetBios " + strings.Repeat("a", 8) + "\\user"),
- },
- expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios",
+ expectedErrorSubstring: "runAsUserName's Domain length must be under",
+ }, {
+ testName: "RunAsUserName's User is too long",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr(strings.Repeat("a", maxRunAsUserNameUserLength+1)),
},
- {
- testName: "RunAsUserName's DNS Domain is too long",
- windowsOptions: &core.WindowsSecurityContextOptions{
- // even if this tests the max Domain length, the Domain should still be "valid".
- RunAsUserName: toPtr(strings.Repeat(strings.Repeat("a", 63)+".", 4)[:253] + ".com\\user"),
- },
- expectedErrorSubstring: "runAsUserName's Domain length must be under",
+ expectedErrorSubstring: "runAsUserName's User length must not be longer than",
+ }, {
+ testName: "RunAsUserName's User cannot contain only spaces or periods",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("... ..."),
},
- {
- testName: "RunAsUserName's User is too long",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr(strings.Repeat("a", maxRunAsUserNameUserLength+1)),
- },
- expectedErrorSubstring: "runAsUserName's User length must not be longer than",
+ expectedErrorSubstring: "runAsUserName's User cannot contain only periods or spaces",
+ }, {
+ testName: "RunAsUserName's NetBios Domain cannot start with a dot",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr(".FooLish\\User"),
},
- {
- testName: "RunAsUserName's User cannot contain only spaces or periods",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("... ..."),
- },
- expectedErrorSubstring: "runAsUserName's User cannot contain only periods or spaces",
+ expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios",
+ }, {
+ testName: "RunAsUserName's NetBios Domain cannot contain invalid characters",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("Foo? Lish?\\User"),
},
- {
- testName: "RunAsUserName's NetBios Domain cannot start with a dot",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr(".FooLish\\User"),
- },
- expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios",
+ expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios",
+ }, {
+ testName: "RunAsUserName's DNS Domain cannot contain invalid characters",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr(strings.Repeat("a", 32) + ".com-\\user"),
},
- {
- testName: "RunAsUserName's NetBios Domain cannot contain invalid characters",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("Foo? Lish?\\User"),
- },
- expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios",
- },
- {
- testName: "RunAsUserName's DNS Domain cannot contain invalid characters",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr(strings.Repeat("a", 32) + ".com-\\user"),
- },
- expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios nor the DNS format",
- },
- {
- testName: "RunAsUserName's User cannot contain invalid characters",
- windowsOptions: &core.WindowsSecurityContextOptions{
- RunAsUserName: toPtr("Container/User"),
- },
- expectedErrorSubstring: "runAsUserName's User cannot contain the following characters",
+ expectedErrorSubstring: "runAsUserName's Domain doesn't match the NetBios nor the DNS format",
+ }, {
+ testName: "RunAsUserName's User cannot contain invalid characters",
+ windowsOptions: &core.WindowsSecurityContextOptions{
+ RunAsUserName: toPtr("Container/User"),
},
+ expectedErrorSubstring: "runAsUserName's User cannot contain the following characters",
+ },
}
for _, testCase := range testCases {
@@ -21863,34 +20389,28 @@ func TestAlphaVolumePVCDataSource(t *testing.T) {
testName string
claimSpec core.PersistentVolumeClaimSpec
expectedFail bool
- }{
- {
- testName: "test create from valid snapshot source",
- claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"),
- },
- {
- testName: "test create from valid pvc source",
- claimSpec: *testDataSourceInSpec("test_pvc", "PersistentVolumeClaim", ""),
- },
- {
- testName: "test missing name in snapshot datasource should fail",
- claimSpec: *testDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"),
- expectedFail: true,
- },
- {
- testName: "test missing kind in snapshot datasource should fail",
- claimSpec: *testDataSourceInSpec("test_snapshot", "", "snapshot.storage.k8s.io"),
- expectedFail: true,
- },
- {
- testName: "test create from valid generic custom resource source",
- claimSpec: *testDataSourceInSpec("test_generic", "Generic", "generic.storage.k8s.io"),
- },
- {
- testName: "test invalid datasource should fail",
- claimSpec: *testDataSourceInSpec("test_pod", "Pod", ""),
- expectedFail: true,
- },
+ }{{
+ testName: "test create from valid snapshot source",
+ claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"),
+ }, {
+ testName: "test create from valid pvc source",
+ claimSpec: *testDataSourceInSpec("test_pvc", "PersistentVolumeClaim", ""),
+ }, {
+ testName: "test missing name in snapshot datasource should fail",
+ claimSpec: *testDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"),
+ expectedFail: true,
+ }, {
+ testName: "test missing kind in snapshot datasource should fail",
+ claimSpec: *testDataSourceInSpec("test_snapshot", "", "snapshot.storage.k8s.io"),
+ expectedFail: true,
+ }, {
+ testName: "test create from valid generic custom resource source",
+ claimSpec: *testDataSourceInSpec("test_generic", "Generic", "generic.storage.k8s.io"),
+ }, {
+ testName: "test invalid datasource should fail",
+ claimSpec: *testDataSourceInSpec("test_pod", "Pod", ""),
+ expectedFail: true,
+ },
}
for _, tc := range testCases {
@@ -21913,34 +20433,28 @@ func testAnyDataSource(t *testing.T, ds, dsRef bool) {
testName string
claimSpec core.PersistentVolumeClaimSpec
expectedFail bool
- }{
- {
- testName: "test create from valid snapshot source",
- claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"),
- },
- {
- testName: "test create from valid pvc source",
- claimSpec: *testDataSourceInSpec("test_pvc", "PersistentVolumeClaim", ""),
- },
- {
- testName: "test missing name in snapshot datasource should fail",
- claimSpec: *testDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"),
- expectedFail: true,
- },
- {
- testName: "test missing kind in snapshot datasource should fail",
- claimSpec: *testDataSourceInSpec("test_snapshot", "", "snapshot.storage.k8s.io"),
- expectedFail: true,
- },
- {
- testName: "test create from valid generic custom resource source",
- claimSpec: *testDataSourceInSpec("test_generic", "Generic", "generic.storage.k8s.io"),
- },
- {
- testName: "test invalid datasource should fail",
- claimSpec: *testDataSourceInSpec("test_pod", "Pod", ""),
- expectedFail: true,
- },
+ }{{
+ testName: "test create from valid snapshot source",
+ claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"),
+ }, {
+ testName: "test create from valid pvc source",
+ claimSpec: *testDataSourceInSpec("test_pvc", "PersistentVolumeClaim", ""),
+ }, {
+ testName: "test missing name in snapshot datasource should fail",
+ claimSpec: *testDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"),
+ expectedFail: true,
+ }, {
+ testName: "test missing kind in snapshot datasource should fail",
+ claimSpec: *testDataSourceInSpec("test_snapshot", "", "snapshot.storage.k8s.io"),
+ expectedFail: true,
+ }, {
+ testName: "test create from valid generic custom resource source",
+ claimSpec: *testDataSourceInSpec("test_generic", "Generic", "generic.storage.k8s.io"),
+ }, {
+ testName: "test invalid datasource should fail",
+ claimSpec: *testDataSourceInSpec("test_pod", "Pod", ""),
+ expectedFail: true,
+ },
}
for _, tc := range testCases {
@@ -22018,47 +20532,39 @@ func TestCrossNamespaceSource(t *testing.T) {
testName string
expectedFail bool
claimSpec *core.PersistentVolumeClaimSpec
- }{
- {
- testName: "Feature gate enabled and valid xns DataSourceRef specified",
- expectedFail: false,
- claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &goodNS, goodName, false),
- },
- {
- testName: "Feature gate enabled and xns DataSourceRef with PVC source specified",
- expectedFail: false,
- claimSpec: pvcSpecWithCrossNamespaceSource(&coreAPIGroup, pvcKind, &goodNS, goodName, false),
- },
- {
- testName: "Feature gate enabled and xns DataSourceRef with unsupported source specified",
- expectedFail: false,
- claimSpec: pvcSpecWithCrossNamespaceSource(&unsupportedAPIGroup, "UnsupportedKind", &goodNS, goodName, false),
- },
- {
- testName: "Feature gate enabled and xns DataSourceRef with nil apiGroup",
- expectedFail: true,
- claimSpec: pvcSpecWithCrossNamespaceSource(nil, "UnsupportedKind", &goodNS, goodName, false),
- },
- {
- testName: "Feature gate enabled and xns DataSourceRef with invalid namspace specified",
- expectedFail: true,
- claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &badNS, goodName, false),
- },
- {
- testName: "Feature gate enabled and xns DataSourceRef with nil namspace specified",
- expectedFail: false,
- claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, nil, goodName, false),
- },
- {
- testName: "Feature gate enabled and xns DataSourceRef with empty namspace specified",
- expectedFail: false,
- claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &emptyNS, goodName, false),
- },
- {
- testName: "Feature gate enabled and both xns DataSourceRef and DataSource specified",
- expectedFail: true,
- claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &goodNS, goodName, true),
- },
+ }{{
+ testName: "Feature gate enabled and valid xns DataSourceRef specified",
+ expectedFail: false,
+ claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &goodNS, goodName, false),
+ }, {
+ testName: "Feature gate enabled and xns DataSourceRef with PVC source specified",
+ expectedFail: false,
+ claimSpec: pvcSpecWithCrossNamespaceSource(&coreAPIGroup, pvcKind, &goodNS, goodName, false),
+ }, {
+ testName: "Feature gate enabled and xns DataSourceRef with unsupported source specified",
+ expectedFail: false,
+ claimSpec: pvcSpecWithCrossNamespaceSource(&unsupportedAPIGroup, "UnsupportedKind", &goodNS, goodName, false),
+ }, {
+ testName: "Feature gate enabled and xns DataSourceRef with nil apiGroup",
+ expectedFail: true,
+ claimSpec: pvcSpecWithCrossNamespaceSource(nil, "UnsupportedKind", &goodNS, goodName, false),
+ }, {
+ testName: "Feature gate enabled and xns DataSourceRef with invalid namspace specified",
+ expectedFail: true,
+ claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &badNS, goodName, false),
+ }, {
+ testName: "Feature gate enabled and xns DataSourceRef with nil namspace specified",
+ expectedFail: false,
+ claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, nil, goodName, false),
+ }, {
+ testName: "Feature gate enabled and xns DataSourceRef with empty namspace specified",
+ expectedFail: false,
+ claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &emptyNS, goodName, false),
+ }, {
+ testName: "Feature gate enabled and both xns DataSourceRef and DataSource specified",
+ expectedFail: true,
+ claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &goodNS, goodName, true),
+ },
}
for _, tc := range testCases {
@@ -22098,262 +20604,210 @@ func TestValidateTopologySpreadConstraints(t *testing.T) {
constraints []core.TopologySpreadConstraint
wantFieldErrors field.ErrorList
opts PodValidationOptions
- }{
- {
- name: "all required fields ok",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- MinDomains: utilpointer.Int32(3),
- },
- },
- wantFieldErrors: field.ErrorList{},
+ }{{
+ name: "all required fields ok",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ MinDomains: utilpointer.Int32(3),
+ }},
+ wantFieldErrors: field.ErrorList{},
+ }, {
+ name: "missing MaxSkew",
+ constraints: []core.TopologySpreadConstraint{
+ {TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
},
- {
- name: "missing MaxSkew",
- constraints: []core.TopologySpreadConstraint{
- {TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
- },
- wantFieldErrors: []*field.Error{field.Invalid(fieldPathMaxSkew, int32(0), isNotPositiveErrorMsg)},
+ wantFieldErrors: []*field.Error{field.Invalid(fieldPathMaxSkew, int32(0), isNotPositiveErrorMsg)},
+ }, {
+ name: "negative MaxSkew",
+ constraints: []core.TopologySpreadConstraint{
+ {MaxSkew: -1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
},
- {
- name: "negative MaxSkew",
- constraints: []core.TopologySpreadConstraint{
- {MaxSkew: -1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
- },
- wantFieldErrors: []*field.Error{field.Invalid(fieldPathMaxSkew, int32(-1), isNotPositiveErrorMsg)},
+ wantFieldErrors: []*field.Error{field.Invalid(fieldPathMaxSkew, int32(-1), isNotPositiveErrorMsg)},
+ }, {
+ name: "can use MinDomains with ScheduleAnyway, when MinDomains = nil",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.ScheduleAnyway,
+ MinDomains: nil,
+ }},
+ wantFieldErrors: field.ErrorList{},
+ }, {
+ name: "negative minDomains is invalid",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ MinDomains: utilpointer.Int32(-1),
+ }},
+ wantFieldErrors: []*field.Error{field.Invalid(fieldPathMinDomains, utilpointer.Int32(-1), isNotPositiveErrorMsg)},
+ }, {
+ name: "cannot use non-nil MinDomains with ScheduleAnyway",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.ScheduleAnyway,
+ MinDomains: utilpointer.Int32(10),
+ }},
+ wantFieldErrors: []*field.Error{field.Invalid(fieldPathMinDomains, utilpointer.Int32(10), fmt.Sprintf("can only use minDomains if whenUnsatisfiable=%s, not %s", string(core.DoNotSchedule), string(core.ScheduleAnyway)))},
+ }, {
+ name: "use negative MinDomains with ScheduleAnyway(invalid)",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.ScheduleAnyway,
+ MinDomains: utilpointer.Int32(-1),
+ }},
+ wantFieldErrors: []*field.Error{
+ field.Invalid(fieldPathMinDomains, utilpointer.Int32(-1), isNotPositiveErrorMsg),
+ field.Invalid(fieldPathMinDomains, utilpointer.Int32(-1), fmt.Sprintf("can only use minDomains if whenUnsatisfiable=%s, not %s", string(core.DoNotSchedule), string(core.ScheduleAnyway))),
},
- {
- name: "can use MinDomains with ScheduleAnyway, when MinDomains = nil",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.ScheduleAnyway,
- MinDomains: nil,
- },
- },
- wantFieldErrors: field.ErrorList{},
+ }, {
+ name: "missing TopologyKey",
+ constraints: []core.TopologySpreadConstraint{
+ {MaxSkew: 1, WhenUnsatisfiable: core.DoNotSchedule},
},
- {
- name: "negative minDomains is invalid",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- MinDomains: utilpointer.Int32(-1),
- },
- },
- wantFieldErrors: []*field.Error{field.Invalid(fieldPathMinDomains, utilpointer.Int32(-1), isNotPositiveErrorMsg)},
+ wantFieldErrors: []*field.Error{field.Required(fieldPathTopologyKey, "can not be empty")},
+ }, {
+ name: "missing scheduling mode",
+ constraints: []core.TopologySpreadConstraint{
+ {MaxSkew: 1, TopologyKey: "k8s.io/zone"},
},
- {
- name: "cannot use non-nil MinDomains with ScheduleAnyway",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.ScheduleAnyway,
- MinDomains: utilpointer.Int32(10),
- },
- },
- wantFieldErrors: []*field.Error{field.Invalid(fieldPathMinDomains, utilpointer.Int32(10), fmt.Sprintf("can only use minDomains if whenUnsatisfiable=%s, not %s", string(core.DoNotSchedule), string(core.ScheduleAnyway)))},
+ wantFieldErrors: []*field.Error{field.NotSupported(fieldPathWhenUnsatisfiable, core.UnsatisfiableConstraintAction(""), supportedScheduleActions.List())},
+ }, {
+ name: "unsupported scheduling mode",
+ constraints: []core.TopologySpreadConstraint{
+ {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.UnsatisfiableConstraintAction("N/A")},
},
- {
- name: "use negative MinDomains with ScheduleAnyway(invalid)",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.ScheduleAnyway,
- MinDomains: utilpointer.Int32(-1),
- },
- },
- wantFieldErrors: []*field.Error{
- field.Invalid(fieldPathMinDomains, utilpointer.Int32(-1), isNotPositiveErrorMsg),
- field.Invalid(fieldPathMinDomains, utilpointer.Int32(-1), fmt.Sprintf("can only use minDomains if whenUnsatisfiable=%s, not %s", string(core.DoNotSchedule), string(core.ScheduleAnyway))),
- },
+ wantFieldErrors: []*field.Error{field.NotSupported(fieldPathWhenUnsatisfiable, core.UnsatisfiableConstraintAction("N/A"), supportedScheduleActions.List())},
+ }, {
+ name: "multiple constraints ok with all required fields",
+ constraints: []core.TopologySpreadConstraint{
+ {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
+ {MaxSkew: 2, TopologyKey: "k8s.io/node", WhenUnsatisfiable: core.ScheduleAnyway},
},
- {
- name: "missing TopologyKey",
- constraints: []core.TopologySpreadConstraint{
- {MaxSkew: 1, WhenUnsatisfiable: core.DoNotSchedule},
- },
- wantFieldErrors: []*field.Error{field.Required(fieldPathTopologyKey, "can not be empty")},
+ wantFieldErrors: field.ErrorList{},
+ }, {
+ name: "multiple constraints missing TopologyKey on partial ones",
+ constraints: []core.TopologySpreadConstraint{
+ {MaxSkew: 1, WhenUnsatisfiable: core.ScheduleAnyway},
+ {MaxSkew: 2, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
},
- {
- name: "missing scheduling mode",
- constraints: []core.TopologySpreadConstraint{
- {MaxSkew: 1, TopologyKey: "k8s.io/zone"},
- },
- wantFieldErrors: []*field.Error{field.NotSupported(fieldPathWhenUnsatisfiable, core.UnsatisfiableConstraintAction(""), supportedScheduleActions.List())},
+ wantFieldErrors: []*field.Error{field.Required(fieldPathTopologyKey, "can not be empty")},
+ }, {
+ name: "duplicate constraints",
+ constraints: []core.TopologySpreadConstraint{
+ {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
+ {MaxSkew: 2, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
},
- {
- name: "unsupported scheduling mode",
- constraints: []core.TopologySpreadConstraint{
- {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.UnsatisfiableConstraintAction("N/A")},
- },
- wantFieldErrors: []*field.Error{field.NotSupported(fieldPathWhenUnsatisfiable, core.UnsatisfiableConstraintAction("N/A"), supportedScheduleActions.List())},
+ wantFieldErrors: []*field.Error{
+ field.Duplicate(fieldPathTopologyKeyAndWhenUnsatisfiable, fmt.Sprintf("{%v, %v}", "k8s.io/zone", core.DoNotSchedule)),
},
- {
- name: "multiple constraints ok with all required fields",
- constraints: []core.TopologySpreadConstraint{
- {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
- {MaxSkew: 2, TopologyKey: "k8s.io/node", WhenUnsatisfiable: core.ScheduleAnyway},
- },
- wantFieldErrors: field.ErrorList{},
+ }, {
+ name: "supported policy name set on NodeAffinityPolicy and NodeTaintsPolicy",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ NodeAffinityPolicy: &honor,
+ NodeTaintsPolicy: &ignore,
+ }},
+ wantFieldErrors: []*field.Error{},
+ }, {
+ name: "unsupported policy name set on NodeAffinityPolicy",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ NodeAffinityPolicy: &unknown,
+ NodeTaintsPolicy: &ignore,
+ }},
+ wantFieldErrors: []*field.Error{
+ field.NotSupported(nodeAffinityField, &unknown, supportedPodTopologySpreadNodePolicies.List()),
},
- {
- name: "multiple constraints missing TopologyKey on partial ones",
- constraints: []core.TopologySpreadConstraint{
- {MaxSkew: 1, WhenUnsatisfiable: core.ScheduleAnyway},
- {MaxSkew: 2, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
- },
- wantFieldErrors: []*field.Error{field.Required(fieldPathTopologyKey, "can not be empty")},
+ }, {
+ name: "unsupported policy name set on NodeTaintsPolicy",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ NodeAffinityPolicy: &honor,
+ NodeTaintsPolicy: &unknown,
+ }},
+ wantFieldErrors: []*field.Error{
+ field.NotSupported(nodeTaintsField, &unknown, supportedPodTopologySpreadNodePolicies.List()),
},
- {
- name: "duplicate constraints",
- constraints: []core.TopologySpreadConstraint{
- {MaxSkew: 1, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
- {MaxSkew: 2, TopologyKey: "k8s.io/zone", WhenUnsatisfiable: core.DoNotSchedule},
+ }, {
+ name: "key in MatchLabelKeys isn't correctly defined",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ LabelSelector: &metav1.LabelSelector{},
+ WhenUnsatisfiable: core.DoNotSchedule,
+ MatchLabelKeys: []string{"/simple"},
+ }},
+ wantFieldErrors: field.ErrorList{field.Invalid(fieldPathMatchLabelKeys.Index(0), "/simple", "prefix part must be non-empty")},
+ }, {
+ name: "key exists in both matchLabelKeys and labelSelector",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ MatchLabelKeys: []string{"foo"},
+ LabelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "foo",
+ Operator: metav1.LabelSelectorOpNotIn,
+ Values: []string{"value1", "value2"},
+ }},
},
- wantFieldErrors: []*field.Error{
- field.Duplicate(fieldPathTopologyKeyAndWhenUnsatisfiable, fmt.Sprintf("{%v, %v}", "k8s.io/zone", core.DoNotSchedule)),
- },
- },
- {
- name: "supported policy name set on NodeAffinityPolicy and NodeTaintsPolicy",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- NodeAffinityPolicy: &honor,
- NodeTaintsPolicy: &ignore,
- },
- },
- wantFieldErrors: []*field.Error{},
- },
- {
- name: "unsupported policy name set on NodeAffinityPolicy",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- NodeAffinityPolicy: &unknown,
- NodeTaintsPolicy: &ignore,
- },
- },
- wantFieldErrors: []*field.Error{
- field.NotSupported(nodeAffinityField, &unknown, supportedPodTopologySpreadNodePolicies.List()),
- },
- },
- {
- name: "unsupported policy name set on NodeTaintsPolicy",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- NodeAffinityPolicy: &honor,
- NodeTaintsPolicy: &unknown,
- },
- },
- wantFieldErrors: []*field.Error{
- field.NotSupported(nodeTaintsField, &unknown, supportedPodTopologySpreadNodePolicies.List()),
- },
- },
- {
- name: "key in MatchLabelKeys isn't correctly defined",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- LabelSelector: &metav1.LabelSelector{},
- WhenUnsatisfiable: core.DoNotSchedule,
- MatchLabelKeys: []string{"/simple"},
- },
- },
- wantFieldErrors: field.ErrorList{field.Invalid(fieldPathMatchLabelKeys.Index(0), "/simple", "prefix part must be non-empty")},
- },
- {
- name: "key exists in both matchLabelKeys and labelSelector",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- MatchLabelKeys: []string{"foo"},
- LabelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "foo",
- Operator: metav1.LabelSelectorOpNotIn,
- Values: []string{"value1", "value2"},
- },
- },
- },
- },
- },
- wantFieldErrors: field.ErrorList{field.Invalid(fieldPathMatchLabelKeys.Index(0), "foo", "exists in both matchLabelKeys and labelSelector")},
- },
- {
- name: "key in MatchLabelKeys is forbidden to be specified when labelSelector is not set",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- MatchLabelKeys: []string{"foo"},
- },
- },
- wantFieldErrors: field.ErrorList{field.Forbidden(fieldPathMatchLabelKeys, "must not be specified when labelSelector is not set")},
- },
- {
- name: "invalid matchLabels set on labelSelector when AllowInvalidTopologySpreadConstraintLabelSelector is false",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- MinDomains: nil,
- LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "foo"}},
- },
- },
- wantFieldErrors: []*field.Error{field.Invalid(labelSelectorField.Child("matchLabels"), "NoUppercaseOrSpecialCharsLike=Equals", "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')")},
- opts: PodValidationOptions{AllowInvalidTopologySpreadConstraintLabelSelector: false},
- },
- {
- name: "invalid matchLabels set on labelSelector when AllowInvalidTopologySpreadConstraintLabelSelector is true",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- MinDomains: nil,
- LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "foo"}},
- },
- },
- wantFieldErrors: []*field.Error{},
- opts: PodValidationOptions{AllowInvalidTopologySpreadConstraintLabelSelector: true},
- },
- {
- name: "valid matchLabels set on labelSelector when AllowInvalidTopologySpreadConstraintLabelSelector is false",
- constraints: []core.TopologySpreadConstraint{
- {
- MaxSkew: 1,
- TopologyKey: "k8s.io/zone",
- WhenUnsatisfiable: core.DoNotSchedule,
- MinDomains: nil,
- LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "foo"}},
- },
- },
- wantFieldErrors: []*field.Error{},
- opts: PodValidationOptions{AllowInvalidTopologySpreadConstraintLabelSelector: false},
- },
+ }},
+ wantFieldErrors: field.ErrorList{field.Invalid(fieldPathMatchLabelKeys.Index(0), "foo", "exists in both matchLabelKeys and labelSelector")},
+ }, {
+ name: "key in MatchLabelKeys is forbidden to be specified when labelSelector is not set",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ MatchLabelKeys: []string{"foo"},
+ }},
+ wantFieldErrors: field.ErrorList{field.Forbidden(fieldPathMatchLabelKeys, "must not be specified when labelSelector is not set")},
+ }, {
+ name: "invalid matchLabels set on labelSelector when AllowInvalidTopologySpreadConstraintLabelSelector is false",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ MinDomains: nil,
+ LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "foo"}},
+ }},
+ wantFieldErrors: []*field.Error{field.Invalid(labelSelectorField.Child("matchLabels"), "NoUppercaseOrSpecialCharsLike=Equals", "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')")},
+ opts: PodValidationOptions{AllowInvalidTopologySpreadConstraintLabelSelector: false},
+ }, {
+ name: "invalid matchLabels set on labelSelector when AllowInvalidTopologySpreadConstraintLabelSelector is true",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ MinDomains: nil,
+ LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "foo"}},
+ }},
+ wantFieldErrors: []*field.Error{},
+ opts: PodValidationOptions{AllowInvalidTopologySpreadConstraintLabelSelector: true},
+ }, {
+ name: "valid matchLabels set on labelSelector when AllowInvalidTopologySpreadConstraintLabelSelector is false",
+ constraints: []core.TopologySpreadConstraint{{
+ MaxSkew: 1,
+ TopologyKey: "k8s.io/zone",
+ WhenUnsatisfiable: core.DoNotSchedule,
+ MinDomains: nil,
+ LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"foo": "foo"}},
+ }},
+ wantFieldErrors: []*field.Error{},
+ opts: PodValidationOptions{AllowInvalidTopologySpreadConstraintLabelSelector: false},
+ },
}
for _, tc := range testCases {
@@ -22370,14 +20824,13 @@ func TestValidateOverhead(t *testing.T) {
successCase := []struct {
Name string
overhead core.ResourceList
- }{
- {
- Name: "Valid Overhead for CPU + Memory",
- overhead: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- },
+ }{{
+ Name: "Valid Overhead for CPU + Memory",
+ overhead: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
},
+ },
}
for _, tc := range successCase {
if errs := validateOverhead(tc.overhead, field.NewPath("overheads"), PodValidationOptions{}); len(errs) != 0 {
@@ -22388,13 +20841,12 @@ func TestValidateOverhead(t *testing.T) {
errorCase := []struct {
Name string
overhead core.ResourceList
- }{
- {
- Name: "Invalid Overhead Resources",
- overhead: core.ResourceList{
- core.ResourceName("my.org"): resource.MustParse("10m"),
- },
+ }{{
+ Name: "Invalid Overhead Resources",
+ overhead: core.ResourceList{
+ core.ResourceName("my.org"): resource.MustParse("10m"),
},
+ },
}
for _, tc := range errorCase {
if errs := validateOverhead(tc.overhead, field.NewPath("resources"), PodValidationOptions{}); len(errs) == 0 {
@@ -22408,11 +20860,9 @@ func makePod(podName string, podNamespace string, podIPs []core.PodIP) core.Pod
return core.Pod{
ObjectMeta: metav1.ObjectMeta{Name: podName, Namespace: podNamespace},
Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
- },
- },
+ Containers: []core.Container{{
+ Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File",
+ }},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
},
@@ -22425,49 +20875,39 @@ func TestPodIPsValidation(t *testing.T) {
testCases := []struct {
pod core.Pod
expectError bool
- }{
- {
- expectError: false,
- pod: makePod("nil-ips", "ns", nil),
- },
- {
- expectError: false,
- pod: makePod("empty-podips-list", "ns", []core.PodIP{}),
- },
- {
- expectError: false,
- pod: makePod("single-ip-family-6", "ns", []core.PodIP{{IP: "::1"}}),
- },
- {
- expectError: false,
- pod: makePod("single-ip-family-4", "ns", []core.PodIP{{IP: "1.1.1.1"}}),
- },
- {
- expectError: false,
- pod: makePod("dual-stack-4-6", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}}),
- },
- {
- expectError: false,
- pod: makePod("dual-stack-6-4", "ns", []core.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
- },
+ }{{
+ expectError: false,
+ pod: makePod("nil-ips", "ns", nil),
+ }, {
+ expectError: false,
+ pod: makePod("empty-podips-list", "ns", []core.PodIP{}),
+ }, {
+ expectError: false,
+ pod: makePod("single-ip-family-6", "ns", []core.PodIP{{IP: "::1"}}),
+ }, {
+ expectError: false,
+ pod: makePod("single-ip-family-4", "ns", []core.PodIP{{IP: "1.1.1.1"}}),
+ }, {
+ expectError: false,
+ pod: makePod("dual-stack-4-6", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}}),
+ }, {
+ expectError: false,
+ pod: makePod("dual-stack-6-4", "ns", []core.PodIP{{IP: "::1"}, {IP: "1.1.1.1"}}),
+ },
/* failure cases start here */
{
expectError: true,
pod: makePod("invalid-pod-ip", "ns", []core.PodIP{{IP: "this-is-not-an-ip"}}),
- },
- {
+ }, {
expectError: true,
pod: makePod("dualstack-same-ip-family-6", "ns", []core.PodIP{{IP: "::1"}, {IP: "::2"}}),
- },
- {
+ }, {
expectError: true,
pod: makePod("dualstack-same-ip-family-4", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "2.2.2.2"}}),
- },
- {
+ }, {
expectError: true,
pod: makePod("dualstack-repeated-ip-family-6", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}, {IP: "::2"}}),
- },
- {
+ }, {
expectError: true,
pod: makePod("dualstack-repeated-ip-family-4", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}, {IP: "2.2.2.2"}}),
},
@@ -22475,8 +20915,7 @@ func TestPodIPsValidation(t *testing.T) {
{
expectError: true,
pod: makePod("dualstack-duplicate-ip-family-4", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "1.1.1.1"}, {IP: "::1"}}),
- },
- {
+ }, {
expectError: true,
pod: makePod("dualstack-duplicate-ip-family-6", "ns", []core.PodIP{{IP: "1.1.1.1"}, {IP: "::1"}, {IP: "::1"}}),
},
@@ -22529,29 +20968,24 @@ func TestValidateNodeCIDRs(t *testing.T) {
testCases := []struct {
expectError bool
node core.Node
- }{
- {
- expectError: false,
- node: makeNode("nil-pod-cidr", nil),
- },
- {
- expectError: false,
- node: makeNode("empty-pod-cidr", []string{}),
- },
- {
- expectError: false,
- node: makeNode("single-pod-cidr-4", []string{"192.168.0.0/16"}),
- },
- {
- expectError: false,
- node: makeNode("single-pod-cidr-6", []string{"2000::/10"}),
- },
+ }{{
+ expectError: false,
+ node: makeNode("nil-pod-cidr", nil),
+ }, {
+ expectError: false,
+ node: makeNode("empty-pod-cidr", []string{}),
+ }, {
+ expectError: false,
+ node: makeNode("single-pod-cidr-4", []string{"192.168.0.0/16"}),
+ }, {
+ expectError: false,
+ node: makeNode("single-pod-cidr-6", []string{"2000::/10"}),
+ },
{
expectError: false,
node: makeNode("multi-pod-cidr-6-4", []string{"2000::/10", "192.168.0.0/16"}),
- },
- {
+ }, {
expectError: false,
node: makeNode("multi-pod-cidr-4-6", []string{"192.168.0.0/16", "2000::/10"}),
},
@@ -22559,28 +20993,22 @@ func TestValidateNodeCIDRs(t *testing.T) {
{
expectError: true,
node: makeNode("invalid-pod-cidr", []string{"this-is-not-a-valid-cidr"}),
- },
- {
+ }, {
expectError: true,
node: makeNode("duplicate-pod-cidr-4", []string{"10.0.0.1/16", "10.0.0.1/16"}),
- },
- {
+ }, {
expectError: true,
node: makeNode("duplicate-pod-cidr-6", []string{"2000::/10", "2000::/10"}),
- },
- {
+ }, {
expectError: true,
node: makeNode("not-a-dualstack-no-v4", []string{"2000::/10", "3000::/10"}),
- },
- {
+ }, {
expectError: true,
node: makeNode("not-a-dualstack-no-v6", []string{"10.0.0.0/16", "10.1.0.0/16"}),
- },
- {
+ }, {
expectError: true,
node: makeNode("not-a-dualstack-repeated-v6", []string{"2000::/10", "10.0.0.0/16", "3000::/10"}),
- },
- {
+ }, {
expectError: true,
node: makeNode("not-a-dualstack-repeated-v4", []string{"10.0.0.0/16", "3000::/10", "10.1.0.0/16"}),
},
@@ -22606,229 +21034,219 @@ func TestValidateSeccompAnnotationAndField(t *testing.T) {
description string
pod *core.Pod
validation func(*testing.T, string, field.ErrorList, *v1.Pod)
- }{
- {
- description: "Field type unconfined and annotation does not match",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompPodAnnotationKey: "not-matching",
+ }{{
+ description: "Field type unconfined and annotation does not match",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompPodAnnotationKey: "not-matching",
+ },
+ },
+ Spec: core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ SeccompProfile: &core.SeccompProfile{
+ Type: core.SeccompProfileTypeUnconfined,
},
},
- Spec: core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
+ },
+ },
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.NotNil(t, allErrs, desc)
+ },
+ }, {
+ description: "Field type default and annotation does not match",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompPodAnnotationKey: "not-matching",
+ },
+ },
+ Spec: core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ SeccompProfile: &core.SeccompProfile{
+ Type: core.SeccompProfileTypeRuntimeDefault,
+ },
+ },
+ },
+ },
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.NotNil(t, allErrs, desc)
+ },
+ }, {
+ description: "Field type localhost and annotation does not match",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompPodAnnotationKey: "not-matching",
+ },
+ },
+ Spec: core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ SeccompProfile: &core.SeccompProfile{
+ Type: core.SeccompProfileTypeLocalhost,
+ LocalhostProfile: &testProfile,
+ },
+ },
+ },
+ },
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.NotNil(t, allErrs, desc)
+ },
+ }, {
+ description: "Field type localhost and localhost/ prefixed annotation does not match",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompPodAnnotationKey: "localhost/not-matching",
+ },
+ },
+ Spec: core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ SeccompProfile: &core.SeccompProfile{
+ Type: core.SeccompProfileTypeLocalhost,
+ LocalhostProfile: &testProfile,
+ },
+ },
+ },
+ },
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.NotNil(t, allErrs, desc)
+ },
+ }, {
+ description: "Field type unconfined and annotation does not match (container)",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompContainerAnnotationKeyPrefix + containerName: "not-matching",
+ },
+ },
+ Spec: core.PodSpec{
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
SeccompProfile: &core.SeccompProfile{
Type: core.SeccompProfileTypeUnconfined,
},
},
- },
- },
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.NotNil(t, allErrs, desc)
+ }},
},
},
- {
- description: "Field type default and annotation does not match",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompPodAnnotationKey: "not-matching",
- },
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.NotNil(t, allErrs, desc)
+ },
+ }, {
+ description: "Field type default and annotation does not match (container)",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompContainerAnnotationKeyPrefix + containerName: "not-matching",
},
- Spec: core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
+ },
+ Spec: core.PodSpec{
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
SeccompProfile: &core.SeccompProfile{
Type: core.SeccompProfileTypeRuntimeDefault,
},
},
- },
- },
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.NotNil(t, allErrs, desc)
+ }},
},
},
- {
- description: "Field type localhost and annotation does not match",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompPodAnnotationKey: "not-matching",
- },
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.NotNil(t, allErrs, desc)
+ },
+ }, {
+ description: "Field type localhost and annotation does not match (container)",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompContainerAnnotationKeyPrefix + containerName: "not-matching",
},
- Spec: core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
+ },
+ Spec: core.PodSpec{
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
SeccompProfile: &core.SeccompProfile{
Type: core.SeccompProfileTypeLocalhost,
LocalhostProfile: &testProfile,
},
},
- },
- },
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.NotNil(t, allErrs, desc)
+ }},
},
},
- {
- description: "Field type localhost and localhost/ prefixed annotation does not match",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompPodAnnotationKey: "localhost/not-matching",
- },
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.NotNil(t, allErrs, desc)
+ },
+ }, {
+ description: "Field type localhost and localhost/ prefixed annotation does not match (container)",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompContainerAnnotationKeyPrefix + containerName: "localhost/not-matching",
},
- Spec: core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
+ },
+ Spec: core.PodSpec{
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
SeccompProfile: &core.SeccompProfile{
Type: core.SeccompProfileTypeLocalhost,
LocalhostProfile: &testProfile,
},
},
- },
- },
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.NotNil(t, allErrs, desc)
+ }},
},
},
- {
- description: "Field type unconfined and annotation does not match (container)",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompContainerAnnotationKeyPrefix + containerName: "not-matching",
- },
- },
- Spec: core.PodSpec{
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- SeccompProfile: &core.SeccompProfile{
- Type: core.SeccompProfileTypeUnconfined,
- },
- },
- }},
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.NotNil(t, allErrs, desc)
+ },
+ }, {
+ description: "Nil errors must not be appended (pod)",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompPodAnnotationKey: "localhost/anyprofile",
},
},
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.NotNil(t, allErrs, desc)
+ Spec: core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ SeccompProfile: &core.SeccompProfile{
+ Type: "Abc",
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ }},
},
},
- {
- description: "Field type default and annotation does not match (container)",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompContainerAnnotationKeyPrefix + containerName: "not-matching",
- },
- },
- Spec: core.PodSpec{
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- SeccompProfile: &core.SeccompProfile{
- Type: core.SeccompProfileTypeRuntimeDefault,
- },
- },
- }},
- },
- },
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.NotNil(t, allErrs, desc)
- },
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.Empty(t, allErrs, desc)
},
- {
- description: "Field type localhost and annotation does not match (container)",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompContainerAnnotationKeyPrefix + containerName: "not-matching",
- },
- },
- Spec: core.PodSpec{
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- SeccompProfile: &core.SeccompProfile{
- Type: core.SeccompProfileTypeLocalhost,
- LocalhostProfile: &testProfile,
- },
- },
- }},
+ }, {
+ description: "Nil errors must not be appended (container)",
+ pod: &core.Pod{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ v1.SeccompContainerAnnotationKeyPrefix + containerName: "localhost/not-matching",
},
},
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.NotNil(t, allErrs, desc)
- },
- },
- {
- description: "Field type localhost and localhost/ prefixed annotation does not match (container)",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompContainerAnnotationKeyPrefix + containerName: "localhost/not-matching",
- },
- },
- Spec: core.PodSpec{
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- SeccompProfile: &core.SeccompProfile{
- Type: core.SeccompProfileTypeLocalhost,
- LocalhostProfile: &testProfile,
- },
- },
- }},
- },
- },
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.NotNil(t, allErrs, desc)
- },
- },
- {
- description: "Nil errors must not be appended (pod)",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompPodAnnotationKey: "localhost/anyprofile",
- },
- },
- Spec: core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
+ Spec: core.PodSpec{
+ Containers: []core.Container{{
+ SecurityContext: &core.SecurityContext{
SeccompProfile: &core.SeccompProfile{
Type: "Abc",
},
},
- Containers: []core.Container{{
- Name: containerName,
- }},
- },
- },
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.Empty(t, allErrs, desc)
+ Name: containerName,
+ }},
},
},
- {
- description: "Nil errors must not be appended (container)",
- pod: &core.Pod{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- v1.SeccompContainerAnnotationKeyPrefix + containerName: "localhost/not-matching",
- },
- },
- Spec: core.PodSpec{
- Containers: []core.Container{{
- SecurityContext: &core.SecurityContext{
- SeccompProfile: &core.SeccompProfile{
- Type: "Abc",
- },
- },
- Name: containerName,
- }},
- },
- },
- validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
- require.Empty(t, allErrs, desc)
- },
+ validation: func(t *testing.T, desc string, allErrs field.ErrorList, pod *v1.Pod) {
+ require.Empty(t, allErrs, desc)
},
+ },
} {
output := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{}},
@@ -22857,63 +21275,54 @@ func TestValidateSeccompAnnotationsAndFieldsMatch(t *testing.T) {
seccompField *core.SeccompProfile
fldPath *field.Path
expectedErr *field.Error
- }{
- {
- description: "seccompField nil should return empty",
- expectedErr: nil,
- },
- {
- description: "unconfined annotation and SeccompProfileTypeUnconfined should return empty",
- annotationValue: "unconfined",
- seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeUnconfined},
- expectedErr: nil,
- },
- {
- description: "runtime/default annotation and SeccompProfileTypeRuntimeDefault should return empty",
- annotationValue: "runtime/default",
- seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeRuntimeDefault},
- expectedErr: nil,
- },
- {
- description: "docker/default annotation and SeccompProfileTypeRuntimeDefault should return empty",
- annotationValue: "docker/default",
- seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeRuntimeDefault},
- expectedErr: nil,
- },
- {
- description: "localhost/test.json annotation and SeccompProfileTypeLocalhost with correct profile should return empty",
- annotationValue: "localhost/test.json",
- seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeLocalhost, LocalhostProfile: utilpointer.String("test.json")},
- expectedErr: nil,
- },
- {
- description: "localhost/test.json annotation and SeccompProfileTypeLocalhost without profile should error",
- annotationValue: "localhost/test.json",
- seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeLocalhost},
- fldPath: rootFld,
- expectedErr: field.Forbidden(rootFld.Child("localhostProfile"), "seccomp profile in annotation and field must match"),
- },
- {
- description: "localhost/test.json annotation and SeccompProfileTypeLocalhost with different profile should error",
- annotationValue: "localhost/test.json",
- seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeLocalhost, LocalhostProfile: utilpointer.String("different.json")},
- fldPath: rootFld,
- expectedErr: field.Forbidden(rootFld.Child("localhostProfile"), "seccomp profile in annotation and field must match"),
- },
- {
- description: "localhost/test.json annotation and SeccompProfileTypeUnconfined with different profile should error",
- annotationValue: "localhost/test.json",
- seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeUnconfined},
- fldPath: rootFld,
- expectedErr: field.Forbidden(rootFld.Child("type"), "seccomp type in annotation and field must match"),
- },
- {
- description: "localhost/test.json annotation and SeccompProfileTypeRuntimeDefault with different profile should error",
- annotationValue: "localhost/test.json",
- seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeRuntimeDefault},
- fldPath: rootFld,
- expectedErr: field.Forbidden(rootFld.Child("type"), "seccomp type in annotation and field must match"),
- },
+ }{{
+ description: "seccompField nil should return empty",
+ expectedErr: nil,
+ }, {
+ description: "unconfined annotation and SeccompProfileTypeUnconfined should return empty",
+ annotationValue: "unconfined",
+ seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeUnconfined},
+ expectedErr: nil,
+ }, {
+ description: "runtime/default annotation and SeccompProfileTypeRuntimeDefault should return empty",
+ annotationValue: "runtime/default",
+ seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeRuntimeDefault},
+ expectedErr: nil,
+ }, {
+ description: "docker/default annotation and SeccompProfileTypeRuntimeDefault should return empty",
+ annotationValue: "docker/default",
+ seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeRuntimeDefault},
+ expectedErr: nil,
+ }, {
+ description: "localhost/test.json annotation and SeccompProfileTypeLocalhost with correct profile should return empty",
+ annotationValue: "localhost/test.json",
+ seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeLocalhost, LocalhostProfile: utilpointer.String("test.json")},
+ expectedErr: nil,
+ }, {
+ description: "localhost/test.json annotation and SeccompProfileTypeLocalhost without profile should error",
+ annotationValue: "localhost/test.json",
+ seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeLocalhost},
+ fldPath: rootFld,
+ expectedErr: field.Forbidden(rootFld.Child("localhostProfile"), "seccomp profile in annotation and field must match"),
+ }, {
+ description: "localhost/test.json annotation and SeccompProfileTypeLocalhost with different profile should error",
+ annotationValue: "localhost/test.json",
+ seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeLocalhost, LocalhostProfile: utilpointer.String("different.json")},
+ fldPath: rootFld,
+ expectedErr: field.Forbidden(rootFld.Child("localhostProfile"), "seccomp profile in annotation and field must match"),
+ }, {
+ description: "localhost/test.json annotation and SeccompProfileTypeUnconfined with different profile should error",
+ annotationValue: "localhost/test.json",
+ seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeUnconfined},
+ fldPath: rootFld,
+ expectedErr: field.Forbidden(rootFld.Child("type"), "seccomp type in annotation and field must match"),
+ }, {
+ description: "localhost/test.json annotation and SeccompProfileTypeRuntimeDefault with different profile should error",
+ annotationValue: "localhost/test.json",
+ seccompField: &core.SeccompProfile{Type: core.SeccompProfileTypeRuntimeDefault},
+ fldPath: rootFld,
+ expectedErr: field.Forbidden(rootFld.Child("type"), "seccomp type in annotation and field must match"),
+ },
}
for i, test := range tests {
@@ -22929,120 +21338,108 @@ func TestValidatePodTemplateSpecSeccomp(t *testing.T) {
spec *core.PodTemplateSpec
fldPath *field.Path
expectedErr field.ErrorList
- }{
- {
- description: "seccomp field and container annotation must match",
- fldPath: rootFld,
- expectedErr: field.ErrorList{
- field.Forbidden(
- rootFld.Child("spec").Child("containers").Index(1).Child("securityContext").Child("seccompProfile").Child("type"),
- "seccomp type in annotation and field must match"),
- },
- spec: &core.PodTemplateSpec{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- "container.seccomp.security.alpha.kubernetes.io/test2": "unconfined",
- },
- },
- Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "test1",
- Image: "alpine",
- ImagePullPolicy: core.PullAlways,
- TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
- },
- {
- SecurityContext: &core.SecurityContext{
- SeccompProfile: &core.SeccompProfile{
- Type: core.SeccompProfileTypeRuntimeDefault,
- },
- },
- Name: "test2",
- Image: "alpine",
- ImagePullPolicy: core.PullAlways,
- TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
- },
- },
- RestartPolicy: core.RestartPolicyAlways,
- DNSPolicy: core.DNSDefault,
- },
- },
+ }{{
+ description: "seccomp field and container annotation must match",
+ fldPath: rootFld,
+ expectedErr: field.ErrorList{
+ field.Forbidden(
+ rootFld.Child("spec").Child("containers").Index(1).Child("securityContext").Child("seccompProfile").Child("type"),
+ "seccomp type in annotation and field must match"),
},
- {
- description: "seccomp field and pod annotation must match",
- fldPath: rootFld,
- expectedErr: field.ErrorList{
- field.Forbidden(
- rootFld.Child("spec").Child("securityContext").Child("seccompProfile").Child("type"),
- "seccomp type in annotation and field must match"),
- },
- spec: &core.PodTemplateSpec{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- "seccomp.security.alpha.kubernetes.io/pod": "runtime/default",
- },
+ spec: &core.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ "container.seccomp.security.alpha.kubernetes.io/test2": "unconfined",
},
- Spec: core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
+ },
+ Spec: core.PodSpec{
+ Containers: []core.Container{{
+ Name: "test1",
+ Image: "alpine",
+ ImagePullPolicy: core.PullAlways,
+ TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
+ }, {
+ SecurityContext: &core.SecurityContext{
SeccompProfile: &core.SeccompProfile{
- Type: core.SeccompProfileTypeUnconfined,
+ Type: core.SeccompProfileTypeRuntimeDefault,
},
},
- Containers: []core.Container{
- {
- Name: "test",
- Image: "alpine",
- ImagePullPolicy: core.PullAlways,
- TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
- },
- },
- RestartPolicy: core.RestartPolicyAlways,
- DNSPolicy: core.DNSDefault,
- },
+ Name: "test2",
+ Image: "alpine",
+ ImagePullPolicy: core.PullAlways,
+ TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
+ }},
+ RestartPolicy: core.RestartPolicyAlways,
+ DNSPolicy: core.DNSDefault,
},
},
- {
- description: "init seccomp field and container annotation must match",
- fldPath: rootFld,
- expectedErr: field.ErrorList{
- field.Forbidden(
- rootFld.Child("spec").Child("initContainers").Index(0).Child("securityContext").Child("seccompProfile").Child("type"),
- "seccomp type in annotation and field must match"),
+ }, {
+ description: "seccomp field and pod annotation must match",
+ fldPath: rootFld,
+ expectedErr: field.ErrorList{
+ field.Forbidden(
+ rootFld.Child("spec").Child("securityContext").Child("seccompProfile").Child("type"),
+ "seccomp type in annotation and field must match"),
+ },
+ spec: &core.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ "seccomp.security.alpha.kubernetes.io/pod": "runtime/default",
+ },
},
- spec: &core.PodTemplateSpec{
- ObjectMeta: metav1.ObjectMeta{
- Annotations: map[string]string{
- "container.seccomp.security.alpha.kubernetes.io/init-test": "unconfined",
+ Spec: core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ SeccompProfile: &core.SeccompProfile{
+ Type: core.SeccompProfileTypeUnconfined,
},
},
- Spec: core.PodSpec{
- Containers: []core.Container{
- {
- Name: "test",
- Image: "alpine",
- ImagePullPolicy: core.PullAlways,
- TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
- },
- },
- InitContainers: []core.Container{
- {
- Name: "init-test",
- SecurityContext: &core.SecurityContext{
- SeccompProfile: &core.SeccompProfile{
- Type: core.SeccompProfileTypeRuntimeDefault,
- },
- },
- Image: "alpine",
- ImagePullPolicy: core.PullAlways,
- TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
- },
- },
- RestartPolicy: core.RestartPolicyAlways,
- DNSPolicy: core.DNSDefault,
- },
+ Containers: []core.Container{{
+ Name: "test",
+ Image: "alpine",
+ ImagePullPolicy: core.PullAlways,
+ TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
+ }},
+ RestartPolicy: core.RestartPolicyAlways,
+ DNSPolicy: core.DNSDefault,
},
},
+ }, {
+ description: "init seccomp field and container annotation must match",
+ fldPath: rootFld,
+ expectedErr: field.ErrorList{
+ field.Forbidden(
+ rootFld.Child("spec").Child("initContainers").Index(0).Child("securityContext").Child("seccompProfile").Child("type"),
+ "seccomp type in annotation and field must match"),
+ },
+ spec: &core.PodTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ "container.seccomp.security.alpha.kubernetes.io/init-test": "unconfined",
+ },
+ },
+ Spec: core.PodSpec{
+ Containers: []core.Container{{
+ Name: "test",
+ Image: "alpine",
+ ImagePullPolicy: core.PullAlways,
+ TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
+ }},
+ InitContainers: []core.Container{{
+ Name: "init-test",
+ SecurityContext: &core.SecurityContext{
+ SeccompProfile: &core.SeccompProfile{
+ Type: core.SeccompProfileTypeRuntimeDefault,
+ },
+ },
+ Image: "alpine",
+ ImagePullPolicy: core.PullAlways,
+ TerminationMessagePolicy: core.TerminationMessageFallbackToLogsOnError,
+ }},
+ RestartPolicy: core.RestartPolicyAlways,
+ DNSPolicy: core.DNSDefault,
+ },
+ },
+ },
}
for i, test := range tests {
@@ -23057,45 +21454,42 @@ func TestValidateResourceRequirements(t *testing.T) {
name string
requirements core.ResourceRequirements
opts PodValidationOptions
- }{
- {
- name: "limits and requests of hugepage resource are equal",
- requirements: core.ResourceRequirements{
- Limits: core.ResourceList{
- core.ResourceCPU: resource.MustParse("10"),
- core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
- },
- Requests: core.ResourceList{
- core.ResourceCPU: resource.MustParse("10"),
- core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
- },
+ }{{
+ name: "limits and requests of hugepage resource are equal",
+ requirements: core.ResourceRequirements{
+ Limits: core.ResourceList{
+ core.ResourceCPU: resource.MustParse("10"),
+ core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
- opts: PodValidationOptions{},
- },
- {
- name: "limits and requests of memory resource are equal",
- requirements: core.ResourceRequirements{
- Limits: core.ResourceList{
- core.ResourceMemory: resource.MustParse("2Mi"),
- },
- Requests: core.ResourceList{
- core.ResourceMemory: resource.MustParse("2Mi"),
- },
+ Requests: core.ResourceList{
+ core.ResourceCPU: resource.MustParse("10"),
+ core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
- opts: PodValidationOptions{},
},
- {
- name: "limits and requests of cpu resource are equal",
- requirements: core.ResourceRequirements{
- Limits: core.ResourceList{
- core.ResourceCPU: resource.MustParse("10"),
- },
- Requests: core.ResourceList{
- core.ResourceCPU: resource.MustParse("10"),
- },
+ opts: PodValidationOptions{},
+ }, {
+ name: "limits and requests of memory resource are equal",
+ requirements: core.ResourceRequirements{
+ Limits: core.ResourceList{
+ core.ResourceMemory: resource.MustParse("2Mi"),
+ },
+ Requests: core.ResourceList{
+ core.ResourceMemory: resource.MustParse("2Mi"),
},
- opts: PodValidationOptions{},
},
+ opts: PodValidationOptions{},
+ }, {
+ name: "limits and requests of cpu resource are equal",
+ requirements: core.ResourceRequirements{
+ Limits: core.ResourceList{
+ core.ResourceCPU: resource.MustParse("10"),
+ },
+ Requests: core.ResourceList{
+ core.ResourceCPU: resource.MustParse("10"),
+ },
+ },
+ opts: PodValidationOptions{},
+ },
}
for _, tc := range tests {
@@ -23110,19 +21504,18 @@ func TestValidateResourceRequirements(t *testing.T) {
name string
requirements core.ResourceRequirements
opts PodValidationOptions
- }{
- {
- name: "hugepage resource without cpu or memory",
- requirements: core.ResourceRequirements{
- Limits: core.ResourceList{
- core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
- },
- Requests: core.ResourceList{
- core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
- },
+ }{{
+ name: "hugepage resource without cpu or memory",
+ requirements: core.ResourceRequirements{
+ Limits: core.ResourceList{
+ core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
+ },
+ Requests: core.ResourceList{
+ core.ResourceName(core.ResourceHugePagesPrefix + "2Mi"): resource.MustParse("2Mi"),
},
- opts: PodValidationOptions{},
},
+ opts: PodValidationOptions{},
+ },
}
for _, tc := range errTests {
@@ -23183,147 +21576,127 @@ func TestValidateHostUsers(t *testing.T) {
name string
success bool
spec *core.PodSpec
- }{
- {
- name: "empty",
- success: true,
- spec: &core.PodSpec{},
+ }{{
+ name: "empty",
+ success: true,
+ spec: &core.PodSpec{},
+ }, {
+ name: "hostUsers unset",
+ success: true,
+ spec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{},
},
- {
- name: "hostUsers unset",
- success: true,
- spec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{},
+ }, {
+ name: "hostUsers=false",
+ success: true,
+ spec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostUsers: &falseVar,
},
},
- {
- name: "hostUsers=false",
- success: true,
- spec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostUsers: &falseVar,
- },
+ }, {
+ name: "hostUsers=true",
+ success: true,
+ spec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostUsers: &trueVar,
},
},
- {
- name: "hostUsers=true",
- success: true,
- spec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostUsers: &trueVar,
- },
+ }, {
+ name: "hostUsers=false & volumes",
+ success: true,
+ spec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostUsers: &falseVar,
},
- },
- {
- name: "hostUsers=false & volumes",
- success: true,
- spec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostUsers: &falseVar,
- },
- Volumes: []core.Volume{
- {
- Name: "configmap",
- VolumeSource: core.VolumeSource{
- ConfigMap: &core.ConfigMapVolumeSource{
- LocalObjectReference: core.LocalObjectReference{Name: "configmap"},
- },
- },
- },
- {
- Name: "secret",
- VolumeSource: core.VolumeSource{
- Secret: &core.SecretVolumeSource{
- SecretName: "secret",
- },
- },
- },
- {
- Name: "downward-api",
- VolumeSource: core.VolumeSource{
- DownwardAPI: &core.DownwardAPIVolumeSource{},
- },
- },
- {
- Name: "proj",
- VolumeSource: core.VolumeSource{
- Projected: &core.ProjectedVolumeSource{},
- },
- },
- {
- Name: "empty-dir",
- VolumeSource: core.VolumeSource{
- EmptyDir: &core.EmptyDirVolumeSource{},
- },
+ Volumes: []core.Volume{{
+ Name: "configmap",
+ VolumeSource: core.VolumeSource{
+ ConfigMap: &core.ConfigMapVolumeSource{
+ LocalObjectReference: core.LocalObjectReference{Name: "configmap"},
},
},
- },
- },
- {
- name: "hostUsers=false - unsupported volume",
- success: false,
- spec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostUsers: &falseVar,
- },
- Volumes: []core.Volume{
- {
- Name: "host-path",
- VolumeSource: core.VolumeSource{
- HostPath: &core.HostPathVolumeSource{},
- },
+ }, {
+ Name: "secret",
+ VolumeSource: core.VolumeSource{
+ Secret: &core.SecretVolumeSource{
+ SecretName: "secret",
},
},
+ }, {
+ Name: "downward-api",
+ VolumeSource: core.VolumeSource{
+ DownwardAPI: &core.DownwardAPIVolumeSource{},
+ },
+ }, {
+ Name: "proj",
+ VolumeSource: core.VolumeSource{
+ Projected: &core.ProjectedVolumeSource{},
+ },
+ }, {
+ Name: "empty-dir",
+ VolumeSource: core.VolumeSource{
+ EmptyDir: &core.EmptyDirVolumeSource{},
+ },
+ }},
+ },
+ }, {
+ name: "hostUsers=false - unsupported volume",
+ success: false,
+ spec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostUsers: &falseVar,
+ },
+ Volumes: []core.Volume{{
+ Name: "host-path",
+ VolumeSource: core.VolumeSource{
+ HostPath: &core.HostPathVolumeSource{},
+ },
+ }},
+ },
+ }, {
+ // It should ignore unsupported volumes with hostUsers=true.
+ name: "hostUsers=true - unsupported volume",
+ success: true,
+ spec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostUsers: &trueVar,
+ },
+ Volumes: []core.Volume{{
+ Name: "host-path",
+ VolumeSource: core.VolumeSource{
+ HostPath: &core.HostPathVolumeSource{},
+ },
+ }},
+ },
+ }, {
+ name: "hostUsers=false & HostNetwork",
+ success: false,
+ spec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostUsers: &falseVar,
+ HostNetwork: true,
},
},
- {
- // It should ignore unsupported volumes with hostUsers=true.
- name: "hostUsers=true - unsupported volume",
- success: true,
- spec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostUsers: &trueVar,
- },
- Volumes: []core.Volume{
- {
- Name: "host-path",
- VolumeSource: core.VolumeSource{
- HostPath: &core.HostPathVolumeSource{},
- },
- },
- },
+ }, {
+ name: "hostUsers=false & HostPID",
+ success: false,
+ spec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostUsers: &falseVar,
+ HostPID: true,
},
},
- {
- name: "hostUsers=false & HostNetwork",
- success: false,
- spec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostUsers: &falseVar,
- HostNetwork: true,
- },
- },
- },
- {
- name: "hostUsers=false & HostPID",
- success: false,
- spec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostUsers: &falseVar,
- HostPID: true,
- },
- },
- },
- {
- name: "hostUsers=false & HostIPC",
- success: false,
- spec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostUsers: &falseVar,
- HostIPC: true,
- },
+ }, {
+ name: "hostUsers=false & HostIPC",
+ success: false,
+ spec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostUsers: &falseVar,
+ HostIPC: true,
},
},
+ },
}
for _, tc := range cases {
@@ -23351,368 +21724,350 @@ func TestValidateWindowsHostProcessPod(t *testing.T) {
expectError bool
allowPrivileged bool
podSpec *core.PodSpec
- }{
- {
- name: "Spec with feature enabled, pod-wide HostProcess=true, and HostNetwork unset should not validate",
- expectError: true,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
+ }{{
+ name: "Spec with feature enabled, pod-wide HostProcess=true, and HostNetwork unset should not validate",
+ expectError: true,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ }},
+ },
+ }, {
+ name: "Spec with feature enabled, pod-wide HostProcess=ture, and HostNetwork set should validate",
+ expectError: false,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ }},
+ },
+ }, {
+ name: "Spec with feature enabled, pod-wide HostProcess=ture, HostNetwork set, and containers setting HostProcess=true should validate",
+ expectError: false,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
WindowsOptions: &core.WindowsSecurityContextOptions{
HostProcess: &trueVar,
},
},
- Containers: []core.Container{{
- Name: containerName,
- }},
- },
- },
- {
- name: "Spec with feature enabled, pod-wide HostProcess=ture, and HostNetwork set should validate",
- expectError: false,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
+ }},
+ InitContainers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
WindowsOptions: &core.WindowsSecurityContextOptions{
HostProcess: &trueVar,
},
},
- Containers: []core.Container{{
- Name: containerName,
- }},
- },
+ }},
},
- {
- name: "Spec with feature enabled, pod-wide HostProcess=ture, HostNetwork set, and containers setting HostProcess=true should validate",
- expectError: false,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
+ }, {
+ name: "Spec with feature enabled, pod-wide HostProcess=nil, HostNetwork set, and all containers setting HostProcess=true should validate",
+ expectError: false,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
WindowsOptions: &core.WindowsSecurityContextOptions{
HostProcess: &trueVar,
},
},
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }},
- InitContainers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }},
- },
- },
- {
- name: "Spec with feature enabled, pod-wide HostProcess=nil, HostNetwork set, and all containers setting HostProcess=true should validate",
- expectError: false,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
- },
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }},
- InitContainers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }},
- },
- },
- {
- name: "Pods with feature enabled, some containers setting HostProcess=true, and others setting HostProcess=false should not validate",
- expectError: true,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
- },
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }},
- InitContainers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &falseVar,
- },
- },
- }},
- },
- },
- {
- name: "Spec with feature enabled, some containers setting HostProcess=true, and other leaving HostProcess unset should not validate",
- expectError: true,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
- },
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }},
- InitContainers: []core.Container{{
- Name: containerName,
- }},
- },
- },
- {
- name: "Spec with feature enabled, pod-wide HostProcess=true, some containers setting HostProcess=true, and init containers setting HostProcess=false should not validate",
- expectError: true,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
+ }},
+ InitContainers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
WindowsOptions: &core.WindowsSecurityContextOptions{
HostProcess: &trueVar,
},
},
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }},
- InitContainers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &falseVar,
- },
- },
- }},
- },
+ }},
},
- {
- name: "Spec with feature enabled, pod-wide HostProcess=true, some containers setting HostProcess=true, and others setting HostProcess=false should not validate",
- expectError: true,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
+ }, {
+ name: "Pods with feature enabled, some containers setting HostProcess=true, and others setting HostProcess=false should not validate",
+ expectError: true,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
WindowsOptions: &core.WindowsSecurityContextOptions{
HostProcess: &trueVar,
},
},
- Containers: []core.Container{
- {
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }, {
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &falseVar,
- },
- },
- },
- },
- },
- },
- {
- name: "Spec with feature enabled, pod-wide HostProcess=true, some containers setting HostProcess=true, and others leaving HostProcess=nil should validate",
- expectError: false,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }},
- InitContainers: []core.Container{{
- Name: containerName,
- }},
- },
- },
- {
- name: "Spec with feature enabled, pod-wide HostProcess=false, some contaienrs setting HostProccess=true should not validate",
- expectError: true,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
+ }},
+ InitContainers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
WindowsOptions: &core.WindowsSecurityContextOptions{
HostProcess: &falseVar,
},
},
- Containers: []core.Container{{
- Name: containerName,
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- }},
- InitContainers: []core.Container{{
- Name: containerName,
- }},
- },
+ }},
},
- {
- name: "Pod's HostProcess set to true but all containers override to false should not validate",
- expectError: true,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
+ }, {
+ name: "Spec with feature enabled, some containers setting HostProcess=true, and other leaving HostProcess unset should not validate",
+ expectError: true,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
WindowsOptions: &core.WindowsSecurityContextOptions{
HostProcess: &trueVar,
},
},
- Containers: []core.Container{{
- Name: containerName,
+ }},
+ InitContainers: []core.Container{{
+ Name: containerName,
+ }},
+ },
+ }, {
+ name: "Spec with feature enabled, pod-wide HostProcess=true, some containers setting HostProcess=true, and init containers setting HostProcess=false should not validate",
+ expectError: true,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ }},
+ InitContainers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &falseVar,
+ },
+ },
+ }},
+ },
+ }, {
+ name: "Spec with feature enabled, pod-wide HostProcess=true, some containers setting HostProcess=true, and others setting HostProcess=false should not validate",
+ expectError: true,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ }, {
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &falseVar,
+ },
+ },
+ }},
+ },
+ }, {
+ name: "Spec with feature enabled, pod-wide HostProcess=true, some containers setting HostProcess=true, and others leaving HostProcess=nil should validate",
+ expectError: false,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ }},
+ InitContainers: []core.Container{{
+ Name: containerName,
+ }},
+ },
+ }, {
+ name: "Spec with feature enabled, pod-wide HostProcess=false, some contaienrs setting HostProccess=true should not validate",
+ expectError: true,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &falseVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ }},
+ InitContainers: []core.Container{{
+ Name: containerName,
+ }},
+ },
+ }, {
+ name: "Pod's HostProcess set to true but all containers override to false should not validate",
+ expectError: true,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &falseVar,
+ },
+ },
+ }},
+ },
+ }, {
+ name: "Valid HostProcess pod should spec should not validate if allowPrivileged is not set",
+ expectError: true,
+ allowPrivileged: false,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ }},
+ },
+ }, {
+ name: "Non-HostProcess ephemeral container in HostProcess pod should not validate",
+ expectError: true,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ }},
+ EphemeralContainers: []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
SecurityContext: &core.SecurityContext{
WindowsOptions: &core.WindowsSecurityContextOptions{
HostProcess: &falseVar,
},
},
- }},
- },
- },
- {
- name: "Valid HostProcess pod should spec should not validate if allowPrivileged is not set",
- expectError: true,
- allowPrivileged: false,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
},
- Containers: []core.Container{{
- Name: containerName,
+ }},
+ },
+ }, {
+ name: "HostProcess ephemeral container in HostProcess pod should validate",
+ expectError: false,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ SecurityContext: &core.PodSecurityContext{
+ HostNetwork: true,
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &trueVar,
+ },
+ },
+ Containers: []core.Container{{
+ Name: containerName,
+ }},
+ EphemeralContainers: []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{},
+ }},
+ },
+ }, {
+ name: "Non-HostProcess ephemeral container in Non-HostProcess pod should validate",
+ expectError: false,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ Containers: []core.Container{{
+ Name: containerName,
+ }},
+ EphemeralContainers: []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
+ SecurityContext: &core.SecurityContext{
+ WindowsOptions: &core.WindowsSecurityContextOptions{
+ HostProcess: &falseVar,
+ },
+ },
+ },
+ }},
+ },
+ }, {
+ name: "HostProcess ephemeral container in Non-HostProcess pod should not validate",
+ expectError: true,
+ allowPrivileged: true,
+ podSpec: &core.PodSpec{
+ Containers: []core.Container{{
+ Name: containerName,
+ }},
+ EphemeralContainers: []core.EphemeralContainer{{
+ EphemeralContainerCommon: core.EphemeralContainerCommon{
SecurityContext: &core.SecurityContext{
WindowsOptions: &core.WindowsSecurityContextOptions{
HostProcess: &trueVar,
},
},
- }},
- },
- },
- {
- name: "Non-HostProcess ephemeral container in HostProcess pod should not validate",
- expectError: true,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
},
- Containers: []core.Container{{
- Name: containerName,
- }},
- EphemeralContainers: []core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &falseVar,
- },
- },
- },
- }},
- },
- },
- {
- name: "HostProcess ephemeral container in HostProcess pod should validate",
- expectError: false,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- SecurityContext: &core.PodSecurityContext{
- HostNetwork: true,
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- Containers: []core.Container{{
- Name: containerName,
- }},
- EphemeralContainers: []core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{},
- }},
- },
- },
- {
- name: "Non-HostProcess ephemeral container in Non-HostProcess pod should validate",
- expectError: false,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- Containers: []core.Container{{
- Name: containerName,
- }},
- EphemeralContainers: []core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &falseVar,
- },
- },
- },
- }},
- },
- },
- {
- name: "HostProcess ephemeral container in Non-HostProcess pod should not validate",
- expectError: true,
- allowPrivileged: true,
- podSpec: &core.PodSpec{
- Containers: []core.Container{{
- Name: containerName,
- }},
- EphemeralContainers: []core.EphemeralContainer{{
- EphemeralContainerCommon: core.EphemeralContainerCommon{
- SecurityContext: &core.SecurityContext{
- WindowsOptions: &core.WindowsSecurityContextOptions{
- HostProcess: &trueVar,
- },
- },
- },
- }},
- },
+ }},
},
+ },
}
for _, testCase := range testCases {
@@ -23738,37 +22093,31 @@ func TestValidateOS(t *testing.T) {
name string
expectError bool
podSpec *core.PodSpec
- }{
- {
- name: "no OS field, featuregate",
- expectError: false,
- podSpec: &core.PodSpec{OS: nil},
- },
- {
- name: "empty OS field, featuregate",
- expectError: true,
- podSpec: &core.PodSpec{OS: &core.PodOS{}},
- },
- {
- name: "OS field, featuregate, valid OS",
- expectError: false,
- podSpec: &core.PodSpec{OS: &core.PodOS{Name: core.Linux}},
- },
- {
- name: "OS field, featuregate, valid OS",
- expectError: false,
- podSpec: &core.PodSpec{OS: &core.PodOS{Name: core.Windows}},
- },
- {
- name: "OS field, featuregate, empty OS",
- expectError: true,
- podSpec: &core.PodSpec{OS: &core.PodOS{Name: ""}},
- },
- {
- name: "OS field, featuregate, invalid OS",
- expectError: true,
- podSpec: &core.PodSpec{OS: &core.PodOS{Name: "dummyOS"}},
- },
+ }{{
+ name: "no OS field, featuregate",
+ expectError: false,
+ podSpec: &core.PodSpec{OS: nil},
+ }, {
+ name: "empty OS field, featuregate",
+ expectError: true,
+ podSpec: &core.PodSpec{OS: &core.PodOS{}},
+ }, {
+ name: "OS field, featuregate, valid OS",
+ expectError: false,
+ podSpec: &core.PodSpec{OS: &core.PodOS{Name: core.Linux}},
+ }, {
+ name: "OS field, featuregate, valid OS",
+ expectError: false,
+ podSpec: &core.PodSpec{OS: &core.PodOS{Name: core.Windows}},
+ }, {
+ name: "OS field, featuregate, empty OS",
+ expectError: true,
+ podSpec: &core.PodSpec{OS: &core.PodOS{Name: ""}},
+ }, {
+ name: "OS field, featuregate, invalid OS",
+ expectError: true,
+ podSpec: &core.PodSpec{OS: &core.PodOS{Name: "dummyOS"}},
+ },
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
@@ -23817,37 +22166,32 @@ func TestValidatePVSecretReference(t *testing.T) {
args args
expectError bool
expectedError string
- }{
- {
- name: "invalid secret ref name",
- args: args{&core.SecretReference{Name: "$%^&*#", Namespace: "default"}, rootFld},
- expectError: true,
- expectedError: "name.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
- },
- {
- name: "invalid secret ref namespace",
- args: args{&core.SecretReference{Name: "valid", Namespace: "$%^&*#"}, rootFld},
- expectError: true,
- expectedError: "name.namespace: Invalid value: \"$%^&*#\": " + dnsLabelErrMsg,
- },
- {
- name: "invalid secret: missing namespace",
- args: args{&core.SecretReference{Name: "valid"}, rootFld},
- expectError: true,
- expectedError: "name.namespace: Required value",
- },
- {
- name: "invalid secret : missing name",
- args: args{&core.SecretReference{Namespace: "default"}, rootFld},
- expectError: true,
- expectedError: "name.name: Required value",
- },
- {
- name: "valid secret",
- args: args{&core.SecretReference{Name: "valid", Namespace: "default"}, rootFld},
- expectError: false,
- expectedError: "",
- },
+ }{{
+ name: "invalid secret ref name",
+ args: args{&core.SecretReference{Name: "$%^&*#", Namespace: "default"}, rootFld},
+ expectError: true,
+ expectedError: "name.name: Invalid value: \"$%^&*#\": " + dnsSubdomainLabelErrMsg,
+ }, {
+ name: "invalid secret ref namespace",
+ args: args{&core.SecretReference{Name: "valid", Namespace: "$%^&*#"}, rootFld},
+ expectError: true,
+ expectedError: "name.namespace: Invalid value: \"$%^&*#\": " + dnsLabelErrMsg,
+ }, {
+ name: "invalid secret: missing namespace",
+ args: args{&core.SecretReference{Name: "valid"}, rootFld},
+ expectError: true,
+ expectedError: "name.namespace: Required value",
+ }, {
+ name: "invalid secret : missing name",
+ args: args{&core.SecretReference{Namespace: "default"}, rootFld},
+ expectError: true,
+ expectedError: "name.name: Required value",
+ }, {
+ name: "valid secret",
+ args: args{&core.SecretReference{Name: "valid", Namespace: "default"}, rootFld},
+ expectError: false,
+ expectedError: "",
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -23884,27 +22228,23 @@ func TestValidateDynamicResourceAllocation(t *testing.T) {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim-template"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim-template",
- Source: core.ClaimSource{
- ResourceClaimTemplateName: &externalClaimTemplateName,
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim-template",
+ Source: core.ClaimSource{
+ ResourceClaimTemplateName: &externalClaimTemplateName,
},
- },
+ }},
}
goodClaimReference := core.PodSpec{
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim-reference"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim-reference",
- Source: core.ClaimSource{
- ResourceClaimName: &externalClaimName,
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim-reference",
+ Source: core.ClaimSource{
+ ResourceClaimName: &externalClaimName,
},
- },
+ }},
}
successCases := map[string]core.PodSpec{
@@ -23914,28 +22254,23 @@ func TestValidateDynamicResourceAllocation(t *testing.T) {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}, {Name: "another-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- {
- Name: "another-claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }, {
+ Name: "another-claim",
+ Source: goodClaimSource,
+ }},
},
"init container": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
InitContainers: []core.Container{{Name: "ctr-init", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }},
},
}
for k, v := range successCases {
@@ -23951,107 +22286,88 @@ func TestValidateDynamicResourceAllocation(t *testing.T) {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "../my-claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "../my-claim",
+ Source: goodClaimSource,
+ }},
},
"pod claim name with path": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my/claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my/claim",
+ Source: goodClaimSource,
+ }},
},
"pod claim name empty": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "",
+ Source: goodClaimSource,
+ }},
},
"duplicate pod claim entries": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }, {
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }},
},
"resource claim source empty": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: core.ClaimSource{},
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: core.ClaimSource{},
+ }},
},
"resource claim reference and template": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: core.ClaimSource{
- ResourceClaimName: &externalClaimName,
- ResourceClaimTemplateName: &externalClaimTemplateName,
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: core.ClaimSource{
+ ResourceClaimName: &externalClaimName,
+ ResourceClaimTemplateName: &externalClaimTemplateName,
},
- },
+ }},
},
"claim not found": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "no-such-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }},
},
"claim name empty": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: ""}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }},
},
"pod claim name duplicates": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}, {Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }},
},
"no claims defined": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
@@ -24062,28 +22378,23 @@ func TestValidateDynamicResourceAllocation(t *testing.T) {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }, {
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }},
},
"ephemeral container don't support resource requirements": {
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}},
EphemeralContainers: []core.EphemeralContainer{{EphemeralContainerCommon: core.EphemeralContainerCommon{Name: "ctr-ephemeral", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", Resources: core.ResourceRequirements{Claims: []core.ResourceClaim{{Name: "my-claim"}}}}, TargetContainerName: "ctr"}},
RestartPolicy: core.RestartPolicyAlways,
DNSPolicy: core.DNSClusterFirst,
- ResourceClaims: []core.PodResourceClaim{
- {
- Name: "my-claim",
- Source: goodClaimSource,
- },
- },
+ ResourceClaims: []core.PodResourceClaim{{
+ Name: "my-claim",
+ Source: goodClaimSource,
+ }},
},
"invalid claim template name": func() core.PodSpec {
spec := goodClaimTemplate.DeepCopy()
diff --git a/pkg/apis/flowcontrol/validation/validation_test.go b/pkg/apis/flowcontrol/validation/validation_test.go
index 11825564988..7b5f11b69c8 100644
--- a/pkg/apis/flowcontrol/validation/validation_test.go
+++ b/pkg/apis/flowcontrol/validation/validation_test.go
@@ -41,31 +41,23 @@ func TestFlowSchemaValidation(t *testing.T) {
PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
Name: flowcontrol.PriorityLevelConfigurationNameExempt,
},
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindGroup,
- Group: &flowcontrol.GroupSubject{Name: "system:masters"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- ClusterScope: true,
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- NonResourceURLs: []string{"/"},
- },
- },
- },
- },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindGroup,
+ Group: &flowcontrol.GroupSubject{Name: "system:masters"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ ClusterScope: true,
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ NonResourceURLs: []string{"/"},
+ }},
+ }},
}
badCatchAll := flowcontrol.FlowSchemaSpec{
MatchingPrecedence: flowcontrol.FlowSchemaMaxMatchingPrecedence,
@@ -73,836 +65,665 @@ func TestFlowSchemaValidation(t *testing.T) {
Name: flowcontrol.PriorityLevelConfigurationNameCatchAll,
},
DistinguisherMethod: &flowcontrol.FlowDistinguisherMethod{Type: flowcontrol.FlowDistinguisherMethodByUserType},
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindGroup,
- Group: &flowcontrol.GroupSubject{Name: user.AllUnauthenticated},
- },
- {
- Kind: flowcontrol.SubjectKindGroup,
- Group: &flowcontrol.GroupSubject{Name: user.AllAuthenticated},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- ClusterScope: true,
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- NonResourceURLs: []string{"/"},
- },
- },
- },
- },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindGroup,
+ Group: &flowcontrol.GroupSubject{Name: user.AllUnauthenticated},
+ }, {
+ Kind: flowcontrol.SubjectKindGroup,
+ Group: &flowcontrol.GroupSubject{Name: user.AllAuthenticated},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ ClusterScope: true,
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ NonResourceURLs: []string{"/"},
+ }},
+ }},
}
testCases := []struct {
name string
flowSchema *flowcontrol.FlowSchema
expectedErrors field.ErrorList
- }{
- {
- name: "missing both resource and non-resource policy-rule should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
+ }{{
+ name: "missing both resource and non-resource policy-rule should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
},
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.Required(field.NewPath("spec").Child("rules").Index(0), "at least one of resourceRules and nonResourceRules has to be non-empty"),
+ },
+ }, {
+ name: "normal flow-schema w/ * verbs/apiGroups/resources should work",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindGroup,
+ Group: &flowcontrol.GroupSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "malformed Subject union in ServiceAccount case",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindServiceAccount,
+ User: &flowcontrol.UserSubject{Name: "fred"},
+ Group: &flowcontrol.GroupSubject{Name: "fred"},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ NonResourceURLs: []string{"*"},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount"), "serviceAccount is required when subject kind is 'ServiceAccount'"),
+ field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("user"), "user is forbidden when subject kind is not 'User'"),
+ field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("group"), "group is forbidden when subject kind is not 'Group'"),
+ },
+ }, {
+ name: "Subject union malformed in User case",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ Group: &flowcontrol.GroupSubject{Name: "fred"},
+ ServiceAccount: &flowcontrol.ServiceAccountSubject{Namespace: "s", Name: "n"},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ NonResourceURLs: []string{"*"},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount"), "serviceAccount is forbidden when subject kind is not 'ServiceAccount'"),
+ field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("user"), "user is required when subject kind is 'User'"),
+ field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("group"), "group is forbidden when subject kind is not 'Group'"),
+ },
+ }, {
+ name: "malformed Subject union in Group case",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindGroup,
+ User: &flowcontrol.UserSubject{Name: "fred"},
+ ServiceAccount: &flowcontrol.ServiceAccountSubject{Namespace: "s", Name: "n"},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ NonResourceURLs: []string{"*"},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount"), "serviceAccount is forbidden when subject kind is not 'ServiceAccount'"),
+ field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("user"), "user is forbidden when subject kind is not 'User'"),
+ field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("group"), "group is required when subject kind is 'Group'"),
+ },
+ }, {
+ name: "exempt flow-schema should work",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: flowcontrol.FlowSchemaNameExempt,
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 1,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: flowcontrol.PriorityLevelConfigurationNameExempt,
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindGroup,
+ Group: &flowcontrol.GroupSubject{Name: "system:masters"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ ClusterScope: true,
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ NonResourceURLs: []string{"*"},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "bad exempt flow-schema should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: flowcontrol.FlowSchemaNameExempt,
+ },
+ Spec: badExempt,
+ },
+ expectedErrors: field.ErrorList{field.Invalid(field.NewPath("spec"), badExempt, "spec of 'exempt' must equal the fixed value")},
+ }, {
+ name: "bad catch-all flow-schema should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: flowcontrol.FlowSchemaNameCatchAll,
+ },
+ Spec: badCatchAll,
+ },
+ expectedErrors: field.ErrorList{field.Invalid(field.NewPath("spec"), badCatchAll, "spec of 'catch-all' must equal the fixed value")},
+ }, {
+ name: "catch-all flow-schema should work",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: flowcontrol.FlowSchemaNameCatchAll,
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 10000,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: flowcontrol.PriorityLevelConfigurationNameCatchAll,
+ },
+ DistinguisherMethod: &flowcontrol.FlowDistinguisherMethod{Type: flowcontrol.FlowDistinguisherMethodByUserType},
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindGroup,
+ Group: &flowcontrol.GroupSubject{Name: user.AllUnauthenticated},
+ }, {
+ Kind: flowcontrol.SubjectKindGroup,
+ Group: &flowcontrol.GroupSubject{Name: user.AllAuthenticated},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ ClusterScope: true,
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ NonResourceURLs: []string{"*"},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "non-exempt flow-schema with matchingPrecedence==1 should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "fred",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 1,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "exempt",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindGroup,
+ Group: &flowcontrol.GroupSubject{Name: "gorp"},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ NonResourceURLs: []string{"*"},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("matchingPrecedence"), int32(1), "only the schema named 'exempt' may have matchingPrecedence 1")},
+ }, {
+ name: "flow-schema mixes * verbs/apiGroups/resources should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll, "create"},
+ APIGroups: []string{flowcontrol.APIGroupAll, "tak"},
+ Resources: []string{flowcontrol.ResourceAll, "tok"},
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("verbs"), []string{"*", "create"}, "if '*' is present, must not specify other verbs"),
+ field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("apiGroups"), []string{"*", "tak"}, "if '*' is present, must not specify other api groups"),
+ field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("resources"), []string{"*", "tok"}, "if '*' is present, must not specify other resources"),
+ },
+ }, {
+ name: "flow-schema has both resource rules and non-resource rules should work",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ NonResourceURLs: []string{"/apis/*"},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "flow-schema mixes * non-resource URLs should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{"*"},
+ NonResourceURLs: []string{flowcontrol.NonResourceAll, "tik"},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("nonResourceRules").Index(0).Child("nonResourceURLs"), []string{"*", "tik"}, "if '*' is present, must not specify other non-resource URLs"),
+ },
+ }, {
+ name: "invalid subject kind should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: "FooKind",
+ }},
+ NonResourceRules: []flowcontrol.NonResourcePolicyRule{{
+ Verbs: []string{"*"},
+ NonResourceURLs: []string{flowcontrol.NonResourceAll},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("kind"), flowcontrol.SubjectKind("FooKind"), supportedSubjectKinds.List()),
+ },
+ }, {
+ name: "flow-schema w/ invalid verb should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{"feed"},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("verbs"), []string{"feed"}, supportedVerbs.List()),
+ },
+ }, {
+ name: "flow-schema w/ invalid priority level configuration name should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system+++$$",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ }},
+ },
+ },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("priorityLevelConfiguration").Child("name"), "system+++$$", `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])?)*')`),
+ },
+ }, {
+ name: "flow-schema w/ service-account kind missing namespace should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindServiceAccount,
+ ServiceAccount: &flowcontrol.ServiceAccountSubject{
+ Name: "noxu",
},
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Required(field.NewPath("spec").Child("rules").Index(0), "at least one of resourceRules and nonResourceRules has to be non-empty"),
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ }},
},
},
- {
- name: "normal flow-schema w/ * verbs/apiGroups/resources should work",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindGroup,
- Group: &flowcontrol.GroupSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{},
+ expectedErrors: field.ErrorList{
+ field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount").Child("namespace"), "must specify namespace for service account"),
},
- {
- name: "malformed Subject union in ServiceAccount case",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindServiceAccount,
- User: &flowcontrol.UserSubject{Name: "fred"},
- Group: &flowcontrol.GroupSubject{Name: "fred"},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- NonResourceURLs: []string{"*"},
- },
- },
- },
- },
- },
+ }, {
+ name: "flow-schema missing kind should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
},
- expectedErrors: field.ErrorList{
- field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount"), "serviceAccount is required when subject kind is 'ServiceAccount'"),
- field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("user"), "user is forbidden when subject kind is not 'User'"),
- field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("group"), "group is forbidden when subject kind is not 'Group'"),
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: "",
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ }},
},
},
- {
- name: "Subject union malformed in User case",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- Group: &flowcontrol.GroupSubject{Name: "fred"},
- ServiceAccount: &flowcontrol.ServiceAccountSubject{Namespace: "s", Name: "n"},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- NonResourceURLs: []string{"*"},
- },
- },
- },
- },
- },
+ expectedErrors: field.ErrorList{
+ field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("kind"), flowcontrol.SubjectKind(""), supportedSubjectKinds.List()),
+ },
+ }, {
+ name: "Omitted ResourceRule.Namespaces should fail",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
},
- expectedErrors: field.ErrorList{
- field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount"), "serviceAccount is forbidden when subject kind is not 'ServiceAccount'"),
- field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("user"), "user is required when subject kind is 'User'"),
- field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("group"), "group is forbidden when subject kind is not 'Group'"),
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: nil,
+ }},
+ }},
},
},
- {
- name: "malformed Subject union in Group case",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindGroup,
- User: &flowcontrol.UserSubject{Name: "fred"},
- ServiceAccount: &flowcontrol.ServiceAccountSubject{Namespace: "s", Name: "n"},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- NonResourceURLs: []string{"*"},
- },
- },
- },
- },
- },
+ expectedErrors: field.ErrorList{
+ field.Required(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("namespaces"), "resource rules that are not cluster scoped must supply at least one namespace"),
+ },
+ }, {
+ name: "ClusterScope is allowed, with no Namespaces",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
},
- expectedErrors: field.ErrorList{
- field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount"), "serviceAccount is forbidden when subject kind is not 'ServiceAccount'"),
- field.Forbidden(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("user"), "user is forbidden when subject kind is not 'User'"),
- field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("group"), "group is required when subject kind is 'Group'"),
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ ClusterScope: true,
+ }},
+ }},
},
},
- {
- name: "exempt flow-schema should work",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: flowcontrol.FlowSchemaNameExempt,
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 1,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: flowcontrol.PriorityLevelConfigurationNameExempt,
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindGroup,
- Group: &flowcontrol.GroupSubject{Name: "system:masters"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- ClusterScope: true,
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- NonResourceURLs: []string{"*"},
- },
- },
- },
- },
- },
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "ClusterScope is allowed with NamespaceEvery",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
},
- expectedErrors: field.ErrorList{},
- },
- {
- name: "bad exempt flow-schema should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: flowcontrol.FlowSchemaNameExempt,
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
},
- Spec: badExempt,
- },
- expectedErrors: field.ErrorList{field.Invalid(field.NewPath("spec"), badExempt, "spec of 'exempt' must equal the fixed value")},
- },
- {
- name: "bad catch-all flow-schema should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: flowcontrol.FlowSchemaNameCatchAll,
- },
- Spec: badCatchAll,
- },
- expectedErrors: field.ErrorList{field.Invalid(field.NewPath("spec"), badCatchAll, "spec of 'catch-all' must equal the fixed value")},
- },
- {
- name: "catch-all flow-schema should work",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: flowcontrol.FlowSchemaNameCatchAll,
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 10000,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: flowcontrol.PriorityLevelConfigurationNameCatchAll,
- },
- DistinguisherMethod: &flowcontrol.FlowDistinguisherMethod{Type: flowcontrol.FlowDistinguisherMethodByUserType},
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindGroup,
- Group: &flowcontrol.GroupSubject{Name: user.AllUnauthenticated},
- },
- {
- Kind: flowcontrol.SubjectKindGroup,
- Group: &flowcontrol.GroupSubject{Name: user.AllAuthenticated},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- ClusterScope: true,
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- NonResourceURLs: []string{"*"},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{},
- },
- {
- name: "non-exempt flow-schema with matchingPrecedence==1 should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "fred",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 1,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "exempt",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindGroup,
- Group: &flowcontrol.GroupSubject{Name: "gorp"},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- NonResourceURLs: []string{"*"},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("matchingPrecedence"), int32(1), "only the schema named 'exempt' may have matchingPrecedence 1")},
- },
- {
- name: "flow-schema mixes * verbs/apiGroups/resources should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll, "create"},
- APIGroups: []string{flowcontrol.APIGroupAll, "tak"},
- Resources: []string{flowcontrol.ResourceAll, "tok"},
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("verbs"), []string{"*", "create"}, "if '*' is present, must not specify other verbs"),
- field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("apiGroups"), []string{"*", "tak"}, "if '*' is present, must not specify other api groups"),
- field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("resources"), []string{"*", "tok"}, "if '*' is present, must not specify other resources"),
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ ClusterScope: true,
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ }},
},
},
- {
- name: "flow-schema has both resource rules and non-resource rules should work",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- NonResourceURLs: []string{"/apis/*"},
- },
- },
- },
- },
- },
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "NamespaceEvery may not be combined with particulars",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
},
- expectedErrors: field.ErrorList{},
- },
- {
- name: "flow-schema mixes * non-resource URLs should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
},
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{"*"},
- NonResourceURLs: []string{flowcontrol.NonResourceAll, "tik"},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("nonResourceRules").Index(0).Child("nonResourceURLs"), []string{"*", "tik"}, "if '*' is present, must not specify other non-resource URLs"),
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: []string{"foo", flowcontrol.NamespaceEvery},
+ }},
+ }},
},
},
- {
- name: "invalid subject kind should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: "FooKind",
- },
- },
- NonResourceRules: []flowcontrol.NonResourcePolicyRule{
- {
- Verbs: []string{"*"},
- NonResourceURLs: []string{flowcontrol.NonResourceAll},
- },
- },
- },
- },
- },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("namespaces"), []string{"foo", flowcontrol.NamespaceEvery}, "if '*' is present, must not specify other namespaces"),
+ },
+ }, {
+ name: "ResourceRule.Namespaces must be well formed",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
},
- expectedErrors: field.ErrorList{
- field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("kind"), flowcontrol.SubjectKind("FooKind"), supportedSubjectKinds.List()),
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 50,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: []string{"-foo"},
+ }},
+ }},
},
},
- {
- name: "flow-schema w/ invalid verb should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{"feed"},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- },
- },
- },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("namespaces").Index(0), "-foo", nsErrIntro+`a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')`),
+ },
+ }, {
+ name: "MatchingPrecedence must not be greater than 10000",
+ flowSchema: &flowcontrol.FlowSchema{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
},
- expectedErrors: field.ErrorList{
- field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("verbs"), []string{"feed"}, supportedVerbs.List()),
+ Spec: flowcontrol.FlowSchemaSpec{
+ MatchingPrecedence: 10001,
+ PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
+ Name: "system-bar",
+ },
+ Rules: []flowcontrol.PolicyRulesWithSubjects{{
+ Subjects: []flowcontrol.Subject{{
+ Kind: flowcontrol.SubjectKindUser,
+ User: &flowcontrol.UserSubject{Name: "noxu"},
+ }},
+ ResourceRules: []flowcontrol.ResourcePolicyRule{{
+ Verbs: []string{flowcontrol.VerbAll},
+ APIGroups: []string{flowcontrol.APIGroupAll},
+ Resources: []string{flowcontrol.ResourceAll},
+ Namespaces: []string{flowcontrol.NamespaceEvery},
+ }},
+ }},
},
},
- {
- name: "flow-schema w/ invalid priority level configuration name should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system+++$$",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("priorityLevelConfiguration").Child("name"), "system+++$$", `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])?)*')`),
- },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("matchingPrecedence"), int32(10001), "must not be greater than 10000"),
},
- {
- name: "flow-schema w/ service-account kind missing namespace should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindServiceAccount,
- ServiceAccount: &flowcontrol.ServiceAccountSubject{
- Name: "noxu",
- },
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Required(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("serviceAccount").Child("namespace"), "must specify namespace for service account"),
- },
- },
- {
- name: "flow-schema missing kind should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: "",
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.NotSupported(field.NewPath("spec").Child("rules").Index(0).Child("subjects").Index(0).Child("kind"), flowcontrol.SubjectKind(""), supportedSubjectKinds.List()),
- },
- },
- {
- name: "Omitted ResourceRule.Namespaces should fail",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: nil,
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Required(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("namespaces"), "resource rules that are not cluster scoped must supply at least one namespace"),
- },
- },
- {
- name: "ClusterScope is allowed, with no Namespaces",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- ClusterScope: true,
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{},
- },
- {
- name: "ClusterScope is allowed with NamespaceEvery",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- ClusterScope: true,
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{},
- },
- {
- name: "NamespaceEvery may not be combined with particulars",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: []string{"foo", flowcontrol.NamespaceEvery},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("namespaces"), []string{"foo", flowcontrol.NamespaceEvery}, "if '*' is present, must not specify other namespaces"),
- },
- },
- {
- name: "ResourceRule.Namespaces must be well formed",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 50,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: []string{"-foo"},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("rules").Index(0).Child("resourceRules").Index(0).Child("namespaces").Index(0), "-foo", nsErrIntro+`a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')`),
- },
- },
- {
- name: "MatchingPrecedence must not be greater than 10000",
- flowSchema: &flowcontrol.FlowSchema{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.FlowSchemaSpec{
- MatchingPrecedence: 10001,
- PriorityLevelConfiguration: flowcontrol.PriorityLevelConfigurationReference{
- Name: "system-bar",
- },
- Rules: []flowcontrol.PolicyRulesWithSubjects{
- {
- Subjects: []flowcontrol.Subject{
- {
- Kind: flowcontrol.SubjectKindUser,
- User: &flowcontrol.UserSubject{Name: "noxu"},
- },
- },
- ResourceRules: []flowcontrol.ResourcePolicyRule{
- {
- Verbs: []string{flowcontrol.VerbAll},
- APIGroups: []string{flowcontrol.APIGroupAll},
- Resources: []string{flowcontrol.ResourceAll},
- Namespaces: []string{flowcontrol.NamespaceEvery},
- },
- },
- },
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("matchingPrecedence"), int32(10001), "must not be greater than 10000"),
- },
- },
- }
+ }}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
errs := ValidateFlowSchema(testCase.flowSchema)
@@ -926,254 +747,239 @@ func TestPriorityLevelConfigurationValidation(t *testing.T) {
name string
priorityLevelConfiguration *flowcontrol.PriorityLevelConfiguration
expectedErrors field.ErrorList
- }{
- {
- name: "exempt should work",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: flowcontrol.PriorityLevelConfigurationNameExempt,
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementExempt,
- },
+ }{{
+ name: "exempt should work",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: flowcontrol.PriorityLevelConfigurationNameExempt,
},
- expectedErrors: field.ErrorList{},
- },
- {
- name: "wrong exempt spec should fail",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: flowcontrol.PriorityLevelConfigurationNameExempt,
- },
- Spec: badSpec,
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("type"), flowcontrol.PriorityLevelEnablementLimited, "type must be 'Exempt' if and only if name is 'exempt'"),
- field.Invalid(field.NewPath("spec"), badSpec, "spec of 'exempt' must equal the fixed value"),
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementExempt,
},
},
- {
- name: "limited requires more details",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "broken-limited",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- },
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "wrong exempt spec should fail",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: flowcontrol.PriorityLevelConfigurationNameExempt,
},
- expectedErrors: field.ErrorList{field.Required(field.NewPath("spec").Child("limited"), "must not be empty when type is Limited")},
+ Spec: badSpec,
},
- {
- name: "max-in-flight should work",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "max-in-flight",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 42,
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeReject},
- },
- },
- },
- expectedErrors: field.ErrorList{},
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("type"), flowcontrol.PriorityLevelEnablementLimited, "type must be 'Exempt' if and only if name is 'exempt'"),
+ field.Invalid(field.NewPath("spec"), badSpec, "spec of 'exempt' must equal the fixed value"),
},
- {
- name: "forbid queuing details when not queuing",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 100,
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeReject,
- Queuing: &flowcontrol.QueuingConfiguration{
- Queues: 512,
- HandSize: 4,
- QueueLengthLimit: 100,
- }}}},
+ }, {
+ name: "limited requires more details",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "broken-limited",
},
- expectedErrors: field.ErrorList{field.Forbidden(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing"), "must be nil if limited.limitResponse.type is not Limited")},
- },
- {
- name: "wrong backstop spec should fail",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: flowcontrol.PriorityLevelConfigurationNameCatchAll,
- },
- Spec: badSpec,
- },
- expectedErrors: field.ErrorList{field.Invalid(field.NewPath("spec"), badSpec, "spec of 'catch-all' must equal the fixed value")},
- },
- {
- name: "backstop should work",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: flowcontrol.PriorityLevelConfigurationNameCatchAll,
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 5,
- LendablePercent: pointer.Int32(0),
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeReject,
- }}},
- },
- expectedErrors: field.ErrorList{},
- },
- {
- name: "broken queuing level should fail",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 100,
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeQueue,
- }}},
- },
- expectedErrors: field.ErrorList{field.Required(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing"), "must not be empty if limited.limitResponse.type is Limited")},
- },
- {
- name: "normal customized priority level should work",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 100,
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeQueue,
- Queuing: &flowcontrol.QueuingConfiguration{
- Queues: 512,
- HandSize: 4,
- QueueLengthLimit: 100,
- }}}},
- },
- expectedErrors: field.ErrorList{},
- },
- {
- name: "customized priority level w/ overflowing handSize/queues should fail 1",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 100,
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeQueue,
- Queuing: &flowcontrol.QueuingConfiguration{
- QueueLengthLimit: 100,
- Queues: 512,
- HandSize: 8,
- }}}},
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("handSize"), int32(8), "required entropy bits of deckSize 512 and handSize 8 should not be greater than 60"),
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
},
},
- {
- name: "customized priority level w/ overflowing handSize/queues should fail 2",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 100,
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeQueue,
- Queuing: &flowcontrol.QueuingConfiguration{
- QueueLengthLimit: 100,
- Queues: 128,
- HandSize: 10,
- }}}},
+ expectedErrors: field.ErrorList{field.Required(field.NewPath("spec").Child("limited"), "must not be empty when type is Limited")},
+ }, {
+ name: "max-in-flight should work",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "max-in-flight",
},
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("handSize"), int32(10), "required entropy bits of deckSize 128 and handSize 10 should not be greater than 60"),
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 42,
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeReject},
+ },
},
},
- {
- name: "customized priority level w/ overflowing handSize/queues should fail 3",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 100,
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeQueue,
- Queuing: &flowcontrol.QueuingConfiguration{
- QueueLengthLimit: 100,
- Queues: math.MaxInt32,
- HandSize: 3,
- }}}},
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("handSize"), int32(3), "required entropy bits of deckSize 2147483647 and handSize 3 should not be greater than 60"),
- field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("queues"), int32(math.MaxInt32), "must not be greater than 10000000"),
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "forbid queuing details when not queuing",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
},
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 100,
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeReject,
+ Queuing: &flowcontrol.QueuingConfiguration{
+ Queues: 512,
+ HandSize: 4,
+ QueueLengthLimit: 100,
+ }}}},
},
- {
- name: "customized priority level w/ handSize=2 and queues=10^7 should work",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 100,
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeQueue,
- Queuing: &flowcontrol.QueuingConfiguration{
- QueueLengthLimit: 100,
- Queues: 10 * 1000 * 1000, // 10^7
- HandSize: 2,
- }}}},
+ expectedErrors: field.ErrorList{field.Forbidden(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing"), "must be nil if limited.limitResponse.type is not Limited")},
+ }, {
+ name: "wrong backstop spec should fail",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: flowcontrol.PriorityLevelConfigurationNameCatchAll,
},
- expectedErrors: field.ErrorList{},
+ Spec: badSpec,
},
- {
- name: "customized priority level w/ handSize greater than queues should fail",
- priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
- ObjectMeta: metav1.ObjectMeta{
- Name: "system-foo",
- },
- Spec: flowcontrol.PriorityLevelConfigurationSpec{
- Type: flowcontrol.PriorityLevelEnablementLimited,
- Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
- NominalConcurrencyShares: 100,
- LimitResponse: flowcontrol.LimitResponse{
- Type: flowcontrol.LimitResponseTypeQueue,
- Queuing: &flowcontrol.QueuingConfiguration{
- QueueLengthLimit: 100,
- Queues: 7,
- HandSize: 8,
- }}}},
- },
- expectedErrors: field.ErrorList{
- field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("handSize"), int32(8), "should not be greater than queues (7)"),
+ expectedErrors: field.ErrorList{field.Invalid(field.NewPath("spec"), badSpec, "spec of 'catch-all' must equal the fixed value")},
+ }, {
+ name: "backstop should work",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: flowcontrol.PriorityLevelConfigurationNameCatchAll,
},
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 5,
+ LendablePercent: pointer.Int32(0),
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeReject,
+ }}},
},
- }
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "broken queuing level should fail",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 100,
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeQueue,
+ }}},
+ },
+ expectedErrors: field.ErrorList{field.Required(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing"), "must not be empty if limited.limitResponse.type is Limited")},
+ }, {
+ name: "normal customized priority level should work",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 100,
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeQueue,
+ Queuing: &flowcontrol.QueuingConfiguration{
+ Queues: 512,
+ HandSize: 4,
+ QueueLengthLimit: 100,
+ }}}},
+ },
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "customized priority level w/ overflowing handSize/queues should fail 1",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 100,
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeQueue,
+ Queuing: &flowcontrol.QueuingConfiguration{
+ QueueLengthLimit: 100,
+ Queues: 512,
+ HandSize: 8,
+ }}}},
+ },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("handSize"), int32(8), "required entropy bits of deckSize 512 and handSize 8 should not be greater than 60"),
+ },
+ }, {
+ name: "customized priority level w/ overflowing handSize/queues should fail 2",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 100,
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeQueue,
+ Queuing: &flowcontrol.QueuingConfiguration{
+ QueueLengthLimit: 100,
+ Queues: 128,
+ HandSize: 10,
+ }}}},
+ },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("handSize"), int32(10), "required entropy bits of deckSize 128 and handSize 10 should not be greater than 60"),
+ },
+ }, {
+ name: "customized priority level w/ overflowing handSize/queues should fail 3",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 100,
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeQueue,
+ Queuing: &flowcontrol.QueuingConfiguration{
+ QueueLengthLimit: 100,
+ Queues: math.MaxInt32,
+ HandSize: 3,
+ }}}},
+ },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("handSize"), int32(3), "required entropy bits of deckSize 2147483647 and handSize 3 should not be greater than 60"),
+ field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("queues"), int32(math.MaxInt32), "must not be greater than 10000000"),
+ },
+ }, {
+ name: "customized priority level w/ handSize=2 and queues=10^7 should work",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 100,
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeQueue,
+ Queuing: &flowcontrol.QueuingConfiguration{
+ QueueLengthLimit: 100,
+ Queues: 10 * 1000 * 1000, // 10^7
+ HandSize: 2,
+ }}}},
+ },
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "customized priority level w/ handSize greater than queues should fail",
+ priorityLevelConfiguration: &flowcontrol.PriorityLevelConfiguration{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "system-foo",
+ },
+ Spec: flowcontrol.PriorityLevelConfigurationSpec{
+ Type: flowcontrol.PriorityLevelEnablementLimited,
+ Limited: &flowcontrol.LimitedPriorityLevelConfiguration{
+ NominalConcurrencyShares: 100,
+ LimitResponse: flowcontrol.LimitResponse{
+ Type: flowcontrol.LimitResponseTypeQueue,
+ Queuing: &flowcontrol.QueuingConfiguration{
+ QueueLengthLimit: 100,
+ Queues: 7,
+ HandSize: 8,
+ }}}},
+ },
+ expectedErrors: field.ErrorList{
+ field.Invalid(field.NewPath("spec").Child("limited").Child("limitResponse").Child("queuing").Child("handSize"), int32(8), "should not be greater than queues (7)"),
+ },
+ }}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
errs := ValidatePriorityLevelConfiguration(testCase.priorityLevelConfiguration, flowcontrolv1beta3.SchemeGroupVersion)
@@ -1189,42 +995,33 @@ func TestValidateFlowSchemaStatus(t *testing.T) {
name string
status *flowcontrol.FlowSchemaStatus
expectedErrors field.ErrorList
- }{
- {
- name: "empty status should work",
- status: &flowcontrol.FlowSchemaStatus{},
- expectedErrors: field.ErrorList{},
+ }{{
+ name: "empty status should work",
+ status: &flowcontrol.FlowSchemaStatus{},
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "duplicate key should fail",
+ status: &flowcontrol.FlowSchemaStatus{
+ Conditions: []flowcontrol.FlowSchemaCondition{{
+ Type: "1",
+ }, {
+ Type: "1",
+ }},
},
- {
- name: "duplicate key should fail",
- status: &flowcontrol.FlowSchemaStatus{
- Conditions: []flowcontrol.FlowSchemaCondition{
- {
- Type: "1",
- },
- {
- Type: "1",
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Duplicate(field.NewPath("status").Child("conditions").Index(1).Child("type"), flowcontrol.FlowSchemaConditionType("1")),
- },
+ expectedErrors: field.ErrorList{
+ field.Duplicate(field.NewPath("status").Child("conditions").Index(1).Child("type"), flowcontrol.FlowSchemaConditionType("1")),
},
- {
- name: "missing key should fail",
- status: &flowcontrol.FlowSchemaStatus{
- Conditions: []flowcontrol.FlowSchemaCondition{
- {
- Type: "",
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Required(field.NewPath("status").Child("conditions").Index(0).Child("type"), "must not be empty"),
- },
+ }, {
+ name: "missing key should fail",
+ status: &flowcontrol.FlowSchemaStatus{
+ Conditions: []flowcontrol.FlowSchemaCondition{{
+ Type: "",
+ }},
},
- }
+ expectedErrors: field.ErrorList{
+ field.Required(field.NewPath("status").Child("conditions").Index(0).Child("type"), "must not be empty"),
+ },
+ }}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
errs := ValidateFlowSchemaStatus(testCase.status, field.NewPath("status"))
@@ -1240,42 +1037,33 @@ func TestValidatePriorityLevelConfigurationStatus(t *testing.T) {
name string
status *flowcontrol.PriorityLevelConfigurationStatus
expectedErrors field.ErrorList
- }{
- {
- name: "empty status should work",
- status: &flowcontrol.PriorityLevelConfigurationStatus{},
- expectedErrors: field.ErrorList{},
+ }{{
+ name: "empty status should work",
+ status: &flowcontrol.PriorityLevelConfigurationStatus{},
+ expectedErrors: field.ErrorList{},
+ }, {
+ name: "duplicate key should fail",
+ status: &flowcontrol.PriorityLevelConfigurationStatus{
+ Conditions: []flowcontrol.PriorityLevelConfigurationCondition{{
+ Type: "1",
+ }, {
+ Type: "1",
+ }},
},
- {
- name: "duplicate key should fail",
- status: &flowcontrol.PriorityLevelConfigurationStatus{
- Conditions: []flowcontrol.PriorityLevelConfigurationCondition{
- {
- Type: "1",
- },
- {
- Type: "1",
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Duplicate(field.NewPath("status").Child("conditions").Index(1).Child("type"), flowcontrol.PriorityLevelConfigurationConditionType("1")),
- },
+ expectedErrors: field.ErrorList{
+ field.Duplicate(field.NewPath("status").Child("conditions").Index(1).Child("type"), flowcontrol.PriorityLevelConfigurationConditionType("1")),
},
- {
- name: "missing key should fail",
- status: &flowcontrol.PriorityLevelConfigurationStatus{
- Conditions: []flowcontrol.PriorityLevelConfigurationCondition{
- {
- Type: "",
- },
- },
- },
- expectedErrors: field.ErrorList{
- field.Required(field.NewPath("status").Child("conditions").Index(0).Child("type"), "must not be empty"),
- },
+ }, {
+ name: "missing key should fail",
+ status: &flowcontrol.PriorityLevelConfigurationStatus{
+ Conditions: []flowcontrol.PriorityLevelConfigurationCondition{{
+ Type: "",
+ }},
},
- }
+ expectedErrors: field.ErrorList{
+ field.Required(field.NewPath("status").Child("conditions").Index(0).Child("type"), "must not be empty"),
+ },
+ }}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
errs := ValidatePriorityLevelConfigurationStatus(testCase.status, field.NewPath("status"))
@@ -1291,73 +1079,59 @@ func TestValidateNonResourceURLPath(t *testing.T) {
name string
path string
expectingError bool
- }{
- {
- name: "empty string should fail",
- path: "",
- expectingError: true,
- },
- {
- name: "no slash should fail",
- path: "foo",
- expectingError: true,
- },
- {
- name: "single slash should work",
- path: "/",
- expectingError: false,
- },
- {
- name: "continuous slash should fail",
- path: "//",
- expectingError: true,
- },
- {
- name: "/foo slash should work",
- path: "/foo",
- expectingError: false,
- },
- {
- name: "multiple continuous slashes should fail",
- path: "/////",
- expectingError: true,
- },
- {
- name: "ending up with slash should work",
- path: "/apis/",
- expectingError: false,
- },
- {
- name: "ending up with wildcard should work",
- path: "/healthz/*",
- expectingError: false,
- },
- {
- name: "single wildcard inside the path should fail",
- path: "/healthz/*/foo",
- expectingError: true,
- },
- {
- name: "white-space in the path should fail",
- path: "/healthz/foo bar",
- expectingError: true,
- },
- {
- name: "wildcard plus plain path should fail",
- path: "/health*",
- expectingError: true,
- },
- {
- name: "wildcard plus plain path should fail 2",
- path: "/health*/foo",
- expectingError: true,
- },
- {
- name: "multiple wildcard internal and suffix should fail",
- path: "/*/*",
- expectingError: true,
- },
- }
+ }{{
+ name: "empty string should fail",
+ path: "",
+ expectingError: true,
+ }, {
+ name: "no slash should fail",
+ path: "foo",
+ expectingError: true,
+ }, {
+ name: "single slash should work",
+ path: "/",
+ expectingError: false,
+ }, {
+ name: "continuous slash should fail",
+ path: "//",
+ expectingError: true,
+ }, {
+ name: "/foo slash should work",
+ path: "/foo",
+ expectingError: false,
+ }, {
+ name: "multiple continuous slashes should fail",
+ path: "/////",
+ expectingError: true,
+ }, {
+ name: "ending up with slash should work",
+ path: "/apis/",
+ expectingError: false,
+ }, {
+ name: "ending up with wildcard should work",
+ path: "/healthz/*",
+ expectingError: false,
+ }, {
+ name: "single wildcard inside the path should fail",
+ path: "/healthz/*/foo",
+ expectingError: true,
+ }, {
+ name: "white-space in the path should fail",
+ path: "/healthz/foo bar",
+ expectingError: true,
+ }, {
+ name: "wildcard plus plain path should fail",
+ path: "/health*",
+ expectingError: true,
+ }, {
+ name: "wildcard plus plain path should fail 2",
+ path: "/health*/foo",
+ expectingError: true,
+ }, {
+ name: "multiple wildcard internal and suffix should fail",
+ path: "/*/*",
+ expectingError: true,
+ }}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
err := ValidateNonResourceURLPath(testCase.path, field.NewPath(""))
@@ -1378,47 +1152,39 @@ func TestValidateLimitedPriorityLevelConfiguration(t *testing.T) {
requestVersion schema.GroupVersion
concurrencyShares int32
errExpected field.ErrorList
- }{
- {
- requestVersion: flowcontrolv1alpha1.SchemeGroupVersion,
- concurrencyShares: 0,
- errExpected: errExpectedFn("assuredConcurrencyShares"),
- },
- {
- requestVersion: flowcontrolv1beta1.SchemeGroupVersion,
- concurrencyShares: 0,
- errExpected: errExpectedFn("assuredConcurrencyShares"),
- },
- {
- requestVersion: flowcontrolv1beta2.SchemeGroupVersion,
- concurrencyShares: 0,
- errExpected: errExpectedFn("assuredConcurrencyShares"),
- },
- {
- requestVersion: flowcontrolv1beta3.SchemeGroupVersion,
- concurrencyShares: 0,
- errExpected: errExpectedFn("nominalConcurrencyShares"),
- },
- {
- // let's simulate a post v1beta3 version, we expect the
- // error to return the new field introduced in v1beta3.
- requestVersion: schema.GroupVersion{Group: flowcontrolv1beta3.GroupName, Version: "v1"},
- concurrencyShares: 0,
- errExpected: errExpectedFn("nominalConcurrencyShares"),
- },
- {
- // this should never really happen in real life, the request
- // context should always contain the request {group, version}
- requestVersion: schema.GroupVersion{},
- concurrencyShares: 0,
- errExpected: errExpectedFn("nominalConcurrencyShares"),
- },
- {
- requestVersion: flowcontrolv1beta3.SchemeGroupVersion,
- concurrencyShares: 100,
- errExpected: nil,
- },
- }
+ }{{
+ requestVersion: flowcontrolv1alpha1.SchemeGroupVersion,
+ concurrencyShares: 0,
+ errExpected: errExpectedFn("assuredConcurrencyShares"),
+ }, {
+ requestVersion: flowcontrolv1beta1.SchemeGroupVersion,
+ concurrencyShares: 0,
+ errExpected: errExpectedFn("assuredConcurrencyShares"),
+ }, {
+ requestVersion: flowcontrolv1beta2.SchemeGroupVersion,
+ concurrencyShares: 0,
+ errExpected: errExpectedFn("assuredConcurrencyShares"),
+ }, {
+ requestVersion: flowcontrolv1beta3.SchemeGroupVersion,
+ concurrencyShares: 0,
+ errExpected: errExpectedFn("nominalConcurrencyShares"),
+ }, {
+ // let's simulate a post v1beta3 version, we expect the
+ // error to return the new field introduced in v1beta3.
+ requestVersion: schema.GroupVersion{Group: flowcontrolv1beta3.GroupName, Version: "v1"},
+ concurrencyShares: 0,
+ errExpected: errExpectedFn("nominalConcurrencyShares"),
+ }, {
+ // this should never really happen in real life, the request
+ // context should always contain the request {group, version}
+ requestVersion: schema.GroupVersion{},
+ concurrencyShares: 0,
+ errExpected: errExpectedFn("nominalConcurrencyShares"),
+ }, {
+ requestVersion: flowcontrolv1beta3.SchemeGroupVersion,
+ concurrencyShares: 100,
+ errExpected: nil,
+ }}
for _, test := range tests {
t.Run(test.requestVersion.String(), func(t *testing.T) {
@@ -1464,48 +1230,37 @@ func TestValidateLimitedPriorityLevelConfigurationWithBorrowing(t *testing.T) {
lendablePercent *int32
borrowingLimitPercent *int32
errExpected field.ErrorList
- }{
- {
- lendablePercent: nil,
- errExpected: nil,
- },
- {
- lendablePercent: pointer.Int32(0),
- errExpected: nil,
- },
- {
- lendablePercent: pointer.Int32(100),
- errExpected: nil,
- },
- {
- lendablePercent: pointer.Int32(101),
- errExpected: errLendablePercentFn(101),
- },
- {
- lendablePercent: pointer.Int32(-1),
- errExpected: errLendablePercentFn(-1),
- },
- {
- borrowingLimitPercent: nil,
- errExpected: nil,
- },
- {
- borrowingLimitPercent: pointer.Int32(1),
- errExpected: nil,
- },
- {
- borrowingLimitPercent: pointer.Int32(100),
- errExpected: nil,
- },
- {
- borrowingLimitPercent: pointer.Int32(0),
- errExpected: nil,
- },
- {
- borrowingLimitPercent: pointer.Int32(-1),
- errExpected: errBorrowingLimitPercentFn(-1),
- },
- }
+ }{{
+ lendablePercent: nil,
+ errExpected: nil,
+ }, {
+ lendablePercent: pointer.Int32(0),
+ errExpected: nil,
+ }, {
+ lendablePercent: pointer.Int32(100),
+ errExpected: nil,
+ }, {
+ lendablePercent: pointer.Int32(101),
+ errExpected: errLendablePercentFn(101),
+ }, {
+ lendablePercent: pointer.Int32(-1),
+ errExpected: errLendablePercentFn(-1),
+ }, {
+ borrowingLimitPercent: nil,
+ errExpected: nil,
+ }, {
+ borrowingLimitPercent: pointer.Int32(1),
+ errExpected: nil,
+ }, {
+ borrowingLimitPercent: pointer.Int32(100),
+ errExpected: nil,
+ }, {
+ borrowingLimitPercent: pointer.Int32(0),
+ errExpected: nil,
+ }, {
+ borrowingLimitPercent: pointer.Int32(-1),
+ errExpected: errBorrowingLimitPercentFn(-1),
+ }}
for _, test := range tests {
t.Run(makeTestNameFn(test.lendablePercent, test.borrowingLimitPercent), func(t *testing.T) {
diff --git a/pkg/apis/networking/validation/validation_test.go b/pkg/apis/networking/validation/validation_test.go
index 4a5d71d9ac1..94b5ed73879 100644
--- a/pkg/apis/networking/validation/validation_test.go
+++ b/pkg/apis/networking/validation/validation_test.go
@@ -477,22 +477,18 @@ func TestValidateIngress(t *testing.T) {
},
Spec: networking.IngressSpec{
DefaultBackend: &defaultBackend,
- Rules: []networking.IngressRule{
- {
- Host: "foo.bar.com",
- IngressRuleValue: networking.IngressRuleValue{
- HTTP: &networking.HTTPIngressRuleValue{
- Paths: []networking.HTTPIngressPath{
- {
- Path: "/foo",
- PathType: &pathTypeImplementationSpecific,
- Backend: defaultBackend,
- },
- },
- },
+ Rules: []networking.IngressRule{{
+ Host: "foo.bar.com",
+ IngressRuleValue: networking.IngressRuleValue{
+ HTTP: &networking.HTTPIngressRuleValue{
+ Paths: []networking.HTTPIngressPath{{
+ Path: "/foo",
+ PathType: &pathTypeImplementationSpecific,
+ Backend: defaultBackend,
+ }},
},
},
- },
+ }},
},
Status: networking.IngressStatus{
LoadBalancer: networking.IngressLoadBalancerStatus{
@@ -595,20 +591,18 @@ func TestValidateIngress(t *testing.T) {
tweakIngress: func(ing *networking.Ingress) {
ing.Spec.Rules[0].IngressRuleValue = networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
- Paths: []networking.HTTPIngressPath{
- {
- Path: "/foo",
- PathType: &pathTypeImplementationSpecific,
- Backend: networking.IngressBackend{
- Service: serviceBackend,
- Resource: &api.TypedLocalObjectReference{
- APIGroup: utilpointer.String("example.com"),
- Kind: "foo",
- Name: "bar",
- },
+ Paths: []networking.HTTPIngressPath{{
+ Path: "/foo",
+ PathType: &pathTypeImplementationSpecific,
+ Backend: networking.IngressBackend{
+ Service: serviceBackend,
+ Resource: &api.TypedLocalObjectReference{
+ APIGroup: utilpointer.String("example.com"),
+ Kind: "foo",
+ Name: "bar",
},
},
- },
+ }},
},
}
},
@@ -620,20 +614,18 @@ func TestValidateIngress(t *testing.T) {
tweakIngress: func(ing *networking.Ingress) {
ing.Spec.Rules[0].IngressRuleValue = networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
- Paths: []networking.HTTPIngressPath{
- {
- Path: "/foo",
- PathType: &pathTypeImplementationSpecific,
- Backend: networking.IngressBackend{
- Service: serviceBackend,
- Resource: &api.TypedLocalObjectReference{
- APIGroup: utilpointer.String("example.com"),
- Kind: "foo",
- Name: "bar",
- },
+ Paths: []networking.HTTPIngressPath{{
+ Path: "/foo",
+ PathType: &pathTypeImplementationSpecific,
+ Backend: networking.IngressBackend{
+ Service: serviceBackend,
+ Resource: &api.TypedLocalObjectReference{
+ APIGroup: utilpointer.String("example.com"),
+ Kind: "foo",
+ Name: "bar",
},
},
- },
+ }},
},
}
},
@@ -790,15 +782,13 @@ func TestValidateIngressRuleValue(t *testing.T) {
t.Run(name, func(t *testing.T) {
irv := &networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
- Paths: []networking.HTTPIngressPath{
- {
- Path: testCase.path,
- PathType: &testCase.pathType,
- Backend: networking.IngressBackend{
- Service: &serviceBackend,
- },
+ Paths: []networking.HTTPIngressPath{{
+ Path: testCase.path,
+ PathType: &testCase.pathType,
+ Backend: networking.IngressBackend{
+ Service: &serviceBackend,
},
- },
+ }},
},
}
errs := validateIngressRuleValue(irv, field.NewPath("testing"), IngressValidationOptions{})
@@ -1638,22 +1628,18 @@ func TestValidateIngressTLS(t *testing.T) {
},
Spec: networking.IngressSpec{
DefaultBackend: &defaultBackend,
- Rules: []networking.IngressRule{
- {
- Host: "foo.bar.com",
- IngressRuleValue: networking.IngressRuleValue{
- HTTP: &networking.HTTPIngressRuleValue{
- Paths: []networking.HTTPIngressPath{
- {
- Path: "/foo",
- PathType: &pathTypeImplementationSpecific,
- Backend: defaultBackend,
- },
- },
- },
+ Rules: []networking.IngressRule{{
+ Host: "foo.bar.com",
+ IngressRuleValue: networking.IngressRuleValue{
+ HTTP: &networking.HTTPIngressRuleValue{
+ Paths: []networking.HTTPIngressPath{{
+ Path: "/foo",
+ PathType: &pathTypeImplementationSpecific,
+ Backend: defaultBackend,
+ }},
},
},
- },
+ }},
},
Status: networking.IngressStatus{
LoadBalancer: networking.IngressLoadBalancerStatus{
@@ -1670,11 +1656,9 @@ func TestValidateIngressTLS(t *testing.T) {
wildcardHost := "foo.*.bar.com"
badWildcardTLS := newValid()
badWildcardTLS.Spec.Rules[0].Host = "*.foo.bar.com"
- badWildcardTLS.Spec.TLS = []networking.IngressTLS{
- {
- Hosts: []string{wildcardHost},
- },
- }
+ badWildcardTLS.Spec.TLS = []networking.IngressTLS{{
+ Hosts: []string{wildcardHost},
+ }}
badWildcardTLSErr := fmt.Sprintf("spec.tls[0].hosts[0]: Invalid value: '%v'", wildcardHost)
errorCases[badWildcardTLSErr] = badWildcardTLS
@@ -1696,11 +1680,9 @@ func TestValidateIngressTLS(t *testing.T) {
wildHost := "*.bar.com"
goodWildcardTLS := newValid()
goodWildcardTLS.Spec.Rules[0].Host = "*.bar.com"
- goodWildcardTLS.Spec.TLS = []networking.IngressTLS{
- {
- Hosts: []string{wildHost},
- },
- }
+ goodWildcardTLS.Spec.TLS = []networking.IngressTLS{{
+ Hosts: []string{wildHost},
+ }}
validCases[fmt.Sprintf("spec.tls[0].hosts: Valid value: '%v'", wildHost)] = goodWildcardTLS
for k, v := range validCases {
errs := validateIngress(&v, IngressValidationOptions{})
@@ -1731,21 +1713,17 @@ func TestValidateEmptyIngressTLS(t *testing.T) {
Namespace: metav1.NamespaceDefault,
},
Spec: networking.IngressSpec{
- Rules: []networking.IngressRule{
- {
- Host: "foo.bar.com",
- IngressRuleValue: networking.IngressRuleValue{
- HTTP: &networking.HTTPIngressRuleValue{
- Paths: []networking.HTTPIngressPath{
- {
- PathType: &pathTypeImplementationSpecific,
- Backend: defaultBackend,
- },
- },
- },
+ Rules: []networking.IngressRule{{
+ Host: "foo.bar.com",
+ IngressRuleValue: networking.IngressRuleValue{
+ HTTP: &networking.HTTPIngressRuleValue{
+ Paths: []networking.HTTPIngressPath{{
+ PathType: &pathTypeImplementationSpecific,
+ Backend: defaultBackend,
+ }},
},
},
- },
+ }},
},
}
}
@@ -1757,11 +1735,9 @@ func TestValidateEmptyIngressTLS(t *testing.T) {
}
validCases[fmt.Sprintf("spec.tls[0]: Valid value: %v", goodEmptyTLS.Spec.TLS[0])] = goodEmptyTLS
goodEmptyHosts := newValid()
- goodEmptyHosts.Spec.TLS = []networking.IngressTLS{
- {
- Hosts: []string{},
- },
- }
+ goodEmptyHosts.Spec.TLS = []networking.IngressTLS{{
+ Hosts: []string{},
+ }}
validCases[fmt.Sprintf("spec.tls[0]: Valid value: %v", goodEmptyHosts.Spec.TLS[0])] = goodEmptyHosts
for k, v := range validCases {
errs := validateIngress(&v, IngressValidationOptions{})
@@ -1791,21 +1767,17 @@ func TestValidateIngressStatusUpdate(t *testing.T) {
},
Spec: networking.IngressSpec{
DefaultBackend: &defaultBackend,
- Rules: []networking.IngressRule{
- {
- Host: "foo.bar.com",
- IngressRuleValue: networking.IngressRuleValue{
- HTTP: &networking.HTTPIngressRuleValue{
- Paths: []networking.HTTPIngressPath{
- {
- Path: "/foo",
- Backend: defaultBackend,
- },
- },
- },
+ Rules: []networking.IngressRule{{
+ Host: "foo.bar.com",
+ IngressRuleValue: networking.IngressRuleValue{
+ HTTP: &networking.HTTPIngressRuleValue{
+ Paths: []networking.HTTPIngressPath{{
+ Path: "/foo",
+ Backend: defaultBackend,
+ }},
},
},
- },
+ }},
},
Status: networking.IngressStatus{
LoadBalancer: networking.IngressLoadBalancerStatus{
@@ -1867,17 +1839,13 @@ func TestValidateIngressStatusUpdate(t *testing.T) {
func makeNodeSelector(key string, op api.NodeSelectorOperator, values []string) *api.NodeSelector {
return &api.NodeSelector{
- NodeSelectorTerms: []api.NodeSelectorTerm{
- {
- MatchExpressions: []api.NodeSelectorRequirement{
- {
- Key: key,
- Operator: op,
- Values: values,
- },
- },
- },
- },
+ NodeSelectorTerms: []api.NodeSelectorTerm{{
+ MatchExpressions: []api.NodeSelectorRequirement{{
+ Key: key,
+ Operator: op,
+ Values: values,
+ }},
+ }},
}
}
@@ -1901,59 +1869,49 @@ func TestValidateClusterCIDR(t *testing.T) {
name string
cc *networking.ClusterCIDR
expectErr bool
- }{
- {
- name: "valid SingleStack IPv4 ClusterCIDR",
- cc: makeClusterCIDR(8, "10.1.0.0/16", "", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: false,
- },
- {
- name: "valid SingleStack IPv4 ClusterCIDR, perNodeHostBits = maxPerNodeHostBits",
- cc: makeClusterCIDR(16, "10.1.0.0/16", "", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: false,
- },
- {
- name: "valid SingleStack IPv4 ClusterCIDR, perNodeHostBits > minPerNodeHostBits",
- cc: makeClusterCIDR(4, "10.1.0.0/16", "", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: false,
- },
- {
- name: "valid SingleStack IPv6 ClusterCIDR",
- cc: makeClusterCIDR(8, "", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: false,
- },
- {
- name: "valid SingleStack IPv6 ClusterCIDR, perNodeHostBits = maxPerNodeHostBit",
- cc: makeClusterCIDR(64, "", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: false,
- },
- {
- name: "valid SingleStack IPv6 ClusterCIDR, perNodeHostBits > minPerNodeHostBit",
- cc: makeClusterCIDR(4, "", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: false,
- },
- {
- name: "valid SingleStack IPv6 ClusterCIDR perNodeHostBits=100",
- cc: makeClusterCIDR(100, "", "fd00:1:1::/16", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: false,
- },
- {
- name: "valid DualStack ClusterCIDR",
- cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: false,
- },
- {
- name: "valid DualStack ClusterCIDR, no NodeSelector",
- cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:1:1::/64", nil),
- expectErr: false,
- },
+ }{{
+ name: "valid SingleStack IPv4 ClusterCIDR",
+ cc: makeClusterCIDR(8, "10.1.0.0/16", "", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: false,
+ }, {
+ name: "valid SingleStack IPv4 ClusterCIDR, perNodeHostBits = maxPerNodeHostBits",
+ cc: makeClusterCIDR(16, "10.1.0.0/16", "", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: false,
+ }, {
+ name: "valid SingleStack IPv4 ClusterCIDR, perNodeHostBits > minPerNodeHostBits",
+ cc: makeClusterCIDR(4, "10.1.0.0/16", "", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: false,
+ }, {
+ name: "valid SingleStack IPv6 ClusterCIDR",
+ cc: makeClusterCIDR(8, "", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: false,
+ }, {
+ name: "valid SingleStack IPv6 ClusterCIDR, perNodeHostBits = maxPerNodeHostBit",
+ cc: makeClusterCIDR(64, "", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: false,
+ }, {
+ name: "valid SingleStack IPv6 ClusterCIDR, perNodeHostBits > minPerNodeHostBit",
+ cc: makeClusterCIDR(4, "", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: false,
+ }, {
+ name: "valid SingleStack IPv6 ClusterCIDR perNodeHostBits=100",
+ cc: makeClusterCIDR(100, "", "fd00:1:1::/16", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: false,
+ }, {
+ name: "valid DualStack ClusterCIDR",
+ cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: false,
+ }, {
+ name: "valid DualStack ClusterCIDR, no NodeSelector",
+ cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:1:1::/64", nil),
+ expectErr: false,
+ },
// Failure cases.
{
name: "invalid ClusterCIDR, no IPv4 or IPv6 CIDR",
cc: makeClusterCIDR(8, "", "", nil),
expectErr: true,
- },
- {
+ }, {
name: "invalid ClusterCIDR, invalid nodeSelector",
cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("NoUppercaseOrSpecialCharsLike=Equals", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
@@ -1963,13 +1921,11 @@ func TestValidateClusterCIDR(t *testing.T) {
name: "invalid SingleStack IPv4 ClusterCIDR, invalid spec.IPv4",
cc: makeClusterCIDR(8, "test", "", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
- },
- {
+ }, {
name: "invalid Singlestack IPv4 ClusterCIDR, perNodeHostBits > maxPerNodeHostBits",
cc: makeClusterCIDR(100, "10.1.0.0/16", "", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
- },
- {
+ }, {
name: "invalid SingleStack IPv4 ClusterCIDR, perNodeHostBits < minPerNodeHostBits",
cc: makeClusterCIDR(2, "10.1.0.0/16", "", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
@@ -1979,18 +1935,15 @@ func TestValidateClusterCIDR(t *testing.T) {
name: "invalid SingleStack IPv6 ClusterCIDR, invalid spec.IPv6",
cc: makeClusterCIDR(8, "", "testv6", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
- },
- {
+ }, {
name: "invalid SingleStack IPv6 ClusterCIDR, valid IPv4 CIDR in spec.IPv6",
cc: makeClusterCIDR(8, "", "10.2.0.0/16", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
- },
- {
+ }, {
name: "invalid SingleStack IPv6 ClusterCIDR, invalid perNodeHostBits > maxPerNodeHostBits",
cc: makeClusterCIDR(12, "", "fd00::/120", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
- },
- {
+ }, {
name: "invalid SingleStack IPv6 ClusterCIDR, invalid perNodeHostBits < minPerNodeHostBits",
cc: makeClusterCIDR(3, "", "fd00::/120", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
@@ -2000,18 +1953,15 @@ func TestValidateClusterCIDR(t *testing.T) {
name: "invalid DualStack ClusterCIDR, valid spec.IPv4, invalid spec.IPv6",
cc: makeClusterCIDR(8, "10.1.0.0/16", "testv6", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
- },
- {
+ }, {
name: "invalid DualStack ClusterCIDR, valid spec.IPv6, invalid spec.IPv4",
cc: makeClusterCIDR(8, "testv4", "fd00::/120", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
- },
- {
+ }, {
name: "invalid DualStack ClusterCIDR, invalid perNodeHostBits > maxPerNodeHostBits",
cc: makeClusterCIDR(24, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
- },
- {
+ }, {
name: "invalid DualStack ClusterCIDR, valid IPv6 CIDR in spec.IPv4",
cc: makeClusterCIDR(8, "fd00::/120", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
expectErr: true,
@@ -2038,33 +1988,27 @@ func TestValidateClusterConfigUpdate(t *testing.T) {
name string
cc *networking.ClusterCIDR
expectErr bool
- }{
- {
- name: "Successful update, no changes to ClusterCIDR.Spec",
- cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: false,
- },
- {
- name: "Failed update, update spec.PerNodeHostBits",
- cc: makeClusterCIDR(12, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: true,
- },
- {
- name: "Failed update, update spec.IPv4",
- cc: makeClusterCIDR(8, "10.2.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: true,
- },
- {
- name: "Failed update, update spec.IPv6",
- cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:2:/112", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
- expectErr: true,
- },
- {
- name: "Failed update, update spec.NodeSelector",
- cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar2"})),
- expectErr: true,
- },
- }
+ }{{
+ name: "Successful update, no changes to ClusterCIDR.Spec",
+ cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: false,
+ }, {
+ name: "Failed update, update spec.PerNodeHostBits",
+ cc: makeClusterCIDR(12, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: true,
+ }, {
+ name: "Failed update, update spec.IPv4",
+ cc: makeClusterCIDR(8, "10.2.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: true,
+ }, {
+ name: "Failed update, update spec.IPv6",
+ cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:2:/112", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})),
+ expectErr: true,
+ }, {
+ name: "Failed update, update spec.NodeSelector",
+ cc: makeClusterCIDR(8, "10.1.0.0/16", "fd00:1:1::/64", makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar2"})),
+ expectErr: true,
+ }}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
err := ValidateClusterCIDRUpdate(testCase.cc, oldCCC)
@@ -2244,15 +2188,14 @@ func TestValidateIPAddressUpdate(t *testing.T) {
name string
new func(svc *networking.IPAddress) *networking.IPAddress
expectErr bool
- }{
- {
- name: "Successful update, no changes",
- new: func(old *networking.IPAddress) *networking.IPAddress {
- out := old.DeepCopy()
- return out
- },
- expectErr: false,
+ }{{
+ name: "Successful update, no changes",
+ new: func(old *networking.IPAddress) *networking.IPAddress {
+ out := old.DeepCopy()
+ return out
},
+ expectErr: false,
+ },
{
name: "Failed update, update spec.ParentRef",
@@ -2267,8 +2210,7 @@ func TestValidateIPAddressUpdate(t *testing.T) {
return out
}, expectErr: true,
- },
- {
+ }, {
name: "Failed update, delete spec.ParentRef",
new: func(svc *networking.IPAddress) *networking.IPAddress {
out := svc.DeepCopy()
diff --git a/pkg/apis/node/validation/validation_test.go b/pkg/apis/node/validation/validation_test.go
index da55f458407..51d62d8d698 100644
--- a/pkg/apis/node/validation/validation_test.go
+++ b/pkg/apis/node/validation/validation_test.go
@@ -134,17 +134,15 @@ func TestValidateOverhead(t *testing.T) {
successCase := []struct {
Name string
overhead *node.Overhead
- }{
- {
- Name: "Overhead with valid cpu and memory resources",
- overhead: &node.Overhead{
- PodFixed: core.ResourceList{
- core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
- core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
- },
+ }{{
+ Name: "Overhead with valid cpu and memory resources",
+ overhead: &node.Overhead{
+ PodFixed: core.ResourceList{
+ core.ResourceName(core.ResourceCPU): resource.MustParse("10"),
+ core.ResourceName(core.ResourceMemory): resource.MustParse("10G"),
},
},
- }
+ }}
for _, tc := range successCase {
rc := &node.RuntimeClass{
@@ -160,16 +158,14 @@ func TestValidateOverhead(t *testing.T) {
errorCase := []struct {
Name string
overhead *node.Overhead
- }{
- {
- Name: "Invalid Resources",
- overhead: &node.Overhead{
- PodFixed: core.ResourceList{
- core.ResourceName("my.org"): resource.MustParse("10m"),
- },
+ }{{
+ Name: "Invalid Resources",
+ overhead: &node.Overhead{
+ PodFixed: core.ResourceList{
+ core.ResourceName("my.org"): resource.MustParse("10m"),
},
},
- }
+ }}
for _, tc := range errorCase {
rc := &node.RuntimeClass{
ObjectMeta: metav1.ObjectMeta{Name: "foo"},
diff --git a/pkg/apis/policy/validation/validation_test.go b/pkg/apis/policy/validation/validation_test.go
index f7e7334d003..b28b239a64c 100644
--- a/pkg/apis/policy/validation/validation_test.go
+++ b/pkg/apis/policy/validation/validation_test.go
@@ -107,40 +107,35 @@ func TestValidateUnhealthyPodEvictionPolicyDisruptionBudgetSpec(t *testing.T) {
name string
pdbSpec policy.PodDisruptionBudgetSpec
expectErr bool
- }{
- {
- name: "valid nil UnhealthyPodEvictionPolicy",
- pdbSpec: policy.PodDisruptionBudgetSpec{
- MinAvailable: &c1,
- UnhealthyPodEvictionPolicy: nil,
- },
- expectErr: false,
+ }{{
+ name: "valid nil UnhealthyPodEvictionPolicy",
+ pdbSpec: policy.PodDisruptionBudgetSpec{
+ MinAvailable: &c1,
+ UnhealthyPodEvictionPolicy: nil,
},
- {
- name: "valid UnhealthyPodEvictionPolicy",
- pdbSpec: policy.PodDisruptionBudgetSpec{
- MinAvailable: &c1,
- UnhealthyPodEvictionPolicy: &alwaysAllowPolicy,
- },
- expectErr: false,
+ expectErr: false,
+ }, {
+ name: "valid UnhealthyPodEvictionPolicy",
+ pdbSpec: policy.PodDisruptionBudgetSpec{
+ MinAvailable: &c1,
+ UnhealthyPodEvictionPolicy: &alwaysAllowPolicy,
},
- {
- name: "empty UnhealthyPodEvictionPolicy",
- pdbSpec: policy.PodDisruptionBudgetSpec{
- MinAvailable: &c1,
- UnhealthyPodEvictionPolicy: new(policy.UnhealthyPodEvictionPolicyType),
- },
- expectErr: true,
+ expectErr: false,
+ }, {
+ name: "empty UnhealthyPodEvictionPolicy",
+ pdbSpec: policy.PodDisruptionBudgetSpec{
+ MinAvailable: &c1,
+ UnhealthyPodEvictionPolicy: new(policy.UnhealthyPodEvictionPolicyType),
},
- {
- name: "invalid UnhealthyPodEvictionPolicy",
- pdbSpec: policy.PodDisruptionBudgetSpec{
- MinAvailable: &c1,
- UnhealthyPodEvictionPolicy: &invalidPolicy,
- },
- expectErr: true,
+ expectErr: true,
+ }, {
+ name: "invalid UnhealthyPodEvictionPolicy",
+ pdbSpec: policy.PodDisruptionBudgetSpec{
+ MinAvailable: &c1,
+ UnhealthyPodEvictionPolicy: &invalidPolicy,
},
- }
+ expectErr: true,
+ }}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
@@ -162,128 +157,112 @@ func TestValidatePodDisruptionBudgetStatus(t *testing.T) {
name string
pdbStatus policy.PodDisruptionBudgetStatus
expectErrForVersion map[schema.GroupVersion]bool
- }{
- {
- name: "DisruptionsAllowed: 10",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- DisruptionsAllowed: 10,
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectNoErrors,
- policyv1beta1.SchemeGroupVersion: expectNoErrors,
- },
+ }{{
+ name: "DisruptionsAllowed: 10",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ DisruptionsAllowed: 10,
},
- {
- name: "CurrentHealthy: 5",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- CurrentHealthy: 5,
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectNoErrors,
- policyv1beta1.SchemeGroupVersion: expectNoErrors,
- },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectNoErrors,
+ policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
- {
- name: "DesiredHealthy: 3",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- DesiredHealthy: 3,
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectNoErrors,
- policyv1beta1.SchemeGroupVersion: expectNoErrors,
- },
+ }, {
+ name: "CurrentHealthy: 5",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ CurrentHealthy: 5,
},
- {
- name: "ExpectedPods: 2",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- ExpectedPods: 2,
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectNoErrors,
- policyv1beta1.SchemeGroupVersion: expectNoErrors,
- },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectNoErrors,
+ policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
- {
- name: "DisruptionsAllowed: -10",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- DisruptionsAllowed: -10,
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectErrors,
- policyv1beta1.SchemeGroupVersion: expectNoErrors,
- },
+ }, {
+ name: "DesiredHealthy: 3",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ DesiredHealthy: 3,
},
- {
- name: "CurrentHealthy: -5",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- CurrentHealthy: -5,
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectErrors,
- policyv1beta1.SchemeGroupVersion: expectNoErrors,
- },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectNoErrors,
+ policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
- {
- name: "DesiredHealthy: -3",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- DesiredHealthy: -3,
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectErrors,
- policyv1beta1.SchemeGroupVersion: expectNoErrors,
- },
+ }, {
+ name: "ExpectedPods: 2",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ ExpectedPods: 2,
},
- {
- name: "ExpectedPods: -2",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- ExpectedPods: -2,
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectErrors,
- policyv1beta1.SchemeGroupVersion: expectNoErrors,
- },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectNoErrors,
+ policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
- {
- name: "Conditions valid",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- Conditions: []metav1.Condition{
- {
- Type: policyv1beta1.DisruptionAllowedCondition,
- Status: metav1.ConditionTrue,
- LastTransitionTime: metav1.Time{
- Time: time.Now().Add(-5 * time.Minute),
- },
- Reason: policyv1beta1.SufficientPodsReason,
- Message: "message",
- ObservedGeneration: 3,
- },
+ }, {
+ name: "DisruptionsAllowed: -10",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ DisruptionsAllowed: -10,
+ },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectErrors,
+ policyv1beta1.SchemeGroupVersion: expectNoErrors,
+ },
+ }, {
+ name: "CurrentHealthy: -5",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ CurrentHealthy: -5,
+ },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectErrors,
+ policyv1beta1.SchemeGroupVersion: expectNoErrors,
+ },
+ }, {
+ name: "DesiredHealthy: -3",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ DesiredHealthy: -3,
+ },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectErrors,
+ policyv1beta1.SchemeGroupVersion: expectNoErrors,
+ },
+ }, {
+ name: "ExpectedPods: -2",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ ExpectedPods: -2,
+ },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectErrors,
+ policyv1beta1.SchemeGroupVersion: expectNoErrors,
+ },
+ }, {
+ name: "Conditions valid",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ Conditions: []metav1.Condition{{
+ Type: policyv1beta1.DisruptionAllowedCondition,
+ Status: metav1.ConditionTrue,
+ LastTransitionTime: metav1.Time{
+ Time: time.Now().Add(-5 * time.Minute),
},
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectNoErrors,
- policyv1beta1.SchemeGroupVersion: expectNoErrors,
- },
+ Reason: policyv1beta1.SufficientPodsReason,
+ Message: "message",
+ ObservedGeneration: 3,
+ }},
},
- {
- name: "Conditions not valid",
- pdbStatus: policy.PodDisruptionBudgetStatus{
- Conditions: []metav1.Condition{
- {
- Type: policyv1beta1.DisruptionAllowedCondition,
- Status: metav1.ConditionTrue,
- },
- {
- Type: policyv1beta1.DisruptionAllowedCondition,
- Status: metav1.ConditionFalse,
- },
- },
- },
- expectErrForVersion: map[schema.GroupVersion]bool{
- policy.SchemeGroupVersion: expectErrors,
- policyv1beta1.SchemeGroupVersion: expectErrors,
- },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectNoErrors,
+ policyv1beta1.SchemeGroupVersion: expectNoErrors,
},
- }
+ }, {
+ name: "Conditions not valid",
+ pdbStatus: policy.PodDisruptionBudgetStatus{
+ Conditions: []metav1.Condition{{
+ Type: policyv1beta1.DisruptionAllowedCondition,
+ Status: metav1.ConditionTrue,
+ }, {
+ Type: policyv1beta1.DisruptionAllowedCondition,
+ Status: metav1.ConditionFalse,
+ }},
+ },
+ expectErrForVersion: map[schema.GroupVersion]bool{
+ policy.SchemeGroupVersion: expectErrors,
+ policyv1beta1.SchemeGroupVersion: expectErrors,
+ },
+ }}
for _, tc := range testCases {
for apiVersion, expectErrors := range tc.expectErrForVersion {
@@ -1165,23 +1144,19 @@ func TestAllowEphemeralVolumeType(t *testing.T) {
description string
hasGenericVolume bool
psp func() *policy.PodSecurityPolicy
- }{
- {
- description: "PodSecurityPolicySpec Without GenericVolume",
- hasGenericVolume: false,
- psp: pspWithoutGenericVolume,
- },
- {
- description: "PodSecurityPolicySpec With GenericVolume",
- hasGenericVolume: true,
- psp: pspWithGenericVolume,
- },
- {
- description: "is nil",
- hasGenericVolume: false,
- psp: pspNil,
- },
- }
+ }{{
+ description: "PodSecurityPolicySpec Without GenericVolume",
+ hasGenericVolume: false,
+ psp: pspWithoutGenericVolume,
+ }, {
+ description: "PodSecurityPolicySpec With GenericVolume",
+ hasGenericVolume: true,
+ psp: pspWithGenericVolume,
+ }, {
+ description: "is nil",
+ hasGenericVolume: false,
+ psp: pspNil,
+ }}
for _, oldPSPInfo := range pspInfo {
for _, newPSPInfo := range pspInfo {
diff --git a/pkg/apis/rbac/validation/validation_test.go b/pkg/apis/rbac/validation/validation_test.go
index 76a9ece8142..f0bf6b22944 100644
--- a/pkg/apis/rbac/validation/validation_test.go
+++ b/pkg/apis/rbac/validation/validation_test.go
@@ -402,12 +402,10 @@ func TestValidateRoleNonResourceURL(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "default",
},
- Rules: []rbac.PolicyRule{
- {
- Verbs: []string{"get"},
- NonResourceURLs: []string{"/*"},
- },
- },
+ Rules: []rbac.PolicyRule{{
+ Verbs: []string{"get"},
+ NonResourceURLs: []string{"/*"},
+ }},
},
wantErr: false,
}.test(t)
@@ -420,13 +418,11 @@ func TestValidateRoleNamespacedNonResourceURL(t *testing.T) {
Namespace: "default",
Name: "default",
},
- Rules: []rbac.PolicyRule{
- {
- // non-resource URLs are invalid for namespaced rules
- Verbs: []string{"get"},
- NonResourceURLs: []string{"/*"},
- },
- },
+ Rules: []rbac.PolicyRule{{
+ // non-resource URLs are invalid for namespaced rules
+ Verbs: []string{"get"},
+ NonResourceURLs: []string{"/*"},
+ }},
},
wantErr: true,
errType: field.ErrorTypeInvalid,
@@ -440,12 +436,10 @@ func TestValidateRoleNonResourceURLNoVerbs(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "default",
},
- Rules: []rbac.PolicyRule{
- {
- Verbs: []string{},
- NonResourceURLs: []string{"/*"},
- },
- },
+ Rules: []rbac.PolicyRule{{
+ Verbs: []string{},
+ NonResourceURLs: []string{"/*"},
+ }},
},
wantErr: true,
errType: field.ErrorTypeRequired,
@@ -460,14 +454,12 @@ func TestValidateRoleMixedNonResourceAndResource(t *testing.T) {
Name: "default",
Namespace: "default",
},
- Rules: []rbac.PolicyRule{
- {
- Verbs: []string{"get"},
- NonResourceURLs: []string{"/*"},
- APIGroups: []string{"v1"},
- Resources: []string{"pods"},
- },
- },
+ Rules: []rbac.PolicyRule{{
+ Verbs: []string{"get"},
+ NonResourceURLs: []string{"/*"},
+ APIGroups: []string{"v1"},
+ Resources: []string{"pods"},
+ }},
},
wantErr: true,
errType: field.ErrorTypeInvalid,
@@ -482,13 +474,11 @@ func TestValidateRoleValidResource(t *testing.T) {
Name: "default",
Namespace: "default",
},
- Rules: []rbac.PolicyRule{
- {
- Verbs: []string{"get"},
- APIGroups: []string{"v1"},
- Resources: []string{"pods"},
- },
- },
+ Rules: []rbac.PolicyRule{{
+ Verbs: []string{"get"},
+ APIGroups: []string{"v1"},
+ Resources: []string{"pods"},
+ }},
},
wantErr: false,
}.test(t)
@@ -501,12 +491,10 @@ func TestValidateRoleNoAPIGroup(t *testing.T) {
Name: "default",
Namespace: "default",
},
- Rules: []rbac.PolicyRule{
- {
- Verbs: []string{"get"},
- Resources: []string{"pods"},
- },
- },
+ Rules: []rbac.PolicyRule{{
+ Verbs: []string{"get"},
+ Resources: []string{"pods"},
+ }},
},
wantErr: true,
errType: field.ErrorTypeRequired,
@@ -521,12 +509,10 @@ func TestValidateRoleNoResources(t *testing.T) {
Name: "default",
Namespace: "default",
},
- Rules: []rbac.PolicyRule{
- {
- Verbs: []string{"get"},
- APIGroups: []string{"v1"},
- },
- },
+ Rules: []rbac.PolicyRule{{
+ Verbs: []string{"get"},
+ APIGroups: []string{"v1"},
+ }},
},
wantErr: true,
errType: field.ErrorTypeRequired,
diff --git a/pkg/apis/storage/validation/validation_test.go b/pkg/apis/storage/validation/validation_test.go
index a0a45450fc7..2abc1aedbca 100644
--- a/pkg/apis/storage/validation/validation_test.go
+++ b/pkg/apis/storage/validation/validation_test.go
@@ -58,42 +58,37 @@ func TestValidateStorageClass(t *testing.T) {
deleteReclaimPolicy := api.PersistentVolumeReclaimPolicy("Delete")
retainReclaimPolicy := api.PersistentVolumeReclaimPolicy("Retain")
recycleReclaimPolicy := api.PersistentVolumeReclaimPolicy("Recycle")
- successCases := []storage.StorageClass{
- {
- // empty parameters
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Provisioner: "kubernetes.io/foo-provisioner",
- Parameters: map[string]string{},
- ReclaimPolicy: &deleteReclaimPolicy,
- VolumeBindingMode: &immediateMode1,
+ successCases := []storage.StorageClass{{
+ // empty parameters
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Provisioner: "kubernetes.io/foo-provisioner",
+ Parameters: map[string]string{},
+ ReclaimPolicy: &deleteReclaimPolicy,
+ VolumeBindingMode: &immediateMode1,
+ }, {
+ // nil parameters
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Provisioner: "kubernetes.io/foo-provisioner",
+ ReclaimPolicy: &deleteReclaimPolicy,
+ VolumeBindingMode: &immediateMode1,
+ }, {
+ // some parameters
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Provisioner: "kubernetes.io/foo-provisioner",
+ Parameters: map[string]string{
+ "kubernetes.io/foo-parameter": "free/form/string",
+ "foo-parameter": "free-form-string",
+ "foo-parameter2": "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}",
},
- {
- // nil parameters
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Provisioner: "kubernetes.io/foo-provisioner",
- ReclaimPolicy: &deleteReclaimPolicy,
- VolumeBindingMode: &immediateMode1,
- },
- {
- // some parameters
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Provisioner: "kubernetes.io/foo-provisioner",
- Parameters: map[string]string{
- "kubernetes.io/foo-parameter": "free/form/string",
- "foo-parameter": "free-form-string",
- "foo-parameter2": "{\"embedded\": \"json\", \"with\": {\"structures\":\"inside\"}}",
- },
- ReclaimPolicy: &deleteReclaimPolicy,
- VolumeBindingMode: &immediateMode1,
- },
- {
- // retain reclaimPolicy
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Provisioner: "kubernetes.io/foo-provisioner",
- ReclaimPolicy: &retainReclaimPolicy,
- VolumeBindingMode: &immediateMode1,
- },
- }
+ ReclaimPolicy: &deleteReclaimPolicy,
+ VolumeBindingMode: &immediateMode1,
+ }, {
+ // retain reclaimPolicy
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Provisioner: "kubernetes.io/foo-provisioner",
+ ReclaimPolicy: &retainReclaimPolicy,
+ VolumeBindingMode: &immediateMode1,
+ }}
// Success cases are expected to pass validation.
for k, v := range successCases {
@@ -160,223 +155,208 @@ func TestValidateStorageClass(t *testing.T) {
func TestVolumeAttachmentValidation(t *testing.T) {
volumeName := "pv-name"
empty := ""
- migrationEnabledSuccessCases := []storage.VolumeAttachment{
- {
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
- NodeName: "mynode",
+ migrationEnabledSuccessCases := []storage.VolumeAttachment{{
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
+ },
+ NodeName: "mynode",
+ },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{
+ InlineVolumeSpec: &inlineSpec,
+ },
+ NodeName: "mynode",
+ },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: "foo-with-status"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
+ },
+ NodeName: "mynode",
+ },
+ Status: storage.VolumeAttachmentStatus{
+ Attached: true,
+ AttachmentMetadata: map[string]string{
+ "foo": "bar",
+ },
+ AttachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
+ },
+ DetachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
},
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{
- InlineVolumeSpec: &inlineSpec,
- },
- NodeName: "mynode",
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec-and-status"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{
+ InlineVolumeSpec: &inlineSpec,
+ },
+ NodeName: "mynode",
+ },
+ Status: storage.VolumeAttachmentStatus{
+ Attached: true,
+ AttachmentMetadata: map[string]string{
+ "foo": "bar",
+ },
+ AttachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
+ },
+ DetachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
},
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: "foo-with-status"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
- NodeName: "mynode",
- },
- Status: storage.VolumeAttachmentStatus{
- Attached: true,
- AttachmentMetadata: map[string]string{
- "foo": "bar",
- },
- AttachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
- DetachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
- },
- },
- {
- ObjectMeta: metav1.ObjectMeta{Name: "foo-with-inlinespec-and-status"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{
- InlineVolumeSpec: &inlineSpec,
- },
- NodeName: "mynode",
- },
- Status: storage.VolumeAttachmentStatus{
- Attached: true,
- AttachmentMetadata: map[string]string{
- "foo": "bar",
- },
- AttachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
- DetachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
- },
- },
- }
+ }}
for _, volumeAttachment := range migrationEnabledSuccessCases {
if errs := ValidateVolumeAttachment(&volumeAttachment); len(errs) != 0 {
t.Errorf("expected success: %v %v", volumeAttachment, errs)
}
}
- migrationEnabledErrorCases := []storage.VolumeAttachment{
- {
- // Empty attacher name
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "",
- NodeName: "mynode",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
+ migrationEnabledErrorCases := []storage.VolumeAttachment{{
+ // Empty attacher name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "",
+ NodeName: "mynode",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
},
},
- {
- // Empty node name
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- NodeName: "",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
+ }, {
+ // Empty node name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ NodeName: "",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
},
},
- {
- // No volume name
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- NodeName: "node",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: nil,
- },
+ }, {
+ // No volume name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ NodeName: "node",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: nil,
},
},
- {
- // Empty volume name
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- NodeName: "node",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &empty,
- },
+ }, {
+ // Empty volume name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ NodeName: "node",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &empty,
},
},
- {
- // Too long error message
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- NodeName: "node",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
- },
- Status: storage.VolumeAttachmentStatus{
- Attached: true,
- AttachmentMetadata: map[string]string{
- "foo": "bar",
- },
- AttachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
- DetachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: strings.Repeat("a", maxVolumeErrorMessageSize+1),
- },
+ }, {
+ // Too long error message
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ NodeName: "node",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
},
},
- {
- // Too long metadata
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- NodeName: "node",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
+ Status: storage.VolumeAttachmentStatus{
+ Attached: true,
+ AttachmentMetadata: map[string]string{
+ "foo": "bar",
},
- Status: storage.VolumeAttachmentStatus{
- Attached: true,
- AttachmentMetadata: map[string]string{
- "foo": strings.Repeat("a", maxAttachedVolumeMetadataSize),
- },
- AttachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
- DetachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
+ AttachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
+ },
+ DetachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: strings.Repeat("a", maxVolumeErrorMessageSize+1),
},
},
- {
- // VolumeAttachmentSource with no PersistentVolumeName nor InlineSpec
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- NodeName: "node",
- Source: storage.VolumeAttachmentSource{},
+ }, {
+ // Too long metadata
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ NodeName: "node",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
},
},
- {
- // VolumeAttachmentSource with PersistentVolumeName and InlineSpec
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- NodeName: "node",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- InlineVolumeSpec: &inlineSpec,
- },
+ Status: storage.VolumeAttachmentStatus{
+ Attached: true,
+ AttachmentMetadata: map[string]string{
+ "foo": strings.Repeat("a", maxAttachedVolumeMetadataSize),
+ },
+ AttachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
+ },
+ DetachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
},
},
- {
- // VolumeAttachmentSource with InlineSpec without CSI PV Source
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- NodeName: "node",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- InlineVolumeSpec: &api.PersistentVolumeSpec{
- Capacity: api.ResourceList{
- api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
- },
- AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
- PersistentVolumeSource: api.PersistentVolumeSource{
- FlexVolume: &api.FlexPersistentVolumeSource{
- Driver: "kubernetes.io/blue",
- FSType: "ext4",
- },
- },
- StorageClassName: "test-storage-class",
+ }, {
+ // VolumeAttachmentSource with no PersistentVolumeName nor InlineSpec
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ NodeName: "node",
+ Source: storage.VolumeAttachmentSource{},
+ },
+ }, {
+ // VolumeAttachmentSource with PersistentVolumeName and InlineSpec
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ NodeName: "node",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
+ InlineVolumeSpec: &inlineSpec,
+ },
+ },
+ }, {
+ // VolumeAttachmentSource with InlineSpec without CSI PV Source
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ NodeName: "node",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
+ InlineVolumeSpec: &api.PersistentVolumeSpec{
+ Capacity: api.ResourceList{
+ api.ResourceName(api.ResourceStorage): resource.MustParse("10G"),
},
+ AccessModes: []api.PersistentVolumeAccessMode{api.ReadWriteOnce},
+ PersistentVolumeSource: api.PersistentVolumeSource{
+ FlexVolume: &api.FlexPersistentVolumeSource{
+ Driver: "kubernetes.io/blue",
+ FSType: "ext4",
+ },
+ },
+ StorageClassName: "test-storage-class",
},
},
},
- }
+ }}
for _, volumeAttachment := range migrationEnabledErrorCases {
if errs := ValidateVolumeAttachment(&volumeAttachment); len(errs) == 0 {
@@ -398,40 +378,37 @@ func TestVolumeAttachmentUpdateValidation(t *testing.T) {
},
}
- successCases := []storage.VolumeAttachment{
- {
- // no change
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{},
- NodeName: "mynode",
+ successCases := []storage.VolumeAttachment{{
+ // no change
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{},
+ NodeName: "mynode",
+ },
+ }, {
+ // modify status
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{},
+ NodeName: "mynode",
+ },
+ Status: storage.VolumeAttachmentStatus{
+ Attached: true,
+ AttachmentMetadata: map[string]string{
+ "foo": "bar",
+ },
+ AttachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
+ },
+ DetachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
},
},
- {
- // modify status
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{},
- NodeName: "mynode",
- },
- Status: storage.VolumeAttachmentStatus{
- Attached: true,
- AttachmentMetadata: map[string]string{
- "foo": "bar",
- },
- AttachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
- DetachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
- },
- },
- }
+ }}
for _, volumeAttachment := range successCases {
volumeAttachment.Spec.Source = storage.VolumeAttachmentSource{}
@@ -457,77 +434,71 @@ func TestVolumeAttachmentUpdateValidation(t *testing.T) {
old.Spec.Source = storage.VolumeAttachmentSource{}
old.Spec.Source.PersistentVolumeName = &volumeName
- errorCases := []storage.VolumeAttachment{
- {
- // change attacher
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "another-attacher",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
- NodeName: "mynode",
+ errorCases := []storage.VolumeAttachment{{
+ // change attacher
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "another-attacher",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
+ },
+ NodeName: "mynode",
+ },
+ }, {
+ // change source volume name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &newVolumeName,
+ },
+ NodeName: "mynode",
+ },
+ }, {
+ // change node
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
+ },
+ NodeName: "anothernode",
+ },
+ }, {
+ // change source
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{
+ InlineVolumeSpec: &inlineSpec,
+ },
+ NodeName: "mynode",
+ },
+ }, {
+ // add invalid status
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
+ },
+ NodeName: "mynode",
+ },
+ Status: storage.VolumeAttachmentStatus{
+ Attached: true,
+ AttachmentMetadata: map[string]string{
+ "foo": "bar",
+ },
+ AttachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: strings.Repeat("a", maxAttachedVolumeMetadataSize),
+ },
+ DetachError: &storage.VolumeError{
+ Time: metav1.Time{},
+ Message: "hello world",
},
},
- {
- // change source volume name
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &newVolumeName,
- },
- NodeName: "mynode",
- },
- },
- {
- // change node
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
- NodeName: "anothernode",
- },
- },
- {
- // change source
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{
- InlineVolumeSpec: &inlineSpec,
- },
- NodeName: "mynode",
- },
- },
- {
- // add invalid status
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
- NodeName: "mynode",
- },
- Status: storage.VolumeAttachmentStatus{
- Attached: true,
- AttachmentMetadata: map[string]string{
- "foo": "bar",
- },
- AttachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: strings.Repeat("a", maxAttachedVolumeMetadataSize),
- },
- DetachError: &storage.VolumeError{
- Time: metav1.Time{},
- Message: "hello world",
- },
- },
- },
- }
+ }}
for _, volumeAttachment := range errorCases {
if errs := ValidateVolumeAttachmentUpdate(&volumeAttachment, &old); len(errs) == 0 {
@@ -539,18 +510,16 @@ func TestVolumeAttachmentUpdateValidation(t *testing.T) {
func TestVolumeAttachmentValidationV1(t *testing.T) {
volumeName := "pv-name"
invalidVolumeName := "-invalid-@#$%^&*()-"
- successCases := []storage.VolumeAttachment{
- {
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
- NodeName: "mynode",
+ successCases := []storage.VolumeAttachment{{
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
},
+ NodeName: "mynode",
},
- }
+ }}
for _, volumeAttachment := range successCases {
if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) != 0 {
@@ -558,30 +527,27 @@ func TestVolumeAttachmentValidationV1(t *testing.T) {
}
}
- errorCases := []storage.VolumeAttachment{
- {
- // Invalid attacher name
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "invalid-@#$%^&*()",
- NodeName: "mynode",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &volumeName,
- },
+ errorCases := []storage.VolumeAttachment{{
+ // Invalid attacher name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "invalid-@#$%^&*()",
+ NodeName: "mynode",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &volumeName,
},
},
- {
- // Invalid PV name
- ObjectMeta: metav1.ObjectMeta{Name: "foo"},
- Spec: storage.VolumeAttachmentSpec{
- Attacher: "myattacher",
- NodeName: "mynode",
- Source: storage.VolumeAttachmentSource{
- PersistentVolumeName: &invalidVolumeName,
- },
+ }, {
+ // Invalid PV name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo"},
+ Spec: storage.VolumeAttachmentSpec{
+ Attacher: "myattacher",
+ NodeName: "mynode",
+ Source: storage.VolumeAttachmentSource{
+ PersistentVolumeName: &invalidVolumeName,
},
},
- }
+ }}
for _, volumeAttachment := range errorCases {
if errs := ValidateVolumeAttachmentV1(&volumeAttachment); len(errs) == 0 {
@@ -694,235 +660,155 @@ func TestValidateUpdateVolumeBindingMode(t *testing.T) {
func TestValidateAllowedTopologies(t *testing.T) {
- validTopology := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone1"},
- },
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1"},
- },
- },
- },
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone2"},
- },
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node2"},
- },
- },
- },
- }
+ validTopology := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone1"},
+ }, {
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1"},
+ }},
+ }, {
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone2"},
+ }, {
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node2"},
+ }},
+ }}
- topologyInvalidKey := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "/invalidkey",
- Values: []string{"zone1"},
- },
- },
- },
- }
+ topologyInvalidKey := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "/invalidkey",
+ Values: []string{"zone1"},
+ }},
+ }}
- topologyLackOfValues := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "kubernetes.io/hostname",
- Values: []string{},
- },
- },
- },
- }
+ topologyLackOfValues := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "kubernetes.io/hostname",
+ Values: []string{},
+ }},
+ }}
- topologyDupValues := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1", "node1"},
- },
- },
- },
- }
+ topologyDupValues := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1", "node1"},
+ }},
+ }}
- topologyMultiValues := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1", "node2"},
- },
- },
- },
- }
+ topologyMultiValues := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1", "node2"},
+ }},
+ }}
- topologyEmptyMatchLabelExpressions := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: nil,
- },
- }
+ topologyEmptyMatchLabelExpressions := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: nil,
+ }}
- topologyDupKeys := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1"},
- },
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node2"},
- },
- },
- },
- }
+ topologyDupKeys := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1"},
+ }, {
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node2"},
+ }},
+ }}
- topologyMultiTerm := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1"},
- },
- },
- },
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node2"},
- },
- },
- },
- }
+ topologyMultiTerm := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1"},
+ }},
+ }, {
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node2"},
+ }},
+ }}
- topologyDupTermsIdentical := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone1"},
- },
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1"},
- },
- },
- },
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone1"},
- },
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1"},
- },
- },
- },
- }
+ topologyDupTermsIdentical := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone1"},
+ }, {
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1"},
+ }},
+ }, {
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone1"},
+ }, {
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1"},
+ }},
+ }}
- topologyExprsOneSameOneDiff := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone1"},
- },
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1"},
- },
- },
- },
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone1"},
- },
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node2"},
- },
- },
- },
- }
+ topologyExprsOneSameOneDiff := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone1"},
+ }, {
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1"},
+ }},
+ }, {
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone1"},
+ }, {
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node2"},
+ }},
+ }}
- topologyValuesOneSameOneDiff := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1", "node2"},
- },
- },
- },
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1", "node3"},
- },
- },
- },
- }
+ topologyValuesOneSameOneDiff := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1", "node2"},
+ }},
+ }, {
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1", "node3"},
+ }},
+ }}
- topologyDupTermsDiffExprOrder := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1"},
- },
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone1"},
- },
- },
- },
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone1"},
- },
- {
- Key: "kubernetes.io/hostname",
- Values: []string{"node1"},
- },
- },
- },
- }
+ topologyDupTermsDiffExprOrder := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1"},
+ }, {
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone1"},
+ }},
+ }, {
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone1"},
+ }, {
+ Key: "kubernetes.io/hostname",
+ Values: []string{"node1"},
+ }},
+ }}
- topologyDupTermsDiffValueOrder := []api.TopologySelectorTerm{
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone1", "zone2"},
- },
- },
- },
- {
- MatchLabelExpressions: []api.TopologySelectorLabelRequirement{
- {
- Key: "failure-domain.beta.kubernetes.io/zone",
- Values: []string{"zone2", "zone1"},
- },
- },
- },
- }
+ topologyDupTermsDiffValueOrder := []api.TopologySelectorTerm{{
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone1", "zone2"},
+ }},
+ }, {
+ MatchLabelExpressions: []api.TopologySelectorLabelRequirement{{
+ Key: "failure-domain.beta.kubernetes.io/zone",
+ Values: []string{"zone2", "zone1"},
+ }},
+ }}
cases := map[string]bindingTest{
"no topology": {
@@ -1000,193 +886,150 @@ func TestCSINodeValidation(t *testing.T) {
longName := "my-a-b-c-d-c-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-ABCDEFGHIJKLMNOPQRSTUVWXYZ-driver" // 88 chars
nodeID := "nodeA"
longID := longName + longName + "abcdefghijklmnopqrstuvwxyz" // 202 chars
- successCases := []storage.CSINode{
- {
- // driver name: dot only
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ successCases := []storage.CSINode{{
+ // driver name: dot only
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // driver name: dash only
- ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io-kubernetes-storage-csi-driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // driver name: dash only
+ ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io-kubernetes-storage-csi-driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // driver name: numbers
- ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "1io-kubernetes-storage-2-csi-driver3",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // driver name: numbers
+ ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "1io-kubernetes-storage-2-csi-driver3",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // driver name: dot, dash
- ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage-csi-driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // driver name: dot, dash
+ ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage-csi-driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // driver name: dot, dash, and numbers
- ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: driverName2,
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // driver name: dot, dash, and numbers
+ ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: driverName2,
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // Driver name length 1
- ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "a",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // Driver name length 1
+ ObjectMeta: metav1.ObjectMeta{Name: "foo2"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "a",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // multiple drivers with different node IDs, topology keys
- ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "driver1",
- NodeID: "node1",
- TopologyKeys: []string{"key1", "key2"},
- },
- {
- Name: "driverB",
- NodeID: "nodeA",
- TopologyKeys: []string{"keyA", "keyB"},
- },
- },
- },
+ }, {
+ // multiple drivers with different node IDs, topology keys
+ ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "driver1",
+ NodeID: "node1",
+ TopologyKeys: []string{"key1", "key2"},
+ }, {
+ Name: "driverB",
+ NodeID: "nodeA",
+ TopologyKeys: []string{"keyA", "keyB"},
+ }},
},
- {
- // multiple drivers with same node IDs, topology keys
- ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "driver1",
- NodeID: "node1",
- TopologyKeys: []string{"key1"},
- },
- {
- Name: "driver2",
- NodeID: "node1",
- TopologyKeys: []string{"key1"},
- },
- },
- },
+ }, {
+ // multiple drivers with same node IDs, topology keys
+ ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "driver1",
+ NodeID: "node1",
+ TopologyKeys: []string{"key1"},
+ }, {
+ Name: "driver2",
+ NodeID: "node1",
+ TopologyKeys: []string{"key1"},
+ }},
},
- {
- // Volume limits being zero
- ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(0)},
- },
- },
- },
+ }, {
+ // Volume limits being zero
+ ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(0)},
+ }},
},
- {
- // Volume limits with positive number
- ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(1)},
- },
- },
- },
+ }, {
+ // Volume limits with positive number
+ ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(1)},
+ }},
},
- {
- // topology key names with -, _, and dot .
- ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "driver1",
- NodeID: "node1",
- TopologyKeys: []string{"zone_1", "zone.2"},
- },
- {
- Name: "driver2",
- NodeID: "node1",
- TopologyKeys: []string{"zone-3", "zone.4"},
- },
- },
- },
+ }, {
+ // topology key names with -, _, and dot .
+ ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "driver1",
+ NodeID: "node1",
+ TopologyKeys: []string{"zone_1", "zone.2"},
+ }, {
+ Name: "driver2",
+ NodeID: "node1",
+ TopologyKeys: []string{"zone-3", "zone.4"},
+ }},
},
- {
- // topology prefix with - and dot.
- ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "driver1",
- NodeID: "node1",
- TopologyKeys: []string{"company-com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // topology prefix with - and dot.
+ ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "driver1",
+ NodeID: "node1",
+ TopologyKeys: []string{"company-com/zone1", "company.com/zone2"},
+ }},
},
- {
- // No topology keys
- ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: driverName,
- NodeID: nodeID,
- },
- },
- },
+ }, {
+ // No topology keys
+ ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: driverName,
+ NodeID: nodeID,
+ }},
},
- }
+ }}
for _, csiNode := range successCases {
if errs := ValidateCSINode(&csiNode, shorterIDValidationOption); len(errs) != 0 {
@@ -1198,13 +1041,11 @@ func TestCSINodeValidation(t *testing.T) {
// node ID length > 128 but < 192
ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: driverName,
- NodeID: longID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
+ Drivers: []storage.CSINodeDriver{{
+ Name: driverName,
+ NodeID: longID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
}
@@ -1212,199 +1053,155 @@ func TestCSINodeValidation(t *testing.T) {
t.Errorf("expected success: %v", errs)
}
- errorCases := []storage.CSINode{
- {
- // Empty driver name
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ errorCases := []storage.CSINode{{
+ // Empty driver name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // Invalid start char in driver name
- ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "_io.kubernetes.storage.csi.driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // Invalid start char in driver name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo3"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "_io.kubernetes.storage.csi.driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // Invalid end char in driver name
- ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver/",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // Invalid end char in driver name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo4"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver/",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // Invalid separators in driver name
- ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io/kubernetes/storage/csi~driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // Invalid separators in driver name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo5"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io/kubernetes/storage/csi~driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // driver name: underscore only
- ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io_kubernetes_storage_csi_driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // driver name: underscore only
+ ObjectMeta: metav1.ObjectMeta{Name: "foo6"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io_kubernetes_storage_csi_driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // Driver name length > 63
- ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: longName,
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // Driver name length > 63
+ ObjectMeta: metav1.ObjectMeta{Name: "foo7"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: longName,
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // No driver name
- ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // No driver name
+ ObjectMeta: metav1.ObjectMeta{Name: "foo8"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // Empty individual topology key
- ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: driverName,
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", ""},
- },
- },
- },
+ }, {
+ // Empty individual topology key
+ ObjectMeta: metav1.ObjectMeta{Name: "foo9"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: driverName,
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", ""},
+ }},
},
- {
- // duplicate drivers in driver specs
- ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "driver1",
- NodeID: "node1",
- TopologyKeys: []string{"key1", "key2"},
- },
- {
- Name: "driver1",
- NodeID: "nodeX",
- TopologyKeys: []string{"keyA", "keyB"},
- },
- },
- },
+ }, {
+ // duplicate drivers in driver specs
+ ObjectMeta: metav1.ObjectMeta{Name: "foo10"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "driver1",
+ NodeID: "node1",
+ TopologyKeys: []string{"key1", "key2"},
+ }, {
+ Name: "driver1",
+ NodeID: "nodeX",
+ TopologyKeys: []string{"keyA", "keyB"},
+ }},
},
- {
- // single driver with duplicate topology keys in driver specs
- ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "driver1",
- NodeID: "node1",
- TopologyKeys: []string{"key1", "key1"},
- },
- },
- },
+ }, {
+ // single driver with duplicate topology keys in driver specs
+ ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "driver1",
+ NodeID: "node1",
+ TopologyKeys: []string{"key1", "key1"},
+ }},
},
- {
- // multiple drivers with one set of duplicate topology keys in driver specs
- ObjectMeta: metav1.ObjectMeta{Name: "foo12"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "driver1",
- NodeID: "node1",
- TopologyKeys: []string{"key1"},
- },
- {
- Name: "driver2",
- NodeID: "nodeX",
- TopologyKeys: []string{"keyA", "keyA"},
- },
- },
- },
+ }, {
+ // multiple drivers with one set of duplicate topology keys in driver specs
+ ObjectMeta: metav1.ObjectMeta{Name: "foo12"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "driver1",
+ NodeID: "node1",
+ TopologyKeys: []string{"key1"},
+ }, {
+ Name: "driver2",
+ NodeID: "nodeX",
+ TopologyKeys: []string{"keyA", "keyA"},
+ }},
},
- {
- // Empty NodeID
- ObjectMeta: metav1.ObjectMeta{Name: "foo13"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: driverName,
- NodeID: "",
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // Empty NodeID
+ ObjectMeta: metav1.ObjectMeta{Name: "foo13"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: driverName,
+ NodeID: "",
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // Volume limits with negative number
- ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(-1)},
- },
- },
- },
+ }, {
+ // Volume limits with negative number
+ ObjectMeta: metav1.ObjectMeta{Name: "foo11"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(-1)},
+ }},
},
- {
- // topology prefix should be lower case
- ObjectMeta: metav1.ObjectMeta{Name: "foo14"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: driverName,
- NodeID: "node1",
- TopologyKeys: []string{"Company.Com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // topology prefix should be lower case
+ ObjectMeta: metav1.ObjectMeta{Name: "foo14"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: driverName,
+ NodeID: "node1",
+ TopologyKeys: []string{"Company.Com/zone1", "company.com/zone2"},
+ }},
},
+ },
nodeIDCase,
}
@@ -1424,100 +1221,80 @@ func TestCSINodeUpdateValidation(t *testing.T) {
old := storage.CSINode{
ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-2",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
- },
- },
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-2",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
+ }},
},
}
- successCases := []storage.CSINode{
- {
- // no change
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-2",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
- },
- },
- },
+ successCases := []storage.CSINode{{
+ // no change
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-2",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
+ }},
},
- {
- // remove a driver
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // remove a driver
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- {
- // add a driver
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-2",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-3",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(30)},
- },
- },
- },
+ }, {
+ // add a driver
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-2",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-3",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(30)},
+ }},
},
- {
- // remove a driver and add a driver
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- {
- Name: "io.kubernetes.storage.csi.new-driver",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(30)},
- },
- },
- },
+ }, {
+ // remove a driver and add a driver
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }, {
+ Name: "io.kubernetes.storage.csi.new-driver",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(30)},
+ }},
},
- }
+ }}
for _, csiNode := range successCases {
if errs := ValidateCSINodeUpdate(&csiNode, &old, shorterIDValidationOption); len(errs) != 0 {
@@ -1525,122 +1302,97 @@ func TestCSINodeUpdateValidation(t *testing.T) {
}
}
- errorCases := []storage.CSINode{
- {
- // invalid change node id
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: "nodeB",
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-2",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
- },
- },
- },
+ errorCases := []storage.CSINode{{
+ // invalid change node id
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: "nodeB",
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-2",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
+ }},
},
- {
- // invalid change topology keys
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-2",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
- },
- },
- },
+ }, {
+ // invalid change topology keys
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-2",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
+ }},
},
- {
- // invalid change trying to set a previously unset allocatable
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(10)},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-2",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
- },
- },
- },
+ }, {
+ // invalid change trying to set a previously unset allocatable
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(10)},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-2",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(20)},
+ }},
},
- {
- // invalid change trying to update allocatable with a different volume limit
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-2",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(21)},
- },
- },
- },
+ }, {
+ // invalid change trying to update allocatable with a different volume limit
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-2",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: utilpointer.Int32(21)},
+ }},
},
- {
- // invalid change trying to update allocatable with an empty volume limit
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-2",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- Allocatable: &storage.VolumeNodeResources{Count: nil},
- },
- },
- },
+ }, {
+ // invalid change trying to update allocatable with an empty volume limit
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-2",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ Allocatable: &storage.VolumeNodeResources{Count: nil},
+ }},
},
- {
- // invalid change trying to remove allocatable
- ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
- Spec: storage.CSINodeSpec{
- Drivers: []storage.CSINodeDriver{
- {
- Name: "io.kubernetes.storage.csi.driver-1",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- {
- Name: "io.kubernetes.storage.csi.driver-2",
- NodeID: nodeID,
- TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
- },
- },
- },
+ }, {
+ // invalid change trying to remove allocatable
+ ObjectMeta: metav1.ObjectMeta{Name: "foo1"},
+ Spec: storage.CSINodeSpec{
+ Drivers: []storage.CSINodeDriver{{
+ Name: "io.kubernetes.storage.csi.driver-1",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }, {
+ Name: "io.kubernetes.storage.csi.driver-2",
+ NodeID: nodeID,
+ TopologyKeys: []string{"company.com/zone1", "company.com/zone2"},
+ }},
},
- }
+ }}
for _, csiNode := range errorCases {
if errs := ValidateCSINodeUpdate(&csiNode, &old, shorterIDValidationOption); len(errs) == 0 {
@@ -1664,258 +1416,234 @@ func TestCSIDriverValidation(t *testing.T) {
notSELinuxMount := false
supportedFSGroupPolicy := storage.FileFSGroupPolicy
invalidFSGroupPolicy := storage.FSGroupPolicy("invalid-mode")
- successCases := []storage.CSIDriver{
- {
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachRequired,
- PodInfoOnMount: &podInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ successCases := []storage.CSIDriver{{
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachRequired,
+ PodInfoOnMount: &podInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- // driver name: dot only
- ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi.driver"},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachRequired,
- PodInfoOnMount: &podInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: ¬StorageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ // driver name: dot only
+ ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi.driver"},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachRequired,
+ PodInfoOnMount: &podInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: ¬StorageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- // driver name: dash only
- ObjectMeta: metav1.ObjectMeta{Name: "io-kubernetes-storage-csi-driver"},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ // driver name: dash only
+ ObjectMeta: metav1.ObjectMeta{Name: "io-kubernetes-storage-csi-driver"},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- // driver name: numbers
- ObjectMeta: metav1.ObjectMeta{Name: "1csi2driver3"},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachRequired,
- PodInfoOnMount: &podInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ // driver name: numbers
+ ObjectMeta: metav1.ObjectMeta{Name: "1csi2driver3"},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachRequired,
+ PodInfoOnMount: &podInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- // driver name: dot and dash
- ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi-driver"},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachRequired,
- PodInfoOnMount: &podInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ // driver name: dot and dash
+ ObjectMeta: metav1.ObjectMeta{Name: "io.kubernetes.storage.csi-driver"},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachRequired,
+ PodInfoOnMount: &podInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachRequired,
- PodInfoOnMount: &podInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachRequired,
+ PodInfoOnMount: &podInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- VolumeLifecycleModes: []storage.VolumeLifecycleMode{
- storage.VolumeLifecyclePersistent,
- },
- SELinuxMount: &seLinuxMount,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ VolumeLifecycleModes: []storage.VolumeLifecycleMode{
+ storage.VolumeLifecyclePersistent,
},
+ SELinuxMount: &seLinuxMount,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- VolumeLifecycleModes: []storage.VolumeLifecycleMode{
- storage.VolumeLifecycleEphemeral,
- },
- SELinuxMount: &seLinuxMount,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ VolumeLifecycleModes: []storage.VolumeLifecycleMode{
+ storage.VolumeLifecycleEphemeral,
},
+ SELinuxMount: &seLinuxMount,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- VolumeLifecycleModes: []storage.VolumeLifecycleMode{
- storage.VolumeLifecycleEphemeral,
- storage.VolumeLifecyclePersistent,
- },
- SELinuxMount: &seLinuxMount,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ VolumeLifecycleModes: []storage.VolumeLifecycleMode{
+ storage.VolumeLifecycleEphemeral,
+ storage.VolumeLifecyclePersistent,
},
+ SELinuxMount: &seLinuxMount,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- VolumeLifecycleModes: []storage.VolumeLifecycleMode{
- storage.VolumeLifecycleEphemeral,
- storage.VolumeLifecyclePersistent,
- storage.VolumeLifecycleEphemeral,
- },
- SELinuxMount: &seLinuxMount,
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ VolumeLifecycleModes: []storage.VolumeLifecycleMode{
+ storage.VolumeLifecycleEphemeral,
+ storage.VolumeLifecyclePersistent,
+ storage.VolumeLifecycleEphemeral,
},
+ SELinuxMount: &seLinuxMount,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- FSGroupPolicy: &supportedFSGroupPolicy,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ FSGroupPolicy: &supportedFSGroupPolicy,
+ SELinuxMount: &seLinuxMount,
},
- {
- // SELinuxMount: false
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- RequiresRepublish: ¬RequiresRepublish,
- StorageCapacity: &storageCapacity,
- SELinuxMount: ¬SELinuxMount,
- },
+ }, {
+ // SELinuxMount: false
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ RequiresRepublish: ¬RequiresRepublish,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: ¬SELinuxMount,
},
- }
+ }}
for _, csiDriver := range successCases {
if errs := ValidateCSIDriver(&csiDriver); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
- errorCases := []storage.CSIDriver{
- {
- ObjectMeta: metav1.ObjectMeta{Name: invalidName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachRequired,
- PodInfoOnMount: &podInfoOnMount,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ errorCases := []storage.CSIDriver{{
+ ObjectMeta: metav1.ObjectMeta{Name: invalidName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachRequired,
+ PodInfoOnMount: &podInfoOnMount,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- ObjectMeta: metav1.ObjectMeta{Name: longName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ ObjectMeta: metav1.ObjectMeta{Name: longName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- // AttachRequired not set
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: nil,
- PodInfoOnMount: &podInfoOnMount,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ // AttachRequired not set
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: nil,
+ PodInfoOnMount: &podInfoOnMount,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- // PodInfoOnMount not set
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: nil,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ // PodInfoOnMount not set
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: nil,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- // StorageCapacity not set
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: &podInfoOnMount,
- StorageCapacity: nil,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ // StorageCapacity not set
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: &podInfoOnMount,
+ StorageCapacity: nil,
+ SELinuxMount: &seLinuxMount,
},
- {
- // invalid mode
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- StorageCapacity: &storageCapacity,
- VolumeLifecycleModes: []storage.VolumeLifecycleMode{
- "no-such-mode",
- },
- SELinuxMount: &seLinuxMount,
+ }, {
+ // invalid mode
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ StorageCapacity: &storageCapacity,
+ VolumeLifecycleModes: []storage.VolumeLifecycleMode{
+ "no-such-mode",
},
+ SELinuxMount: &seLinuxMount,
},
- {
- // invalid fsGroupPolicy
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- FSGroupPolicy: &invalidFSGroupPolicy,
- StorageCapacity: &storageCapacity,
- SELinuxMount: &seLinuxMount,
- },
+ }, {
+ // invalid fsGroupPolicy
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ FSGroupPolicy: &invalidFSGroupPolicy,
+ StorageCapacity: &storageCapacity,
+ SELinuxMount: &seLinuxMount,
},
- {
- // no SELinuxMount
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- AttachRequired: &attachNotRequired,
- PodInfoOnMount: ¬PodInfoOnMount,
- StorageCapacity: &storageCapacity,
- },
+ }, {
+ // no SELinuxMount
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ AttachRequired: &attachNotRequired,
+ PodInfoOnMount: ¬PodInfoOnMount,
+ StorageCapacity: &storageCapacity,
},
- }
+ }}
for _, csiDriver := range errorCases {
if errs := ValidateCSIDriver(&csiDriver); len(errs) == 0 {
@@ -1958,36 +1686,30 @@ func TestCSIDriverValidationUpdate(t *testing.T) {
successCases := []struct {
name string
modify func(new *storage.CSIDriver)
- }{
- {
- name: "no change",
- modify: func(new *storage.CSIDriver) {},
+ }{{
+ name: "no change",
+ modify: func(new *storage.CSIDriver) {},
+ }, {
+ name: "change TokenRequests",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.TokenRequests = []storage.TokenRequest{{Audience: gcp}}
},
- {
- name: "change TokenRequests",
- modify: func(new *storage.CSIDriver) {
- new.Spec.TokenRequests = []storage.TokenRequest{{Audience: gcp}}
- },
+ }, {
+ name: "change RequiresRepublish",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.RequiresRepublish = &requiresRepublish
},
- {
- name: "change RequiresRepublish",
- modify: func(new *storage.CSIDriver) {
- new.Spec.RequiresRepublish = &requiresRepublish
- },
+ }, {
+ name: "StorageCapacity changed",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.StorageCapacity = ¬StorageCapacity
},
- {
- name: "StorageCapacity changed",
- modify: func(new *storage.CSIDriver) {
- new.Spec.StorageCapacity = ¬StorageCapacity
- },
+ }, {
+ name: "SELinuxMount changed",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.SELinuxMount = ¬SELinuxMount
},
- {
- name: "SELinuxMount changed",
- modify: func(new *storage.CSIDriver) {
- new.Spec.SELinuxMount = ¬SELinuxMount
- },
- },
- }
+ }}
for _, test := range successCases {
t.Run(test.name, func(t *testing.T) {
new := old.DeepCopy()
@@ -2002,106 +1724,90 @@ func TestCSIDriverValidationUpdate(t *testing.T) {
errorCases := []struct {
name string
modify func(new *storage.CSIDriver)
- }{
- {
- name: "invalid name",
- modify: func(new *storage.CSIDriver) {
- new.Name = invalidName
- },
+ }{{
+ name: "invalid name",
+ modify: func(new *storage.CSIDriver) {
+ new.Name = invalidName
},
- {
- name: "long name",
- modify: func(new *storage.CSIDriver) {
- new.Name = longName
- },
+ }, {
+ name: "long name",
+ modify: func(new *storage.CSIDriver) {
+ new.Name = longName
},
- {
- name: "AttachRequired not set",
- modify: func(new *storage.CSIDriver) {
- new.Spec.AttachRequired = nil
- },
+ }, {
+ name: "AttachRequired not set",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.AttachRequired = nil
},
- {
- name: "AttachRequired changed",
- modify: func(new *storage.CSIDriver) {
- new.Spec.AttachRequired = &attachRequired
- },
+ }, {
+ name: "AttachRequired changed",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.AttachRequired = &attachRequired
},
- {
- name: "PodInfoOnMount not set",
- modify: func(new *storage.CSIDriver) {
- new.Spec.PodInfoOnMount = nil
- },
+ }, {
+ name: "PodInfoOnMount not set",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.PodInfoOnMount = nil
},
- {
- name: "PodInfoOnMount changed",
- modify: func(new *storage.CSIDriver) {
- new.Spec.PodInfoOnMount = &podInfoOnMount
- },
+ }, {
+ name: "PodInfoOnMount changed",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.PodInfoOnMount = &podInfoOnMount
},
- {
- name: "invalid volume lifecycle mode",
- modify: func(new *storage.CSIDriver) {
- new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
- "no-such-mode",
- }
- },
+ }, {
+ name: "invalid volume lifecycle mode",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
+ "no-such-mode",
+ }
},
- {
- name: "volume lifecycle modes not set",
- modify: func(new *storage.CSIDriver) {
- new.Spec.VolumeLifecycleModes = nil
- },
+ }, {
+ name: "volume lifecycle modes not set",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.VolumeLifecycleModes = nil
},
- {
- name: "VolumeLifecyclePersistent removed",
- modify: func(new *storage.CSIDriver) {
- new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
- storage.VolumeLifecycleEphemeral,
- }
- },
+ }, {
+ name: "VolumeLifecyclePersistent removed",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
+ storage.VolumeLifecycleEphemeral,
+ }
},
- {
- name: "VolumeLifecycleEphemeral removed",
- modify: func(new *storage.CSIDriver) {
- new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
- storage.VolumeLifecyclePersistent,
- }
- },
+ }, {
+ name: "VolumeLifecycleEphemeral removed",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.VolumeLifecycleModes = []storage.VolumeLifecycleMode{
+ storage.VolumeLifecyclePersistent,
+ }
},
- {
- name: "FSGroupPolicy invalidated",
- modify: func(new *storage.CSIDriver) {
- invalidFSGroupPolicy := storage.FSGroupPolicy("invalid")
- new.Spec.FSGroupPolicy = &invalidFSGroupPolicy
- },
+ }, {
+ name: "FSGroupPolicy invalidated",
+ modify: func(new *storage.CSIDriver) {
+ invalidFSGroupPolicy := storage.FSGroupPolicy("invalid")
+ new.Spec.FSGroupPolicy = &invalidFSGroupPolicy
},
- {
- name: "FSGroupPolicy changed",
- modify: func(new *storage.CSIDriver) {
- fileFSGroupPolicy := storage.FileFSGroupPolicy
- new.Spec.FSGroupPolicy = &fileFSGroupPolicy
- },
+ }, {
+ name: "FSGroupPolicy changed",
+ modify: func(new *storage.CSIDriver) {
+ fileFSGroupPolicy := storage.FileFSGroupPolicy
+ new.Spec.FSGroupPolicy = &fileFSGroupPolicy
},
- {
- name: "TokenRequests invalidated",
- modify: func(new *storage.CSIDriver) {
- new.Spec.TokenRequests = []storage.TokenRequest{{Audience: gcp}, {Audience: gcp}}
- },
+ }, {
+ name: "TokenRequests invalidated",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.TokenRequests = []storage.TokenRequest{{Audience: gcp}, {Audience: gcp}}
},
- {
- name: "invalid nil StorageCapacity",
- modify: func(new *storage.CSIDriver) {
- new.Spec.StorageCapacity = nil
- },
+ }, {
+ name: "invalid nil StorageCapacity",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.StorageCapacity = nil
},
- {
- name: "SELinuxMount not set",
- modify: func(new *storage.CSIDriver) {
- new.Spec.SELinuxMount = nil
- },
+ }, {
+ name: "SELinuxMount not set",
+ modify: func(new *storage.CSIDriver) {
+ new.Spec.SELinuxMount = nil
},
- }
+ }}
for _, test := range errorCases {
t.Run(test.name, func(t *testing.T) {
@@ -2224,15 +1930,13 @@ func TestValidateCSIStorageCapacity(t *testing.T) {
capacity: func() *storage.CSIStorageCapacity {
capacity := goodCapacity
capacity.NodeTopology = &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "foo",
- Operator: metav1.LabelSelectorOperator("no-such-operator"),
- Values: []string{
- "bar",
- },
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "foo",
+ Operator: metav1.LabelSelectorOperator("no-such-operator"),
+ Values: []string{
+ "bar",
},
- },
+ }},
}
return &capacity
}(),
@@ -2262,61 +1966,55 @@ func TestCSIServiceAccountToken(t *testing.T) {
desc string
csiDriver *storage.CSIDriver
wantErr bool
- }{
- {
- desc: "invalid - TokenRequests has tokens with the same audience",
- csiDriver: &storage.CSIDriver{
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- TokenRequests: []storage.TokenRequest{{Audience: gcp}, {Audience: gcp}},
- RequiresRepublish: ¬RequiresRepublish,
- },
- },
- wantErr: true,
- },
- {
- desc: "invalid - TokenRequests has tokens with ExpirationSeconds less than 10min",
- csiDriver: &storage.CSIDriver{
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- TokenRequests: []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64(10)}},
- RequiresRepublish: ¬RequiresRepublish,
- },
- },
- wantErr: true,
- },
- {
- desc: "invalid - TokenRequests has tokens with ExpirationSeconds longer than 1<<32 min",
- csiDriver: &storage.CSIDriver{
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- TokenRequests: []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64(1<<32 + 1)}},
- RequiresRepublish: ¬RequiresRepublish,
- },
- },
- wantErr: true,
- },
- {
- desc: "valid - TokenRequests has at most one token with empty string audience",
- csiDriver: &storage.CSIDriver{
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- TokenRequests: []storage.TokenRequest{{Audience: ""}},
- RequiresRepublish: ¬RequiresRepublish,
- },
+ }{{
+ desc: "invalid - TokenRequests has tokens with the same audience",
+ csiDriver: &storage.CSIDriver{
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ TokenRequests: []storage.TokenRequest{{Audience: gcp}, {Audience: gcp}},
+ RequiresRepublish: ¬RequiresRepublish,
},
},
- {
- desc: "valid - TokenRequests has tokens with different audience",
- csiDriver: &storage.CSIDriver{
- ObjectMeta: metav1.ObjectMeta{Name: driverName},
- Spec: storage.CSIDriverSpec{
- TokenRequests: []storage.TokenRequest{{}, {Audience: gcp}, {Audience: aws}},
- RequiresRepublish: ¬RequiresRepublish,
- },
+ wantErr: true,
+ }, {
+ desc: "invalid - TokenRequests has tokens with ExpirationSeconds less than 10min",
+ csiDriver: &storage.CSIDriver{
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ TokenRequests: []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64(10)}},
+ RequiresRepublish: ¬RequiresRepublish,
},
},
- }
+ wantErr: true,
+ }, {
+ desc: "invalid - TokenRequests has tokens with ExpirationSeconds longer than 1<<32 min",
+ csiDriver: &storage.CSIDriver{
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ TokenRequests: []storage.TokenRequest{{Audience: gcp, ExpirationSeconds: utilpointer.Int64(1<<32 + 1)}},
+ RequiresRepublish: ¬RequiresRepublish,
+ },
+ },
+ wantErr: true,
+ }, {
+ desc: "valid - TokenRequests has at most one token with empty string audience",
+ csiDriver: &storage.CSIDriver{
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ TokenRequests: []storage.TokenRequest{{Audience: ""}},
+ RequiresRepublish: ¬RequiresRepublish,
+ },
+ },
+ }, {
+ desc: "valid - TokenRequests has tokens with different audience",
+ csiDriver: &storage.CSIDriver{
+ ObjectMeta: metav1.ObjectMeta{Name: driverName},
+ Spec: storage.CSIDriverSpec{
+ TokenRequests: []storage.TokenRequest{{}, {Audience: gcp}, {Audience: aws}},
+ RequiresRepublish: ¬RequiresRepublish,
+ },
+ },
+ }}
for _, test := range tests {
test.csiDriver.Spec.AttachRequired = new(bool)
@@ -2335,32 +2033,27 @@ func TestCSIDriverValidationSELinuxMountAlpha(t *testing.T) {
featureEnabled bool
seLinuxMountValue *bool
expectError bool
- }{
- {
- name: "feature enabled, nil value",
- featureEnabled: true,
- seLinuxMountValue: nil,
- expectError: true,
- },
- {
- name: "feature enabled, non-nil value",
- featureEnabled: true,
- seLinuxMountValue: utilpointer.Bool(true),
- expectError: false,
- },
- {
- name: "feature disabled, nil value",
- featureEnabled: false,
- seLinuxMountValue: nil,
- expectError: false,
- },
- {
- name: "feature disabled, non-nil value",
- featureEnabled: false,
- seLinuxMountValue: utilpointer.Bool(true),
- expectError: false,
- },
- }
+ }{{
+ name: "feature enabled, nil value",
+ featureEnabled: true,
+ seLinuxMountValue: nil,
+ expectError: true,
+ }, {
+ name: "feature enabled, non-nil value",
+ featureEnabled: true,
+ seLinuxMountValue: utilpointer.Bool(true),
+ expectError: false,
+ }, {
+ name: "feature disabled, nil value",
+ featureEnabled: false,
+ seLinuxMountValue: nil,
+ expectError: false,
+ }, {
+ name: "feature disabled, non-nil value",
+ featureEnabled: false,
+ seLinuxMountValue: utilpointer.Bool(true),
+ expectError: false,
+ }}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SELinuxMountReadWriteOncePod, test.featureEnabled)()
diff --git a/pkg/kubelet/apis/config/validation/validation_test.go b/pkg/kubelet/apis/config/validation/validation_test.go
index c1b049e059e..776a7597372 100644
--- a/pkg/kubelet/apis/config/validation/validation_test.go
+++ b/pkg/kubelet/apis/config/validation/validation_test.go
@@ -86,502 +86,442 @@ func TestValidateKubeletConfiguration(t *testing.T) {
name string
configure func(config *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration
errMsg string
- }{
- {
- name: "Success",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- return conf
- },
+ }{{
+ name: "Success",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ return conf
},
- {
- name: "invalid NodeLeaseDurationSeconds",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.NodeLeaseDurationSeconds = 0
- return conf
- },
- errMsg: "invalid configuration: nodeLeaseDurationSeconds must be greater than 0",
+ }, {
+ name: "invalid NodeLeaseDurationSeconds",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.NodeLeaseDurationSeconds = 0
+ return conf
},
- {
- name: "specify EnforceNodeAllocatable without enabling CgroupsPerQOS",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.CgroupsPerQOS = false
- conf.EnforceNodeAllocatable = []string{"pods"}
- return conf
- },
- errMsg: "invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) is not supported unless cgroupsPerQOS (--cgroups-per-qos) is set to true",
+ errMsg: "invalid configuration: nodeLeaseDurationSeconds must be greater than 0",
+ }, {
+ name: "specify EnforceNodeAllocatable without enabling CgroupsPerQOS",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.CgroupsPerQOS = false
+ conf.EnforceNodeAllocatable = []string{"pods"}
+ return conf
},
- {
- name: "specify SystemCgroups without CgroupRoot",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.SystemCgroups = "/"
- conf.CgroupRoot = ""
- return conf
- },
- errMsg: "invalid configuration: systemCgroups (--system-cgroups) was specified and cgroupRoot (--cgroup-root) was not specified",
+ errMsg: "invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) is not supported unless cgroupsPerQOS (--cgroups-per-qos) is set to true",
+ }, {
+ name: "specify SystemCgroups without CgroupRoot",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.SystemCgroups = "/"
+ conf.CgroupRoot = ""
+ return conf
},
- {
- name: "invalid EventBurst",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.EventBurst = -1
- return conf
- },
- errMsg: "invalid configuration: eventBurst (--event-burst) -1 must not be a negative number",
+ errMsg: "invalid configuration: systemCgroups (--system-cgroups) was specified and cgroupRoot (--cgroup-root) was not specified",
+ }, {
+ name: "invalid EventBurst",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.EventBurst = -1
+ return conf
},
- {
- name: "invalid EventRecordQPS",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.EventRecordQPS = -1
- return conf
- },
- errMsg: "invalid configuration: eventRecordQPS (--event-qps) -1 must not be a negative number",
+ errMsg: "invalid configuration: eventBurst (--event-burst) -1 must not be a negative number",
+ }, {
+ name: "invalid EventRecordQPS",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.EventRecordQPS = -1
+ return conf
},
- {
- name: "invalid HealthzPort",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.HealthzPort = 65536
- return conf
- },
- errMsg: "invalid configuration: healthzPort (--healthz-port) 65536 must be between 1 and 65535, inclusive",
+ errMsg: "invalid configuration: eventRecordQPS (--event-qps) -1 must not be a negative number",
+ }, {
+ name: "invalid HealthzPort",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.HealthzPort = 65536
+ return conf
},
- {
- name: "specify CPUCFSQuotaPeriod without enabling CPUCFSQuotaPeriod",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"CustomCPUCFSQuotaPeriod": false}
- conf.CPUCFSQuotaPeriod = metav1.Duration{Duration: 200 * time.Millisecond}
- return conf
- },
- errMsg: "invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) {200ms} requires feature gate CustomCPUCFSQuotaPeriod",
+ errMsg: "invalid configuration: healthzPort (--healthz-port) 65536 must be between 1 and 65535, inclusive",
+ }, {
+ name: "specify CPUCFSQuotaPeriod without enabling CPUCFSQuotaPeriod",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"CustomCPUCFSQuotaPeriod": false}
+ conf.CPUCFSQuotaPeriod = metav1.Duration{Duration: 200 * time.Millisecond}
+ return conf
},
- {
- name: "invalid CPUCFSQuotaPeriod",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"CustomCPUCFSQuotaPeriod": true}
- conf.CPUCFSQuotaPeriod = metav1.Duration{Duration: 2 * time.Second}
- return conf
- },
- errMsg: "invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) {2s} must be between 1ms and 1sec, inclusive",
+ errMsg: "invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) {200ms} requires feature gate CustomCPUCFSQuotaPeriod",
+ }, {
+ name: "invalid CPUCFSQuotaPeriod",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"CustomCPUCFSQuotaPeriod": true}
+ conf.CPUCFSQuotaPeriod = metav1.Duration{Duration: 2 * time.Second}
+ return conf
},
- {
- name: "invalid ImageGCHighThresholdPercent",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.ImageGCHighThresholdPercent = 101
- return conf
- },
- errMsg: "invalid configuration: imageGCHighThresholdPercent (--image-gc-high-threshold) 101 must be between 0 and 100, inclusive",
+ errMsg: "invalid configuration: cpuCFSQuotaPeriod (--cpu-cfs-quota-period) {2s} must be between 1ms and 1sec, inclusive",
+ }, {
+ name: "invalid ImageGCHighThresholdPercent",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.ImageGCHighThresholdPercent = 101
+ return conf
},
- {
- name: "invalid ImageGCLowThresholdPercent",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.ImageGCLowThresholdPercent = -1
- return conf
- },
- errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) -1 must be between 0 and 100, inclusive",
+ errMsg: "invalid configuration: imageGCHighThresholdPercent (--image-gc-high-threshold) 101 must be between 0 and 100, inclusive",
+ }, {
+ name: "invalid ImageGCLowThresholdPercent",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.ImageGCLowThresholdPercent = -1
+ return conf
},
- {
- name: "ImageGCLowThresholdPercent is equal to ImageGCHighThresholdPercent",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.ImageGCHighThresholdPercent = 0
- conf.ImageGCLowThresholdPercent = 0
- return conf
- },
- errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) 0 must be less than imageGCHighThresholdPercent (--image-gc-high-threshold) 0",
+ errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) -1 must be between 0 and 100, inclusive",
+ }, {
+ name: "ImageGCLowThresholdPercent is equal to ImageGCHighThresholdPercent",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.ImageGCHighThresholdPercent = 0
+ conf.ImageGCLowThresholdPercent = 0
+ return conf
},
- {
- name: "ImageGCLowThresholdPercent is greater than ImageGCHighThresholdPercent",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.ImageGCHighThresholdPercent = 0
- conf.ImageGCLowThresholdPercent = 1
- return conf
- },
- errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) 1 must be less than imageGCHighThresholdPercent (--image-gc-high-threshold) 0",
+ errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) 0 must be less than imageGCHighThresholdPercent (--image-gc-high-threshold) 0",
+ }, {
+ name: "ImageGCLowThresholdPercent is greater than ImageGCHighThresholdPercent",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.ImageGCHighThresholdPercent = 0
+ conf.ImageGCLowThresholdPercent = 1
+ return conf
},
- {
- name: "invalid IPTablesDropBit",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.IPTablesDropBit = 32
- return conf
- },
- errMsg: "invalid configuration: iptablesDropBit (--iptables-drop-bit) 32 must be between 0 and 31, inclusive",
+ errMsg: "invalid configuration: imageGCLowThresholdPercent (--image-gc-low-threshold) 1 must be less than imageGCHighThresholdPercent (--image-gc-high-threshold) 0",
+ }, {
+ name: "invalid IPTablesDropBit",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.IPTablesDropBit = 32
+ return conf
},
- {
- name: "invalid IPTablesMasqueradeBit",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.IPTablesMasqueradeBit = 32
- return conf
- },
- errMsg: "invalid configuration: iptablesMasqueradeBit (--iptables-masquerade-bit) 32 must be between 0 and 31, inclusive",
+ errMsg: "invalid configuration: iptablesDropBit (--iptables-drop-bit) 32 must be between 0 and 31, inclusive",
+ }, {
+ name: "invalid IPTablesMasqueradeBit",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.IPTablesMasqueradeBit = 32
+ return conf
},
- {
- name: "invalid KubeAPIBurst",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.KubeAPIBurst = -1
- return conf
- },
- errMsg: "invalid configuration: kubeAPIBurst (--kube-api-burst) -1 must not be a negative number",
+ errMsg: "invalid configuration: iptablesMasqueradeBit (--iptables-masquerade-bit) 32 must be between 0 and 31, inclusive",
+ }, {
+ name: "invalid KubeAPIBurst",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.KubeAPIBurst = -1
+ return conf
},
- {
- name: "invalid KubeAPIQPS",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.KubeAPIQPS = -1
- return conf
- },
- errMsg: "invalid configuration: kubeAPIQPS (--kube-api-qps) -1 must not be a negative number",
+ errMsg: "invalid configuration: kubeAPIBurst (--kube-api-burst) -1 must not be a negative number",
+ }, {
+ name: "invalid KubeAPIQPS",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.KubeAPIQPS = -1
+ return conf
},
- {
- name: "invalid NodeStatusMaxImages",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.NodeStatusMaxImages = -2
- return conf
- },
- errMsg: "invalid configuration: nodeStatusMaxImages (--node-status-max-images) -2 must be -1 or greater",
+ errMsg: "invalid configuration: kubeAPIQPS (--kube-api-qps) -1 must not be a negative number",
+ }, {
+ name: "invalid NodeStatusMaxImages",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.NodeStatusMaxImages = -2
+ return conf
},
- {
- name: "invalid MaxOpenFiles",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.MaxOpenFiles = -1
- return conf
- },
- errMsg: "invalid configuration: maxOpenFiles (--max-open-files) -1 must not be a negative number",
+ errMsg: "invalid configuration: nodeStatusMaxImages (--node-status-max-images) -2 must be -1 or greater",
+ }, {
+ name: "invalid MaxOpenFiles",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.MaxOpenFiles = -1
+ return conf
},
- {
- name: "invalid MaxPods",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.MaxPods = -1
- return conf
- },
- errMsg: "invalid configuration: maxPods (--max-pods) -1 must not be a negative number",
+ errMsg: "invalid configuration: maxOpenFiles (--max-open-files) -1 must not be a negative number",
+ }, {
+ name: "invalid MaxPods",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.MaxPods = -1
+ return conf
},
- {
- name: "invalid OOMScoreAdj",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.OOMScoreAdj = 1001
- return conf
- },
- errMsg: "invalid configuration: oomScoreAdj (--oom-score-adj) 1001 must be between -1000 and 1000, inclusive",
+ errMsg: "invalid configuration: maxPods (--max-pods) -1 must not be a negative number",
+ }, {
+ name: "invalid OOMScoreAdj",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.OOMScoreAdj = 1001
+ return conf
},
- {
- name: "invalid PodsPerCore",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.PodsPerCore = -1
- return conf
- },
- errMsg: "invalid configuration: podsPerCore (--pods-per-core) -1 must not be a negative number",
+ errMsg: "invalid configuration: oomScoreAdj (--oom-score-adj) 1001 must be between -1000 and 1000, inclusive",
+ }, {
+ name: "invalid PodsPerCore",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.PodsPerCore = -1
+ return conf
},
- {
- name: "invalid Port",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.Port = 65536
- return conf
- },
- errMsg: "invalid configuration: port (--port) 65536 must be between 1 and 65535, inclusive",
+ errMsg: "invalid configuration: podsPerCore (--pods-per-core) -1 must not be a negative number",
+ }, {
+ name: "invalid Port",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.Port = 65536
+ return conf
},
- {
- name: "invalid ReadOnlyPort",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.ReadOnlyPort = 65536
- return conf
- },
- errMsg: "invalid configuration: readOnlyPort (--read-only-port) 65536 must be between 0 and 65535, inclusive",
+ errMsg: "invalid configuration: port (--port) 65536 must be between 1 and 65535, inclusive",
+ }, {
+ name: "invalid ReadOnlyPort",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.ReadOnlyPort = 65536
+ return conf
},
- {
- name: "invalid RegistryBurst",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.RegistryBurst = -1
- return conf
- },
- errMsg: "invalid configuration: registryBurst (--registry-burst) -1 must not be a negative number",
+ errMsg: "invalid configuration: readOnlyPort (--read-only-port) 65536 must be between 0 and 65535, inclusive",
+ }, {
+ name: "invalid RegistryBurst",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.RegistryBurst = -1
+ return conf
},
- {
- name: "invalid RegistryPullQPS",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.RegistryPullQPS = -1
- return conf
- },
- errMsg: "invalid configuration: registryPullQPS (--registry-qps) -1 must not be a negative number",
+ errMsg: "invalid configuration: registryBurst (--registry-burst) -1 must not be a negative number",
+ }, {
+ name: "invalid RegistryPullQPS",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.RegistryPullQPS = -1
+ return conf
},
- {
- name: "invalid MaxParallelImagePulls",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.MaxParallelImagePulls = utilpointer.Int32(0)
- return conf
- },
- errMsg: "invalid configuration: maxParallelImagePulls 0 must be a positive number",
+ errMsg: "invalid configuration: registryPullQPS (--registry-qps) -1 must not be a negative number",
+ }, {
+ name: "invalid MaxParallelImagePulls",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.MaxParallelImagePulls = utilpointer.Int32(0)
+ return conf
},
- {
- name: "invalid MaxParallelImagePulls and SerializeImagePulls combination",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.MaxParallelImagePulls = utilpointer.Int32(3)
- conf.SerializeImagePulls = true
- return conf
- },
- errMsg: "invalid configuration: maxParallelImagePulls cannot be larger than 1 unless SerializeImagePulls (--serialize-image-pulls) is set to false",
+ errMsg: "invalid configuration: maxParallelImagePulls 0 must be a positive number",
+ }, {
+ name: "invalid MaxParallelImagePulls and SerializeImagePulls combination",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.MaxParallelImagePulls = utilpointer.Int32(3)
+ conf.SerializeImagePulls = true
+ return conf
},
- {
- name: "valid MaxParallelImagePulls and SerializeImagePulls combination",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.MaxParallelImagePulls = utilpointer.Int32(1)
- conf.SerializeImagePulls = true
- return conf
- },
+ errMsg: "invalid configuration: maxParallelImagePulls cannot be larger than 1 unless SerializeImagePulls (--serialize-image-pulls) is set to false",
+ }, {
+ name: "valid MaxParallelImagePulls and SerializeImagePulls combination",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.MaxParallelImagePulls = utilpointer.Int32(1)
+ conf.SerializeImagePulls = true
+ return conf
},
- {
- name: "specify ServerTLSBootstrap without enabling RotateKubeletServerCertificate",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"RotateKubeletServerCertificate": false}
- conf.ServerTLSBootstrap = true
- return conf
- },
- errMsg: "invalid configuration: serverTLSBootstrap true requires feature gate RotateKubeletServerCertificate",
+ }, {
+ name: "specify ServerTLSBootstrap without enabling RotateKubeletServerCertificate",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"RotateKubeletServerCertificate": false}
+ conf.ServerTLSBootstrap = true
+ return conf
},
- {
- name: "invalid TopologyManagerPolicy",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.TopologyManagerPolicy = "invalid-policy"
- return conf
- },
- errMsg: "invalid configuration: topologyManagerPolicy (--topology-manager-policy) \"invalid-policy\" must be one of: [\"none\" \"best-effort\" \"restricted\" \"single-numa-node\"]",
+ errMsg: "invalid configuration: serverTLSBootstrap true requires feature gate RotateKubeletServerCertificate",
+ }, {
+ name: "invalid TopologyManagerPolicy",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.TopologyManagerPolicy = "invalid-policy"
+ return conf
},
- {
- name: "invalid TopologyManagerScope",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.TopologyManagerScope = "invalid-scope"
- return conf
- },
- errMsg: "invalid configuration: topologyManagerScope (--topology-manager-scope) \"invalid-scope\" must be one of: \"container\", or \"pod\"",
+ errMsg: "invalid configuration: topologyManagerPolicy (--topology-manager-policy) \"invalid-policy\" must be one of: [\"none\" \"best-effort\" \"restricted\" \"single-numa-node\"]",
+ }, {
+ name: "invalid TopologyManagerScope",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.TopologyManagerScope = "invalid-scope"
+ return conf
},
- {
- name: "ShutdownGracePeriodCriticalPods is greater than ShutdownGracePeriod",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true}
- conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 2 * time.Second}
- conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Second}
- return conf
- },
- errMsg: "invalid configuration: shutdownGracePeriodCriticalPods {2s} must be <= shutdownGracePeriod {1s}",
+ errMsg: "invalid configuration: topologyManagerScope (--topology-manager-scope) \"invalid-scope\" must be one of: \"container\", or \"pod\"",
+ }, {
+ name: "ShutdownGracePeriodCriticalPods is greater than ShutdownGracePeriod",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true}
+ conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 2 * time.Second}
+ conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Second}
+ return conf
},
- {
- name: "ShutdownGracePeriod is less than 1 sec",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true}
- conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Millisecond}
- return conf
- },
- errMsg: "invalid configuration: shutdownGracePeriod {1ms} must be either zero or otherwise >= 1 sec",
+ errMsg: "invalid configuration: shutdownGracePeriodCriticalPods {2s} must be <= shutdownGracePeriod {1s}",
+ }, {
+ name: "ShutdownGracePeriod is less than 1 sec",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true}
+ conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Millisecond}
+ return conf
},
- {
- name: "ShutdownGracePeriodCriticalPods is less than 1 sec",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true}
- conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 1 * time.Millisecond}
- return conf
- },
- errMsg: "invalid configuration: shutdownGracePeriodCriticalPods {1ms} must be either zero or otherwise >= 1 sec",
+ errMsg: "invalid configuration: shutdownGracePeriod {1ms} must be either zero or otherwise >= 1 sec",
+ }, {
+ name: "ShutdownGracePeriodCriticalPods is less than 1 sec",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": true}
+ conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 1 * time.Millisecond}
+ return conf
},
- {
- name: "specify ShutdownGracePeriod without enabling GracefulNodeShutdown",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": false}
- conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Second}
- return conf
- },
- errMsg: "invalid configuration: specifying shutdownGracePeriod or shutdownGracePeriodCriticalPods requires feature gate GracefulNodeShutdown",
+ errMsg: "invalid configuration: shutdownGracePeriodCriticalPods {1ms} must be either zero or otherwise >= 1 sec",
+ }, {
+ name: "specify ShutdownGracePeriod without enabling GracefulNodeShutdown",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": false}
+ conf.ShutdownGracePeriod = metav1.Duration{Duration: 1 * time.Second}
+ return conf
},
- {
- name: "specify ShutdownGracePeriodCriticalPods without enabling GracefulNodeShutdown",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": false}
- conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 1 * time.Second}
- return conf
- },
- errMsg: "invalid configuration: specifying shutdownGracePeriod or shutdownGracePeriodCriticalPods requires feature gate GracefulNodeShutdown",
+ errMsg: "invalid configuration: specifying shutdownGracePeriod or shutdownGracePeriodCriticalPods requires feature gate GracefulNodeShutdown",
+ }, {
+ name: "specify ShutdownGracePeriodCriticalPods without enabling GracefulNodeShutdown",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"GracefulNodeShutdown": false}
+ conf.ShutdownGracePeriodCriticalPods = metav1.Duration{Duration: 1 * time.Second}
+ return conf
},
- {
- name: "invalid MemorySwap.SwapBehavior",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"NodeSwap": true}
- conf.MemorySwap.SwapBehavior = "invalid-behavior"
- return conf
- },
- errMsg: "invalid configuration: memorySwap.swapBehavior \"invalid-behavior\" must be one of: \"\", \"LimitedSwap\", or \"UnlimitedSwap\"",
+ errMsg: "invalid configuration: specifying shutdownGracePeriod or shutdownGracePeriodCriticalPods requires feature gate GracefulNodeShutdown",
+ }, {
+ name: "invalid MemorySwap.SwapBehavior",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"NodeSwap": true}
+ conf.MemorySwap.SwapBehavior = "invalid-behavior"
+ return conf
},
- {
- name: "specify MemorySwap.SwapBehavior without enabling NodeSwap",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"NodeSwap": false}
- conf.MemorySwap.SwapBehavior = kubetypes.LimitedSwap
- return conf
- },
- errMsg: "invalid configuration: memorySwap.swapBehavior cannot be set when NodeSwap feature flag is disabled",
+ errMsg: "invalid configuration: memorySwap.swapBehavior \"invalid-behavior\" must be one of: \"\", \"LimitedSwap\", or \"UnlimitedSwap\"",
+ }, {
+ name: "specify MemorySwap.SwapBehavior without enabling NodeSwap",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"NodeSwap": false}
+ conf.MemorySwap.SwapBehavior = kubetypes.LimitedSwap
+ return conf
},
- {
- name: "specify SystemReservedEnforcementKey without specifying SystemReservedCgroup",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.EnforceNodeAllocatable = []string{kubetypes.SystemReservedEnforcementKey}
- conf.SystemReservedCgroup = ""
- return conf
- },
- errMsg: "invalid configuration: systemReservedCgroup (--system-reserved-cgroup) must be specified when \"system-reserved\" contained in enforceNodeAllocatable (--enforce-node-allocatable)",
+ errMsg: "invalid configuration: memorySwap.swapBehavior cannot be set when NodeSwap feature flag is disabled",
+ }, {
+ name: "specify SystemReservedEnforcementKey without specifying SystemReservedCgroup",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.EnforceNodeAllocatable = []string{kubetypes.SystemReservedEnforcementKey}
+ conf.SystemReservedCgroup = ""
+ return conf
},
- {
- name: "specify KubeReservedEnforcementKey without specifying KubeReservedCgroup",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.EnforceNodeAllocatable = []string{kubetypes.KubeReservedEnforcementKey}
- conf.KubeReservedCgroup = ""
- return conf
- },
- errMsg: "invalid configuration: kubeReservedCgroup (--kube-reserved-cgroup) must be specified when \"kube-reserved\" contained in enforceNodeAllocatable (--enforce-node-allocatable)",
+ errMsg: "invalid configuration: systemReservedCgroup (--system-reserved-cgroup) must be specified when \"system-reserved\" contained in enforceNodeAllocatable (--enforce-node-allocatable)",
+ }, {
+ name: "specify KubeReservedEnforcementKey without specifying KubeReservedCgroup",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.EnforceNodeAllocatable = []string{kubetypes.KubeReservedEnforcementKey}
+ conf.KubeReservedCgroup = ""
+ return conf
},
- {
- name: "specify NodeAllocatableNoneKey with additional enforcements",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.EnforceNodeAllocatable = []string{kubetypes.NodeAllocatableNoneKey, kubetypes.KubeReservedEnforcementKey}
- return conf
- },
- errMsg: "invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) may not contain additional enforcements when \"none\" is specified",
+ errMsg: "invalid configuration: kubeReservedCgroup (--kube-reserved-cgroup) must be specified when \"kube-reserved\" contained in enforceNodeAllocatable (--enforce-node-allocatable)",
+ }, {
+ name: "specify NodeAllocatableNoneKey with additional enforcements",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.EnforceNodeAllocatable = []string{kubetypes.NodeAllocatableNoneKey, kubetypes.KubeReservedEnforcementKey}
+ return conf
},
- {
- name: "invalid EnforceNodeAllocatable",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.EnforceNodeAllocatable = []string{"invalid-enforce-node-allocatable"}
- return conf
- },
- errMsg: "invalid configuration: option \"invalid-enforce-node-allocatable\" specified for enforceNodeAllocatable (--enforce-node-allocatable). Valid options are \"pods\", \"system-reserved\", \"kube-reserved\", or \"none\"",
+ errMsg: "invalid configuration: enforceNodeAllocatable (--enforce-node-allocatable) may not contain additional enforcements when \"none\" is specified",
+ }, {
+ name: "invalid EnforceNodeAllocatable",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.EnforceNodeAllocatable = []string{"invalid-enforce-node-allocatable"}
+ return conf
},
- {
- name: "invalid HairpinMode",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.HairpinMode = "invalid-hair-pin-mode"
- return conf
- },
- errMsg: "invalid configuration: option \"invalid-hair-pin-mode\" specified for hairpinMode (--hairpin-mode). Valid options are \"none\", \"hairpin-veth\" or \"promiscuous-bridge\"",
+ errMsg: "invalid configuration: option \"invalid-enforce-node-allocatable\" specified for enforceNodeAllocatable (--enforce-node-allocatable). Valid options are \"pods\", \"system-reserved\", \"kube-reserved\", or \"none\"",
+ }, {
+ name: "invalid HairpinMode",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.HairpinMode = "invalid-hair-pin-mode"
+ return conf
},
- {
- name: "specify ReservedSystemCPUs with SystemReservedCgroup",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.ReservedSystemCPUs = "0-3"
- conf.SystemReservedCgroup = "/system.slice"
- return conf
- },
- errMsg: "invalid configuration: can't use reservedSystemCPUs (--reserved-cpus) with systemReservedCgroup (--system-reserved-cgroup) or kubeReservedCgroup (--kube-reserved-cgroup)",
+ errMsg: "invalid configuration: option \"invalid-hair-pin-mode\" specified for hairpinMode (--hairpin-mode). Valid options are \"none\", \"hairpin-veth\" or \"promiscuous-bridge\"",
+ }, {
+ name: "specify ReservedSystemCPUs with SystemReservedCgroup",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.ReservedSystemCPUs = "0-3"
+ conf.SystemReservedCgroup = "/system.slice"
+ return conf
},
- {
- name: "specify ReservedSystemCPUs with KubeReservedCgroup",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.ReservedSystemCPUs = "0-3"
- conf.KubeReservedCgroup = "/system.slice"
- return conf
- },
- errMsg: "invalid configuration: can't use reservedSystemCPUs (--reserved-cpus) with systemReservedCgroup (--system-reserved-cgroup) or kubeReservedCgroup (--kube-reserved-cgroup)",
+ errMsg: "invalid configuration: can't use reservedSystemCPUs (--reserved-cpus) with systemReservedCgroup (--system-reserved-cgroup) or kubeReservedCgroup (--kube-reserved-cgroup)",
+ }, {
+ name: "specify ReservedSystemCPUs with KubeReservedCgroup",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.ReservedSystemCPUs = "0-3"
+ conf.KubeReservedCgroup = "/system.slice"
+ return conf
},
- {
- name: "invalid ReservedSystemCPUs",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.ReservedSystemCPUs = "invalid-reserved-system-cpus"
- return conf
- },
- errMsg: "invalid configuration: unable to parse reservedSystemCPUs (--reserved-cpus) invalid-reserved-system-cpus, error:",
+ errMsg: "invalid configuration: can't use reservedSystemCPUs (--reserved-cpus) with systemReservedCgroup (--system-reserved-cgroup) or kubeReservedCgroup (--kube-reserved-cgroup)",
+ }, {
+ name: "invalid ReservedSystemCPUs",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.ReservedSystemCPUs = "invalid-reserved-system-cpus"
+ return conf
},
- {
- name: "enable MemoryQoS without specifying MemoryThrottlingFactor",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"MemoryQoS": true}
- conf.MemoryThrottlingFactor = nil
- return conf
- },
- errMsg: "invalid configuration: memoryThrottlingFactor is required when MemoryQoS feature flag is enabled",
+ errMsg: "invalid configuration: unable to parse reservedSystemCPUs (--reserved-cpus) invalid-reserved-system-cpus, error:",
+ }, {
+ name: "enable MemoryQoS without specifying MemoryThrottlingFactor",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"MemoryQoS": true}
+ conf.MemoryThrottlingFactor = nil
+ return conf
},
- {
- name: "invalid MemoryThrottlingFactor",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.MemoryThrottlingFactor = utilpointer.Float64(1.1)
- return conf
- },
- errMsg: "invalid configuration: memoryThrottlingFactor 1.1 must be greater than 0 and less than or equal to 1.0",
+ errMsg: "invalid configuration: memoryThrottlingFactor is required when MemoryQoS feature flag is enabled",
+ }, {
+ name: "invalid MemoryThrottlingFactor",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.MemoryThrottlingFactor = utilpointer.Float64(1.1)
+ return conf
},
- {
- name: "invalid Taint.TimeAdded",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- now := metav1.Now()
- conf.RegisterWithTaints = []v1.Taint{{TimeAdded: &now}}
- return conf
- },
- errMsg: "invalid configuration: taint.TimeAdded is not nil",
+ errMsg: "invalid configuration: memoryThrottlingFactor 1.1 must be greater than 0 and less than or equal to 1.0",
+ }, {
+ name: "invalid Taint.TimeAdded",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ now := metav1.Now()
+ conf.RegisterWithTaints = []v1.Taint{{TimeAdded: &now}}
+ return conf
},
- {
- name: "specify tracing with KubeletTracing disabled",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- samplingRate := int32(99999)
- conf.FeatureGates = map[string]bool{"KubeletTracing": false}
- conf.Tracing = &tracingapi.TracingConfiguration{SamplingRatePerMillion: &samplingRate}
- return conf
- },
- errMsg: "invalid configuration: tracing should not be configured if KubeletTracing feature flag is disabled.",
+ errMsg: "invalid configuration: taint.TimeAdded is not nil",
+ }, {
+ name: "specify tracing with KubeletTracing disabled",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ samplingRate := int32(99999)
+ conf.FeatureGates = map[string]bool{"KubeletTracing": false}
+ conf.Tracing = &tracingapi.TracingConfiguration{SamplingRatePerMillion: &samplingRate}
+ return conf
},
- {
- name: "specify tracing invalid sampling rate",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- samplingRate := int32(-1)
- conf.FeatureGates = map[string]bool{"KubeletTracing": true}
- conf.Tracing = &tracingapi.TracingConfiguration{SamplingRatePerMillion: &samplingRate}
- return conf
- },
- errMsg: "tracing.samplingRatePerMillion: Invalid value: -1: sampling rate must be positive",
+ errMsg: "invalid configuration: tracing should not be configured if KubeletTracing feature flag is disabled.",
+ }, {
+ name: "specify tracing invalid sampling rate",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ samplingRate := int32(-1)
+ conf.FeatureGates = map[string]bool{"KubeletTracing": true}
+ conf.Tracing = &tracingapi.TracingConfiguration{SamplingRatePerMillion: &samplingRate}
+ return conf
},
- {
- name: "specify tracing invalid endpoint",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- ep := "dn%2s://localhost:4317"
- conf.FeatureGates = map[string]bool{"KubeletTracing": true}
- conf.Tracing = &tracingapi.TracingConfiguration{Endpoint: &ep}
- return conf
- },
- errMsg: "tracing.endpoint: Invalid value: \"dn%2s://localhost:4317\": parse \"dn%2s://localhost:4317\": first path segment in URL cannot contain colon",
+ errMsg: "tracing.samplingRatePerMillion: Invalid value: -1: sampling rate must be positive",
+ }, {
+ name: "specify tracing invalid endpoint",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ ep := "dn%2s://localhost:4317"
+ conf.FeatureGates = map[string]bool{"KubeletTracing": true}
+ conf.Tracing = &tracingapi.TracingConfiguration{Endpoint: &ep}
+ return conf
},
- {
- name: "invalid GracefulNodeShutdownBasedOnPodPriority",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"GracefulNodeShutdownBasedOnPodPriority": true}
- conf.ShutdownGracePeriodByPodPriority = []kubeletconfig.ShutdownGracePeriodByPodPriority{
- {
- Priority: 0,
- ShutdownGracePeriodSeconds: 0,
- }}
- return conf
- },
- errMsg: "invalid configuration: Cannot specify both shutdownGracePeriodByPodPriority and shutdownGracePeriod at the same time",
+ errMsg: "tracing.endpoint: Invalid value: \"dn%2s://localhost:4317\": parse \"dn%2s://localhost:4317\": first path segment in URL cannot contain colon",
+ }, {
+ name: "invalid GracefulNodeShutdownBasedOnPodPriority",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"GracefulNodeShutdownBasedOnPodPriority": true}
+ conf.ShutdownGracePeriodByPodPriority = []kubeletconfig.ShutdownGracePeriodByPodPriority{{
+ Priority: 0,
+ ShutdownGracePeriodSeconds: 0,
+ }}
+ return conf
},
- {
- name: "Specifying shutdownGracePeriodByPodPriority without enable GracefulNodeShutdownBasedOnPodPriority",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"GracefulNodeShutdownBasedOnPodPriority": false}
- conf.ShutdownGracePeriodByPodPriority = []kubeletconfig.ShutdownGracePeriodByPodPriority{
- {
- Priority: 0,
- ShutdownGracePeriodSeconds: 0,
- }}
- return conf
- },
- errMsg: "invalid configuration: Specifying shutdownGracePeriodByPodPriority requires feature gate GracefulNodeShutdownBasedOnPodPriority",
+ errMsg: "invalid configuration: Cannot specify both shutdownGracePeriodByPodPriority and shutdownGracePeriod at the same time",
+ }, {
+ name: "Specifying shutdownGracePeriodByPodPriority without enable GracefulNodeShutdownBasedOnPodPriority",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"GracefulNodeShutdownBasedOnPodPriority": false}
+ conf.ShutdownGracePeriodByPodPriority = []kubeletconfig.ShutdownGracePeriodByPodPriority{{
+ Priority: 0,
+ ShutdownGracePeriodSeconds: 0,
+ }}
+ return conf
},
- {
- name: "enableSystemLogQuery is enabled without NodeLogQuery feature gate",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.EnableSystemLogQuery = true
- return conf
- },
- errMsg: "invalid configuration: NodeLogQuery feature gate is required for enableSystemLogHandler",
+ errMsg: "invalid configuration: Specifying shutdownGracePeriodByPodPriority requires feature gate GracefulNodeShutdownBasedOnPodPriority",
+ }, {
+ name: "enableSystemLogQuery is enabled without NodeLogQuery feature gate",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.EnableSystemLogQuery = true
+ return conf
},
- {
- name: "enableSystemLogQuery is enabled without enableSystemLogHandler",
- configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
- conf.FeatureGates = map[string]bool{"NodeLogQuery": true}
- conf.EnableSystemLogHandler = false
- conf.EnableSystemLogQuery = true
- return conf
- },
- errMsg: "invalid configuration: enableSystemLogHandler is required for enableSystemLogQuery",
+ errMsg: "invalid configuration: NodeLogQuery feature gate is required for enableSystemLogHandler",
+ }, {
+ name: "enableSystemLogQuery is enabled without enableSystemLogHandler",
+ configure: func(conf *kubeletconfig.KubeletConfiguration) *kubeletconfig.KubeletConfiguration {
+ conf.FeatureGates = map[string]bool{"NodeLogQuery": true}
+ conf.EnableSystemLogHandler = false
+ conf.EnableSystemLogQuery = true
+ return conf
},
- }
+ errMsg: "invalid configuration: enableSystemLogHandler is required for enableSystemLogQuery",
+ }}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
diff --git a/pkg/proxy/apis/config/validation/validation_test.go b/pkg/proxy/apis/config/validation/validation_test.go
index eee26449a21..8810bc4f1ce 100644
--- a/pkg/proxy/apis/config/validation/validation_test.go
+++ b/pkg/proxy/apis/config/validation/validation_test.go
@@ -36,183 +36,173 @@ func TestValidateKubeProxyConfiguration(t *testing.T) {
} else {
proxyMode = kubeproxyconfig.ProxyModeIPVS
}
- successCases := []kubeproxyconfig.KubeProxyConfiguration{
- {
- BindAddress: "192.168.59.103",
- HealthzBindAddress: "0.0.0.0:10256",
- MetricsBindAddress: "127.0.0.1:10249",
- ClusterCIDR: "192.168.59.0/24",
- ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
- IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
- MasqueradeAll: true,
- SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
- },
- Mode: proxyMode,
- IPVS: kubeproxyconfig.KubeProxyIPVSConfiguration{
- SyncPeriod: metav1.Duration{Duration: 10 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- },
- Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
- MaxPerCore: pointer.Int32(1),
- Min: pointer.Int32(1),
- TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
- TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
- },
+ successCases := []kubeproxyconfig.KubeProxyConfiguration{{
+ BindAddress: "192.168.59.103",
+ HealthzBindAddress: "0.0.0.0:10256",
+ MetricsBindAddress: "127.0.0.1:10249",
+ ClusterCIDR: "192.168.59.0/24",
+ ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
+ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
+ MasqueradeAll: true,
+ SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
},
- {
- BindAddress: "192.168.59.103",
- HealthzBindAddress: "0.0.0.0:10256",
- MetricsBindAddress: "127.0.0.1:10249",
- ClusterCIDR: "192.168.59.0/24",
- ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
- IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
- MasqueradeAll: true,
- SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
- },
- Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
- MaxPerCore: pointer.Int32(1),
- Min: pointer.Int32(1),
- TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
- TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
- },
+ Mode: proxyMode,
+ IPVS: kubeproxyconfig.KubeProxyIPVSConfiguration{
+ SyncPeriod: metav1.Duration{Duration: 10 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 5 * time.Second},
},
- {
- BindAddress: "192.168.59.103",
- HealthzBindAddress: "",
- MetricsBindAddress: "127.0.0.1:10249",
- ClusterCIDR: "192.168.59.0/24",
- ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
- IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
- MasqueradeAll: true,
- SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
- },
- Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
- MaxPerCore: pointer.Int32(1),
- Min: pointer.Int32(1),
- TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
- TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
- },
+ Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
+ MaxPerCore: pointer.Int32(1),
+ Min: pointer.Int32(1),
+ TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
},
- {
- BindAddress: "fd00:192:168:59::103",
- HealthzBindAddress: "",
- MetricsBindAddress: "[::1]:10249",
- ClusterCIDR: "fd00:192:168:59::/64",
- ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
- IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
- MasqueradeAll: true,
- SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
- },
- Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
- MaxPerCore: pointer.Int32(1),
- Min: pointer.Int32(1),
- TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
- TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
- },
+ }, {
+ BindAddress: "192.168.59.103",
+ HealthzBindAddress: "0.0.0.0:10256",
+ MetricsBindAddress: "127.0.0.1:10249",
+ ClusterCIDR: "192.168.59.0/24",
+ ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
+ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
+ MasqueradeAll: true,
+ SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
},
- {
- BindAddress: "10.10.12.11",
- HealthzBindAddress: "0.0.0.0:12345",
- MetricsBindAddress: "127.0.0.1:10249",
- ClusterCIDR: "192.168.59.0/24",
- ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
- IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
- MasqueradeAll: true,
- SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
- },
- Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
- MaxPerCore: pointer.Int32(1),
- Min: pointer.Int32(1),
- TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
- TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
- },
+ Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
+ MaxPerCore: pointer.Int32(1),
+ Min: pointer.Int32(1),
+ TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
},
- {
- BindAddress: "10.10.12.11",
- HealthzBindAddress: "0.0.0.0:12345",
- MetricsBindAddress: "127.0.0.1:10249",
- ClusterCIDR: "fd00:192:168::/64",
- ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
- IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
- MasqueradeAll: true,
- SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
- },
- Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
- MaxPerCore: pointer.Int32(1),
- Min: pointer.Int32(1),
- TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
- TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
- },
+ }, {
+ BindAddress: "192.168.59.103",
+ HealthzBindAddress: "",
+ MetricsBindAddress: "127.0.0.1:10249",
+ ClusterCIDR: "192.168.59.0/24",
+ ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
+ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
+ MasqueradeAll: true,
+ SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
},
- {
- BindAddress: "10.10.12.11",
- HealthzBindAddress: "0.0.0.0:12345",
- MetricsBindAddress: "127.0.0.1:10249",
- ClusterCIDR: "192.168.59.0/24,fd00:192:168::/64",
- ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
- IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
- MasqueradeAll: true,
- SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
- },
- Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
- MaxPerCore: pointer.Int32(1),
- Min: pointer.Int32(1),
- TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
- TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
- },
+ Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
+ MaxPerCore: pointer.Int32(1),
+ Min: pointer.Int32(1),
+ TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
},
- {
- BindAddress: "10.10.12.11",
- HealthzBindAddress: "0.0.0.0:12345",
- MetricsBindAddress: "127.0.0.1:10249",
- ClusterCIDR: "192.168.59.0/24",
- ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
- IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
- MasqueradeAll: true,
- SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
- },
- Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
- MaxPerCore: pointer.Int32(1),
- Min: pointer.Int32(1),
- TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
- TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
- },
- DetectLocalMode: kubeproxyconfig.LocalModeInterfaceNamePrefix,
- DetectLocal: kubeproxyconfig.DetectLocalConfiguration{
- InterfaceNamePrefix: "vethabcde",
- },
+ }, {
+ BindAddress: "fd00:192:168:59::103",
+ HealthzBindAddress: "",
+ MetricsBindAddress: "[::1]:10249",
+ ClusterCIDR: "fd00:192:168:59::/64",
+ ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
+ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
+ MasqueradeAll: true,
+ SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
},
- {
- BindAddress: "10.10.12.11",
- HealthzBindAddress: "0.0.0.0:12345",
- MetricsBindAddress: "127.0.0.1:10249",
- ClusterCIDR: "192.168.59.0/24",
- ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
- IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
- MasqueradeAll: true,
- SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
- MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
- },
- Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
- MaxPerCore: pointer.Int32(1),
- Min: pointer.Int32(1),
- TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
- TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
- },
- DetectLocalMode: kubeproxyconfig.LocalModeBridgeInterface,
- DetectLocal: kubeproxyconfig.DetectLocalConfiguration{
- BridgeInterface: "avz",
- },
+ Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
+ MaxPerCore: pointer.Int32(1),
+ Min: pointer.Int32(1),
+ TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
},
- }
+ }, {
+ BindAddress: "10.10.12.11",
+ HealthzBindAddress: "0.0.0.0:12345",
+ MetricsBindAddress: "127.0.0.1:10249",
+ ClusterCIDR: "192.168.59.0/24",
+ ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
+ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
+ MasqueradeAll: true,
+ SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
+ },
+ Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
+ MaxPerCore: pointer.Int32(1),
+ Min: pointer.Int32(1),
+ TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ },
+ }, {
+ BindAddress: "10.10.12.11",
+ HealthzBindAddress: "0.0.0.0:12345",
+ MetricsBindAddress: "127.0.0.1:10249",
+ ClusterCIDR: "fd00:192:168::/64",
+ ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
+ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
+ MasqueradeAll: true,
+ SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
+ },
+ Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
+ MaxPerCore: pointer.Int32(1),
+ Min: pointer.Int32(1),
+ TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ },
+ }, {
+ BindAddress: "10.10.12.11",
+ HealthzBindAddress: "0.0.0.0:12345",
+ MetricsBindAddress: "127.0.0.1:10249",
+ ClusterCIDR: "192.168.59.0/24,fd00:192:168::/64",
+ ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
+ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
+ MasqueradeAll: true,
+ SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
+ },
+ Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
+ MaxPerCore: pointer.Int32(1),
+ Min: pointer.Int32(1),
+ TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ },
+ }, {
+ BindAddress: "10.10.12.11",
+ HealthzBindAddress: "0.0.0.0:12345",
+ MetricsBindAddress: "127.0.0.1:10249",
+ ClusterCIDR: "192.168.59.0/24",
+ ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
+ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
+ MasqueradeAll: true,
+ SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
+ },
+ Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
+ MaxPerCore: pointer.Int32(1),
+ Min: pointer.Int32(1),
+ TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ },
+ DetectLocalMode: kubeproxyconfig.LocalModeInterfaceNamePrefix,
+ DetectLocal: kubeproxyconfig.DetectLocalConfiguration{
+ InterfaceNamePrefix: "vethabcde",
+ },
+ }, {
+ BindAddress: "10.10.12.11",
+ HealthzBindAddress: "0.0.0.0:12345",
+ MetricsBindAddress: "127.0.0.1:10249",
+ ClusterCIDR: "192.168.59.0/24",
+ ConfigSyncPeriod: metav1.Duration{Duration: 1 * time.Second},
+ IPTables: kubeproxyconfig.KubeProxyIPTablesConfiguration{
+ MasqueradeAll: true,
+ SyncPeriod: metav1.Duration{Duration: 5 * time.Second},
+ MinSyncPeriod: metav1.Duration{Duration: 2 * time.Second},
+ },
+ Conntrack: kubeproxyconfig.KubeProxyConntrackConfiguration{
+ MaxPerCore: pointer.Int32(1),
+ Min: pointer.Int32(1),
+ TCPEstablishedTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
+ },
+ DetectLocalMode: kubeproxyconfig.LocalModeBridgeInterface,
+ DetectLocal: kubeproxyconfig.DetectLocalConfiguration{
+ BridgeInterface: "avz",
+ },
+ }}
for _, successCase := range successCases {
if errs := Validate(&successCase); len(errs) != 0 {
diff --git a/pkg/scheduler/apis/config/validation/validation_test.go b/pkg/scheduler/apis/config/validation/validation_test.go
index ddb9a9ccc91..029c6985c74 100644
--- a/pkg/scheduler/apis/config/validation/validation_test.go
+++ b/pkg/scheduler/apis/config/validation/validation_test.go
@@ -57,42 +57,35 @@ func TestValidateKubeSchedulerConfigurationV1beta2(t *testing.T) {
PodInitialBackoffSeconds: podInitialBackoffSeconds,
PodMaxBackoffSeconds: podMaxBackoffSeconds,
PercentageOfNodesToScore: pointer.Int32(35),
- Profiles: []config.KubeSchedulerProfile{
- {
- SchedulerName: "me",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomSort"}},
- },
- Score: config.PluginSet{
- Disabled: []config.Plugin{{Name: "*"}},
- },
+ Profiles: []config.KubeSchedulerProfile{{
+ SchedulerName: "me",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomSort"}},
},
- PluginConfig: []config.PluginConfig{
- {
- Name: "DefaultPreemption",
- Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 10, MinCandidateNodesAbsolute: 100},
- },
+ Score: config.PluginSet{
+ Disabled: []config.Plugin{{Name: "*"}},
},
},
- {
- SchedulerName: "other",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomSort"}},
- },
- Bind: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomBind"}},
- },
+ PluginConfig: []config.PluginConfig{{
+ Name: "DefaultPreemption",
+ Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 10, MinCandidateNodesAbsolute: 100},
+ }},
+ }, {
+ SchedulerName: "other",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomSort"}},
+ },
+ Bind: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomBind"}},
},
},
- },
- Extenders: []config.Extender{
- {
- PrioritizeVerb: "prioritize",
- Weight: 1,
- },
- },
+ }},
+ Extenders: []config.Extender{{
+ PrioritizeVerb: "prioritize",
+ Weight: 1,
+ }},
}
invalidParallelismValue := validConfig.DeepCopy()
@@ -145,60 +138,46 @@ func TestValidateKubeSchedulerConfigurationV1beta2(t *testing.T) {
extenderNegativeWeight.Extenders[0].Weight = -1
invalidNodePercentage := validConfig.DeepCopy()
- invalidNodePercentage.Profiles[0].PluginConfig = []config.PluginConfig{
- {
- Name: "DefaultPreemption",
- Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 200, MinCandidateNodesAbsolute: 100},
- },
- }
+ invalidNodePercentage.Profiles[0].PluginConfig = []config.PluginConfig{{
+ Name: "DefaultPreemption",
+ Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 200, MinCandidateNodesAbsolute: 100},
+ }}
invalidPluginArgs := validConfig.DeepCopy()
- invalidPluginArgs.Profiles[0].PluginConfig = []config.PluginConfig{
- {
- Name: "DefaultPreemption",
- Args: &config.InterPodAffinityArgs{},
- },
- }
+ invalidPluginArgs.Profiles[0].PluginConfig = []config.PluginConfig{{
+ Name: "DefaultPreemption",
+ Args: &config.InterPodAffinityArgs{},
+ }}
duplicatedPluginConfig := validConfig.DeepCopy()
- duplicatedPluginConfig.Profiles[0].PluginConfig = []config.PluginConfig{
- {
- Name: "config",
- },
- {
- Name: "config",
- },
- }
+ duplicatedPluginConfig.Profiles[0].PluginConfig = []config.PluginConfig{{
+ Name: "config",
+ }, {
+ Name: "config",
+ }}
mismatchQueueSort := validConfig.DeepCopy()
- mismatchQueueSort.Profiles = []config.KubeSchedulerProfile{
- {
- SchedulerName: "me",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "PrioritySort"}},
- },
- },
- PluginConfig: []config.PluginConfig{
- {
- Name: "PrioritySort",
- },
+ mismatchQueueSort.Profiles = []config.KubeSchedulerProfile{{
+ SchedulerName: "me",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "PrioritySort"}},
},
},
- {
- SchedulerName: "other",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomSort"}},
- },
- },
- PluginConfig: []config.PluginConfig{
- {
- Name: "CustomSort",
- },
+ PluginConfig: []config.PluginConfig{{
+ Name: "PrioritySort",
+ }},
+ }, {
+ SchedulerName: "other",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomSort"}},
},
},
- }
+ PluginConfig: []config.PluginConfig{{
+ Name: "CustomSort",
+ }},
+ }}
extenderDuplicateManagedResource := validConfig.DeepCopy()
extenderDuplicateManagedResource.Extenders[0].ManagedResources = []config.ExtenderManagedResource{
@@ -455,42 +434,35 @@ func TestValidateKubeSchedulerConfigurationV1beta3(t *testing.T) {
PodInitialBackoffSeconds: podInitialBackoffSeconds,
PodMaxBackoffSeconds: podMaxBackoffSeconds,
PercentageOfNodesToScore: pointer.Int32(35),
- Profiles: []config.KubeSchedulerProfile{
- {
- SchedulerName: "me",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomSort"}},
- },
- Score: config.PluginSet{
- Disabled: []config.Plugin{{Name: "*"}},
- },
+ Profiles: []config.KubeSchedulerProfile{{
+ SchedulerName: "me",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomSort"}},
},
- PluginConfig: []config.PluginConfig{
- {
- Name: "DefaultPreemption",
- Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 10, MinCandidateNodesAbsolute: 100},
- },
+ Score: config.PluginSet{
+ Disabled: []config.Plugin{{Name: "*"}},
},
},
- {
- SchedulerName: "other",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomSort"}},
- },
- Bind: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomBind"}},
- },
+ PluginConfig: []config.PluginConfig{{
+ Name: "DefaultPreemption",
+ Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 10, MinCandidateNodesAbsolute: 100},
+ }},
+ }, {
+ SchedulerName: "other",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomSort"}},
+ },
+ Bind: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomBind"}},
},
},
- },
- Extenders: []config.Extender{
- {
- PrioritizeVerb: "prioritize",
- Weight: 1,
- },
- },
+ }},
+ Extenders: []config.Extender{{
+ PrioritizeVerb: "prioritize",
+ Weight: 1,
+ }},
}
invalidParallelismValue := validConfig.DeepCopy()
@@ -543,20 +515,16 @@ func TestValidateKubeSchedulerConfigurationV1beta3(t *testing.T) {
extenderNegativeWeight.Extenders[0].Weight = -1
invalidNodePercentage := validConfig.DeepCopy()
- invalidNodePercentage.Profiles[0].PluginConfig = []config.PluginConfig{
- {
- Name: "DefaultPreemption",
- Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 200, MinCandidateNodesAbsolute: 100},
- },
- }
+ invalidNodePercentage.Profiles[0].PluginConfig = []config.PluginConfig{{
+ Name: "DefaultPreemption",
+ Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 200, MinCandidateNodesAbsolute: 100},
+ }}
invalidPluginArgs := validConfig.DeepCopy()
- invalidPluginArgs.Profiles[0].PluginConfig = []config.PluginConfig{
- {
- Name: "DefaultPreemption",
- Args: &config.InterPodAffinityArgs{},
- },
- }
+ invalidPluginArgs.Profiles[0].PluginConfig = []config.PluginConfig{{
+ Name: "DefaultPreemption",
+ Args: &config.InterPodAffinityArgs{},
+ }}
duplicatedPlugins := validConfig.DeepCopy()
duplicatedPlugins.Profiles[0].Plugins.PreEnqueue.Enabled = []config.Plugin{
@@ -565,44 +533,34 @@ func TestValidateKubeSchedulerConfigurationV1beta3(t *testing.T) {
}
duplicatedPluginConfig := validConfig.DeepCopy()
- duplicatedPluginConfig.Profiles[0].PluginConfig = []config.PluginConfig{
- {
- Name: "config",
- },
- {
- Name: "config",
- },
- }
+ duplicatedPluginConfig.Profiles[0].PluginConfig = []config.PluginConfig{{
+ Name: "config",
+ }, {
+ Name: "config",
+ }}
mismatchQueueSort := validConfig.DeepCopy()
- mismatchQueueSort.Profiles = []config.KubeSchedulerProfile{
- {
- SchedulerName: "me",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "PrioritySort"}},
- },
- },
- PluginConfig: []config.PluginConfig{
- {
- Name: "PrioritySort",
- },
+ mismatchQueueSort.Profiles = []config.KubeSchedulerProfile{{
+ SchedulerName: "me",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "PrioritySort"}},
},
},
- {
- SchedulerName: "other",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomSort"}},
- },
- },
- PluginConfig: []config.PluginConfig{
- {
- Name: "CustomSort",
- },
+ PluginConfig: []config.PluginConfig{{
+ Name: "PrioritySort",
+ }},
+ }, {
+ SchedulerName: "other",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomSort"}},
},
},
- }
+ PluginConfig: []config.PluginConfig{{
+ Name: "CustomSort",
+ }},
+ }}
extenderDuplicateManagedResource := validConfig.DeepCopy()
extenderDuplicateManagedResource.Extenders[0].ManagedResources = []config.ExtenderManagedResource{
@@ -858,44 +816,37 @@ func TestValidateKubeSchedulerConfigurationV1(t *testing.T) {
},
PodInitialBackoffSeconds: podInitialBackoffSeconds,
PodMaxBackoffSeconds: podMaxBackoffSeconds,
- Profiles: []config.KubeSchedulerProfile{
- {
- SchedulerName: "me",
- PercentageOfNodesToScore: pointer.Int32(35),
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomSort"}},
- },
- Score: config.PluginSet{
- Disabled: []config.Plugin{{Name: "*"}},
- },
+ Profiles: []config.KubeSchedulerProfile{{
+ SchedulerName: "me",
+ PercentageOfNodesToScore: pointer.Int32(35),
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomSort"}},
},
- PluginConfig: []config.PluginConfig{
- {
- Name: "DefaultPreemption",
- Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 10, MinCandidateNodesAbsolute: 100},
- },
+ Score: config.PluginSet{
+ Disabled: []config.Plugin{{Name: "*"}},
},
},
- {
- SchedulerName: "other",
- PercentageOfNodesToScore: pointer.Int32(35),
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomSort"}},
- },
- Bind: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomBind"}},
- },
+ PluginConfig: []config.PluginConfig{{
+ Name: "DefaultPreemption",
+ Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 10, MinCandidateNodesAbsolute: 100},
+ }},
+ }, {
+ SchedulerName: "other",
+ PercentageOfNodesToScore: pointer.Int32(35),
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomSort"}},
+ },
+ Bind: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomBind"}},
},
},
- },
- Extenders: []config.Extender{
- {
- PrioritizeVerb: "prioritize",
- Weight: 1,
- },
- },
+ }},
+ Extenders: []config.Extender{{
+ PrioritizeVerb: "prioritize",
+ Weight: 1,
+ }},
}
invalidParallelismValue := validConfig.DeepCopy()
@@ -948,60 +899,46 @@ func TestValidateKubeSchedulerConfigurationV1(t *testing.T) {
extenderNegativeWeight.Extenders[0].Weight = -1
invalidNodePercentage := validConfig.DeepCopy()
- invalidNodePercentage.Profiles[0].PluginConfig = []config.PluginConfig{
- {
- Name: "DefaultPreemption",
- Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 200, MinCandidateNodesAbsolute: 100},
- },
- }
+ invalidNodePercentage.Profiles[0].PluginConfig = []config.PluginConfig{{
+ Name: "DefaultPreemption",
+ Args: &config.DefaultPreemptionArgs{MinCandidateNodesPercentage: 200, MinCandidateNodesAbsolute: 100},
+ }}
invalidPluginArgs := validConfig.DeepCopy()
- invalidPluginArgs.Profiles[0].PluginConfig = []config.PluginConfig{
- {
- Name: "DefaultPreemption",
- Args: &config.InterPodAffinityArgs{},
- },
- }
+ invalidPluginArgs.Profiles[0].PluginConfig = []config.PluginConfig{{
+ Name: "DefaultPreemption",
+ Args: &config.InterPodAffinityArgs{},
+ }}
duplicatedPluginConfig := validConfig.DeepCopy()
- duplicatedPluginConfig.Profiles[0].PluginConfig = []config.PluginConfig{
- {
- Name: "config",
- },
- {
- Name: "config",
- },
- }
+ duplicatedPluginConfig.Profiles[0].PluginConfig = []config.PluginConfig{{
+ Name: "config",
+ }, {
+ Name: "config",
+ }}
mismatchQueueSort := validConfig.DeepCopy()
- mismatchQueueSort.Profiles = []config.KubeSchedulerProfile{
- {
- SchedulerName: "me",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "PrioritySort"}},
- },
- },
- PluginConfig: []config.PluginConfig{
- {
- Name: "PrioritySort",
- },
+ mismatchQueueSort.Profiles = []config.KubeSchedulerProfile{{
+ SchedulerName: "me",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "PrioritySort"}},
},
},
- {
- SchedulerName: "other",
- Plugins: &config.Plugins{
- QueueSort: config.PluginSet{
- Enabled: []config.Plugin{{Name: "CustomSort"}},
- },
- },
- PluginConfig: []config.PluginConfig{
- {
- Name: "CustomSort",
- },
+ PluginConfig: []config.PluginConfig{{
+ Name: "PrioritySort",
+ }},
+ }, {
+ SchedulerName: "other",
+ Plugins: &config.Plugins{
+ QueueSort: config.PluginSet{
+ Enabled: []config.Plugin{{Name: "CustomSort"}},
},
},
- }
+ PluginConfig: []config.PluginConfig{{
+ Name: "CustomSort",
+ }},
+ }}
extenderDuplicateManagedResource := validConfig.DeepCopy()
extenderDuplicateManagedResource.Extenders[0].ManagedResources = []config.ExtenderManagedResource{
diff --git a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation_test.go b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation_test.go
index 03415d0449d..ef161730fe8 100644
--- a/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation_test.go
+++ b/plugin/pkg/admission/eventratelimit/apis/eventratelimit/validation/validation_test.go
@@ -27,158 +27,124 @@ func TestValidateConfiguration(t *testing.T) {
name string
config eventratelimitapi.Configuration
expectedResult bool
- }{
- {
- name: "valid server",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Type: "Server",
- Burst: 5,
- QPS: 1,
- },
- },
- },
- expectedResult: true,
+ }{{
+ name: "valid server",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Type: "Server",
+ Burst: 5,
+ QPS: 1,
+ }},
},
- {
- name: "valid namespace",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Type: "Namespace",
- Burst: 10,
- QPS: 2,
- CacheSize: 100,
- },
- },
- },
- expectedResult: true,
+ expectedResult: true,
+ }, {
+ name: "valid namespace",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Type: "Namespace",
+ Burst: 10,
+ QPS: 2,
+ CacheSize: 100,
+ }},
},
- {
- name: "valid user",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Type: "User",
- Burst: 10,
- QPS: 2,
- CacheSize: 100,
- },
- },
- },
- expectedResult: true,
+ expectedResult: true,
+ }, {
+ name: "valid user",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Type: "User",
+ Burst: 10,
+ QPS: 2,
+ CacheSize: 100,
+ }},
},
- {
- name: "valid source+object",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Type: "SourceAndObject",
- Burst: 5,
- QPS: 1,
- CacheSize: 1000,
- },
- },
- },
- expectedResult: true,
+ expectedResult: true,
+ }, {
+ name: "valid source+object",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Type: "SourceAndObject",
+ Burst: 5,
+ QPS: 1,
+ CacheSize: 1000,
+ }},
},
- {
- name: "valid multiple",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Type: "Server",
- Burst: 5,
- QPS: 1,
- },
- {
- Type: "Namespace",
- Burst: 10,
- QPS: 2,
- CacheSize: 100,
- },
- {
- Type: "SourceAndObject",
- Burst: 25,
- QPS: 10,
- CacheSize: 1000,
- },
- },
- },
- expectedResult: true,
+ expectedResult: true,
+ }, {
+ name: "valid multiple",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Type: "Server",
+ Burst: 5,
+ QPS: 1,
+ }, {
+ Type: "Namespace",
+ Burst: 10,
+ QPS: 2,
+ CacheSize: 100,
+ }, {
+ Type: "SourceAndObject",
+ Burst: 25,
+ QPS: 10,
+ CacheSize: 1000,
+ }},
},
- {
- name: "missing limits",
- config: eventratelimitapi.Configuration{},
- expectedResult: false,
+ expectedResult: true,
+ }, {
+ name: "missing limits",
+ config: eventratelimitapi.Configuration{},
+ expectedResult: false,
+ }, {
+ name: "missing type",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Burst: 25,
+ QPS: 10,
+ CacheSize: 1000,
+ }},
},
- {
- name: "missing type",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Burst: 25,
- QPS: 10,
- CacheSize: 1000,
- },
- },
- },
- expectedResult: false,
+ expectedResult: false,
+ }, {
+ name: "invalid type",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Type: "unknown-type",
+ Burst: 25,
+ QPS: 10,
+ CacheSize: 1000,
+ }},
},
- {
- name: "invalid type",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Type: "unknown-type",
- Burst: 25,
- QPS: 10,
- CacheSize: 1000,
- },
- },
- },
- expectedResult: false,
+ expectedResult: false,
+ }, {
+ name: "missing burst",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Type: "Server",
+ QPS: 1,
+ }},
},
- {
- name: "missing burst",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Type: "Server",
- QPS: 1,
- },
- },
- },
- expectedResult: false,
+ expectedResult: false,
+ }, {
+ name: "missing qps",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Type: "Server",
+ Burst: 5,
+ }},
},
- {
- name: "missing qps",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Type: "Server",
- Burst: 5,
- },
- },
- },
- expectedResult: false,
+ expectedResult: false,
+ }, {
+ name: "negative cache size",
+ config: eventratelimitapi.Configuration{
+ Limits: []eventratelimitapi.Limit{{
+ Type: "Namespace",
+ Burst: 10,
+ QPS: 2,
+ CacheSize: -1,
+ }},
},
- {
- name: "negative cache size",
- config: eventratelimitapi.Configuration{
- Limits: []eventratelimitapi.Limit{
- {
- Type: "Namespace",
- Burst: 10,
- QPS: 2,
- CacheSize: -1,
- },
- },
- },
- expectedResult: false,
- },
- }
+ expectedResult: false,
+ }}
for _, tc := range cases {
errs := ValidateConfiguration(&tc.config)
if e, a := tc.expectedResult, len(errs) == 0; e != a {
diff --git a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation_test.go b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation_test.go
index d444921a51a..44a07f145a0 100644
--- a/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation_test.go
+++ b/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/validation/validation_test.go
@@ -17,9 +17,10 @@ limitations under the License.
package validation
import (
+ "testing"
+
api "k8s.io/kubernetes/pkg/apis/core"
internalapi "k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction"
- "testing"
)
func TestValidateConfiguration(t *testing.T) {
@@ -28,38 +29,34 @@ func TestValidateConfiguration(t *testing.T) {
config internalapi.Configuration
testName string
testStatus bool
- }{
- {
- config: internalapi.Configuration{
- Default: []api.Toleration{
- {Key: "foo", Operator: "Exists", Value: "", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]},
- {Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]},
- {Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"},
- {Operator: "Exists", Effect: "NoSchedule"},
- },
- Whitelist: []api.Toleration{
- {Key: "foo", Value: "bar", Effect: "NoSchedule"},
- {Key: "foo", Operator: "Equal", Value: "bar"},
- },
+ }{{
+ config: internalapi.Configuration{
+ Default: []api.Toleration{
+ {Key: "foo", Operator: "Exists", Value: "", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]},
+ {Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoExecute", TolerationSeconds: &[]int64{60}[0]},
+ {Key: "foo", Operator: "Equal", Value: "bar", Effect: "NoSchedule"},
+ {Operator: "Exists", Effect: "NoSchedule"},
},
- testName: "Valid cases",
- testStatus: true,
- },
- {
- config: internalapi.Configuration{
- Whitelist: []api.Toleration{{Key: "foo", Operator: "Exists", Value: "bar", Effect: "NoSchedule"}},
+ Whitelist: []api.Toleration{
+ {Key: "foo", Value: "bar", Effect: "NoSchedule"},
+ {Key: "foo", Operator: "Equal", Value: "bar"},
},
- testName: "Invalid case",
- testStatus: false,
},
- {
- config: internalapi.Configuration{
- Default: []api.Toleration{{Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
- },
- testName: "Invalid case",
- testStatus: false,
+ testName: "Valid cases",
+ testStatus: true,
+ }, {
+ config: internalapi.Configuration{
+ Whitelist: []api.Toleration{{Key: "foo", Operator: "Exists", Value: "bar", Effect: "NoSchedule"}},
},
- }
+ testName: "Invalid case",
+ testStatus: false,
+ }, {
+ config: internalapi.Configuration{
+ Default: []api.Toleration{{Operator: "Equal", Value: "bar", Effect: "NoSchedule"}},
+ },
+ testName: "Invalid case",
+ testStatus: false,
+ }}
for i := range tests {
errs := ValidateConfiguration(&tests[i].config)
diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation_test.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation_test.go
index d6da2298882..1a32143817b 100644
--- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation_test.go
+++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/validation/validation_test.go
@@ -17,9 +17,10 @@ limitations under the License.
package validation
import (
+ "testing"
+
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "testing"
)
func TestValidateListOptions(t *testing.T) {
@@ -32,164 +33,145 @@ func TestValidateListOptions(t *testing.T) {
opts internalversion.ListOptions
watchListFeatureEnabled bool
expectErrors []string
- }{
- {
- name: "valid-default",
- opts: internalversion.ListOptions{},
+ }{{
+ name: "valid-default",
+ opts: internalversion.ListOptions{},
+ }, {
+ name: "valid-resourceversionmatch-exact",
+ opts: internalversion.ListOptions{
+ ResourceVersion: "1",
+ ResourceVersionMatch: metav1.ResourceVersionMatchExact,
},
- {
- name: "valid-resourceversionmatch-exact",
- opts: internalversion.ListOptions{
- ResourceVersion: "1",
- ResourceVersionMatch: metav1.ResourceVersionMatchExact,
- },
+ }, {
+ name: "invalid-resourceversionmatch-exact",
+ opts: internalversion.ListOptions{
+ ResourceVersion: "0",
+ ResourceVersionMatch: metav1.ResourceVersionMatchExact,
},
- {
- name: "invalid-resourceversionmatch-exact",
- opts: internalversion.ListOptions{
- ResourceVersion: "0",
- ResourceVersionMatch: metav1.ResourceVersionMatchExact,
- },
- expectErrors: []string{"resourceVersionMatch: Forbidden: resourceVersionMatch \"exact\" is forbidden for resourceVersion \"0\""},
+ expectErrors: []string{"resourceVersionMatch: Forbidden: resourceVersionMatch \"exact\" is forbidden for resourceVersion \"0\""},
+ }, {
+ name: "valid-resourceversionmatch-notolderthan",
+ opts: internalversion.ListOptions{
+ ResourceVersion: "0",
+ ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
},
- {
- name: "valid-resourceversionmatch-notolderthan",
- opts: internalversion.ListOptions{
- ResourceVersion: "0",
- ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
- },
+ }, {
+ name: "invalid-resourceversionmatch",
+ opts: internalversion.ListOptions{
+ ResourceVersion: "0",
+ ResourceVersionMatch: "foo",
},
- {
- name: "invalid-resourceversionmatch",
- opts: internalversion.ListOptions{
- ResourceVersion: "0",
- ResourceVersionMatch: "foo",
- },
- expectErrors: []string{"resourceVersionMatch: Unsupported value: \"foo\": supported values: \"Exact\", \"NotOlderThan\", \"\""},
+ expectErrors: []string{"resourceVersionMatch: Unsupported value: \"foo\": supported values: \"Exact\", \"NotOlderThan\", \"\""},
+ }, {
+ name: "list-sendInitialEvents-forbidden",
+ opts: internalversion.ListOptions{
+ SendInitialEvents: boolPtrFn(true),
},
- {
- name: "list-sendInitialEvents-forbidden",
- opts: internalversion.ListOptions{
- SendInitialEvents: boolPtrFn(true),
- },
- expectErrors: []string{"sendInitialEvents: Forbidden: sendInitialEvents is forbidden for list"},
+ expectErrors: []string{"sendInitialEvents: Forbidden: sendInitialEvents is forbidden for list"},
+ }, {
+ name: "valid-watch-default",
+ opts: internalversion.ListOptions{
+ Watch: true,
},
- {
- name: "valid-watch-default",
- opts: internalversion.ListOptions{
- Watch: true,
- },
+ }, {
+ name: "valid-watch-sendInitialEvents-on",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(true),
+ ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
+ AllowWatchBookmarks: true,
},
- {
- name: "valid-watch-sendInitialEvents-on",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(true),
- ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
- AllowWatchBookmarks: true,
- },
- watchListFeatureEnabled: true,
+ watchListFeatureEnabled: true,
+ }, {
+ name: "valid-watch-sendInitialEvents-off",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(false),
+ ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
+ AllowWatchBookmarks: true,
},
- {
- name: "valid-watch-sendInitialEvents-off",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(false),
- ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
- AllowWatchBookmarks: true,
- },
- watchListFeatureEnabled: true,
+ watchListFeatureEnabled: true,
+ }, {
+ name: "watch-resourceversionmatch-without-sendInitialEvents-forbidden",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
},
- {
- name: "watch-resourceversionmatch-without-sendInitialEvents-forbidden",
- opts: internalversion.ListOptions{
- Watch: true,
- ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
- },
- expectErrors: []string{"resourceVersionMatch: Forbidden: resourceVersionMatch is forbidden for watch unless sendInitialEvents is provided"},
+ expectErrors: []string{"resourceVersionMatch: Forbidden: resourceVersionMatch is forbidden for watch unless sendInitialEvents is provided"},
+ }, {
+ name: "watch-sendInitialEvents-without-resourceversionmatch-forbidden",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(true),
},
- {
- name: "watch-sendInitialEvents-without-resourceversionmatch-forbidden",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(true),
- },
- expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "sendInitialEvents: Forbidden: sendInitialEvents is forbidden for watch unless the WatchList feature gate is enabled"},
+ expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "sendInitialEvents: Forbidden: sendInitialEvents is forbidden for watch unless the WatchList feature gate is enabled"},
+ }, {
+ name: "watch-sendInitialEvents-with-exact-resourceversionmatch-forbidden",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(true),
+ ResourceVersionMatch: metav1.ResourceVersionMatchExact,
+ AllowWatchBookmarks: true,
},
- {
- name: "watch-sendInitialEvents-with-exact-resourceversionmatch-forbidden",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(true),
- ResourceVersionMatch: metav1.ResourceVersionMatchExact,
- AllowWatchBookmarks: true,
- },
- watchListFeatureEnabled: true,
- expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "resourceVersionMatch: Unsupported value: \"Exact\": supported values: \"NotOlderThan\""},
+ watchListFeatureEnabled: true,
+ expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "resourceVersionMatch: Unsupported value: \"Exact\": supported values: \"NotOlderThan\""},
+ }, {
+ name: "watch-sendInitialEvents-on-with-empty-resourceversionmatch-forbidden",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(true),
+ ResourceVersionMatch: "",
},
- {
- name: "watch-sendInitialEvents-on-with-empty-resourceversionmatch-forbidden",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(true),
- ResourceVersionMatch: "",
- },
- expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "sendInitialEvents: Forbidden: sendInitialEvents is forbidden for watch unless the WatchList feature gate is enabled"},
+ expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "sendInitialEvents: Forbidden: sendInitialEvents is forbidden for watch unless the WatchList feature gate is enabled"},
+ }, {
+ name: "watch-sendInitialEvents-off-with-empty-resourceversionmatch-forbidden",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(false),
+ ResourceVersionMatch: "",
},
- {
- name: "watch-sendInitialEvents-off-with-empty-resourceversionmatch-forbidden",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(false),
- ResourceVersionMatch: "",
- },
- expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "sendInitialEvents: Forbidden: sendInitialEvents is forbidden for watch unless the WatchList feature gate is enabled"},
+ expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "sendInitialEvents: Forbidden: sendInitialEvents is forbidden for watch unless the WatchList feature gate is enabled"},
+ }, {
+ name: "watch-sendInitialEvents-with-incorrect-resourceversionmatch-forbidden",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(true),
+ ResourceVersionMatch: "incorrect",
+ AllowWatchBookmarks: true,
},
- {
- name: "watch-sendInitialEvents-with-incorrect-resourceversionmatch-forbidden",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(true),
- ResourceVersionMatch: "incorrect",
- AllowWatchBookmarks: true,
- },
- watchListFeatureEnabled: true,
- expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "resourceVersionMatch: Unsupported value: \"incorrect\": supported values: \"NotOlderThan\""},
+ watchListFeatureEnabled: true,
+ expectErrors: []string{"resourceVersionMatch: Forbidden: sendInitialEvents requires setting resourceVersionMatch to NotOlderThan", "resourceVersionMatch: Unsupported value: \"incorrect\": supported values: \"NotOlderThan\""},
+ }, {
+ // note that validating allowWatchBookmarks would break backward compatibility
+ // because it was possible to request initial events via resourceVersion=0 before this change
+ name: "watch-sendInitialEvents-no-allowWatchBookmark",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(true),
+ ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
},
- {
- // note that validating allowWatchBookmarks would break backward compatibility
- // because it was possible to request initial events via resourceVersion=0 before this change
- name: "watch-sendInitialEvents-no-allowWatchBookmark",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(true),
- ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
- },
- watchListFeatureEnabled: true,
+ watchListFeatureEnabled: true,
+ }, {
+ name: "watch-sendInitialEvents-no-watchlist-fg-disabled",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(true),
+ ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
+ AllowWatchBookmarks: true,
},
- {
- name: "watch-sendInitialEvents-no-watchlist-fg-disabled",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(true),
- ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
- AllowWatchBookmarks: true,
- },
- expectErrors: []string{"sendInitialEvents: Forbidden: sendInitialEvents is forbidden for watch unless the WatchList feature gate is enabled"},
+ expectErrors: []string{"sendInitialEvents: Forbidden: sendInitialEvents is forbidden for watch unless the WatchList feature gate is enabled"},
+ }, {
+ name: "watch-sendInitialEvents-no-watchlist-fg-disabled",
+ opts: internalversion.ListOptions{
+ Watch: true,
+ SendInitialEvents: boolPtrFn(true),
+ ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
+ AllowWatchBookmarks: true,
+ Continue: "123",
},
- {
- name: "watch-sendInitialEvents-no-watchlist-fg-disabled",
- opts: internalversion.ListOptions{
- Watch: true,
- SendInitialEvents: boolPtrFn(true),
- ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan,
- AllowWatchBookmarks: true,
- Continue: "123",
- },
- watchListFeatureEnabled: true,
- expectErrors: []string{"resourceVersionMatch: Forbidden: resourceVersionMatch is forbidden when continue is provided"},
- },
- }
+ watchListFeatureEnabled: true,
+ expectErrors: []string{"resourceVersionMatch: Forbidden: resourceVersionMatch is forbidden when continue is provided"},
+ }}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation_test.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation_test.go
index 60b0cd3dbad..e9c6c161620 100644
--- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation_test.go
+++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation_test.go
@@ -136,31 +136,26 @@ func TestValidPatchOptions(t *testing.T) {
tests := []struct {
opts metav1.PatchOptions
patchType types.PatchType
- }{
- {
- opts: metav1.PatchOptions{
- Force: boolPtr(true),
- FieldManager: "kubectl",
- },
- patchType: types.ApplyPatchType,
+ }{{
+ opts: metav1.PatchOptions{
+ Force: boolPtr(true),
+ FieldManager: "kubectl",
},
- {
- opts: metav1.PatchOptions{
- FieldManager: "kubectl",
- },
- patchType: types.ApplyPatchType,
+ patchType: types.ApplyPatchType,
+ }, {
+ opts: metav1.PatchOptions{
+ FieldManager: "kubectl",
},
- {
- opts: metav1.PatchOptions{},
- patchType: types.MergePatchType,
+ patchType: types.ApplyPatchType,
+ }, {
+ opts: metav1.PatchOptions{},
+ patchType: types.MergePatchType,
+ }, {
+ opts: metav1.PatchOptions{
+ FieldManager: "patcher",
},
- {
- opts: metav1.PatchOptions{
- FieldManager: "patcher",
- },
- patchType: types.MergePatchType,
- },
- }
+ patchType: types.MergePatchType,
+ }}
for _, test := range tests {
t.Run(fmt.Sprintf("%v", test.opts), func(t *testing.T) {
@@ -243,36 +238,30 @@ func TestValidateFieldManagerInvalid(t *testing.T) {
}
func TestValidateManagedFieldsInvalid(t *testing.T) {
- tests := []metav1.ManagedFieldsEntry{
- {
- Operation: metav1.ManagedFieldsOperationUpdate,
- FieldsType: "RandomVersion",
- APIVersion: "v1",
- },
- {
- Operation: "RandomOperation",
- FieldsType: "FieldsV1",
- APIVersion: "v1",
- },
- {
- // Operation is missing
- FieldsType: "FieldsV1",
- APIVersion: "v1",
- },
- {
- Operation: metav1.ManagedFieldsOperationUpdate,
- FieldsType: "FieldsV1",
- // Invalid fieldManager
- Manager: "field\nmanager",
- APIVersion: "v1",
- },
- {
- Operation: metav1.ManagedFieldsOperationApply,
- FieldsType: "FieldsV1",
- APIVersion: "v1",
- Subresource: "TooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLong",
- },
- }
+ tests := []metav1.ManagedFieldsEntry{{
+ Operation: metav1.ManagedFieldsOperationUpdate,
+ FieldsType: "RandomVersion",
+ APIVersion: "v1",
+ }, {
+ Operation: "RandomOperation",
+ FieldsType: "FieldsV1",
+ APIVersion: "v1",
+ }, {
+ // Operation is missing
+ FieldsType: "FieldsV1",
+ APIVersion: "v1",
+ }, {
+ Operation: metav1.ManagedFieldsOperationUpdate,
+ FieldsType: "FieldsV1",
+ // Invalid fieldManager
+ Manager: "field\nmanager",
+ APIVersion: "v1",
+ }, {
+ Operation: metav1.ManagedFieldsOperationApply,
+ FieldsType: "FieldsV1",
+ APIVersion: "v1",
+ Subresource: "TooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLongTooLong",
+ }}
for _, test := range tests {
t.Run(fmt.Sprintf("%#v", test), func(t *testing.T) {
@@ -285,30 +274,25 @@ func TestValidateManagedFieldsInvalid(t *testing.T) {
}
func TestValidateMangedFieldsValid(t *testing.T) {
- tests := []metav1.ManagedFieldsEntry{
- {
- Operation: metav1.ManagedFieldsOperationUpdate,
- APIVersion: "v1",
- // FieldsType is missing
- },
- {
- Operation: metav1.ManagedFieldsOperationUpdate,
- FieldsType: "FieldsV1",
- APIVersion: "v1",
- },
- {
- Operation: metav1.ManagedFieldsOperationApply,
- FieldsType: "FieldsV1",
- APIVersion: "v1",
- Subresource: "scale",
- },
- {
- Operation: metav1.ManagedFieldsOperationApply,
- FieldsType: "FieldsV1",
- APIVersion: "v1",
- Manager: "🍔",
- },
- }
+ tests := []metav1.ManagedFieldsEntry{{
+ Operation: metav1.ManagedFieldsOperationUpdate,
+ APIVersion: "v1",
+ // FieldsType is missing
+ }, {
+ Operation: metav1.ManagedFieldsOperationUpdate,
+ FieldsType: "FieldsV1",
+ APIVersion: "v1",
+ }, {
+ Operation: metav1.ManagedFieldsOperationApply,
+ FieldsType: "FieldsV1",
+ APIVersion: "v1",
+ Subresource: "scale",
+ }, {
+ Operation: metav1.ManagedFieldsOperationApply,
+ FieldsType: "FieldsV1",
+ APIVersion: "v1",
+ Manager: "🍔",
+ }}
for _, test := range tests {
t.Run(fmt.Sprintf("%#v", test), func(t *testing.T) {
@@ -325,99 +309,90 @@ func TestValidateConditions(t *testing.T) {
name string
conditions []metav1.Condition
validateErrs func(t *testing.T, errs field.ErrorList)
- }{
- {
- name: "bunch-of-invalid-fields",
- conditions: []metav1.Condition{{
- Type: ":invalid",
- Status: "unknown",
- ObservedGeneration: -1,
- LastTransitionTime: metav1.Time{},
- Reason: "invalid;val",
- Message: "",
- }},
- validateErrs: func(t *testing.T, errs field.ErrorList) {
- needle := `status.conditions[0].type: Invalid value: ":invalid": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`
- if !hasError(errs, needle) {
- t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
- }
- needle = `status.conditions[0].status: Unsupported value: "unknown": supported values: "False", "True", "Unknown"`
- if !hasError(errs, needle) {
- t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
- }
- needle = `status.conditions[0].observedGeneration: Invalid value: -1: must be greater than or equal to zero`
- if !hasError(errs, needle) {
- t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
- }
- needle = `status.conditions[0].lastTransitionTime: Required value: must be set`
- if !hasError(errs, needle) {
- t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
- }
- needle = `status.conditions[0].reason: Invalid value: "invalid;val": a condition reason must start with alphabetic character, optionally followed by a string of alphanumeric characters or '_,:', and must end with an alphanumeric character or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', or 'ReasonA,ReasonB', or 'ReasonA:ReasonB', regex used for validation is '[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?')`
- if !hasError(errs, needle) {
- t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
- }
- },
+ }{{
+ name: "bunch-of-invalid-fields",
+ conditions: []metav1.Condition{{
+ Type: ":invalid",
+ Status: "unknown",
+ ObservedGeneration: -1,
+ LastTransitionTime: metav1.Time{},
+ Reason: "invalid;val",
+ Message: "",
+ }},
+ validateErrs: func(t *testing.T, errs field.ErrorList) {
+ needle := `status.conditions[0].type: Invalid value: ":invalid": name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')`
+ if !hasError(errs, needle) {
+ t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
+ }
+ needle = `status.conditions[0].status: Unsupported value: "unknown": supported values: "False", "True", "Unknown"`
+ if !hasError(errs, needle) {
+ t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
+ }
+ needle = `status.conditions[0].observedGeneration: Invalid value: -1: must be greater than or equal to zero`
+ if !hasError(errs, needle) {
+ t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
+ }
+ needle = `status.conditions[0].lastTransitionTime: Required value: must be set`
+ if !hasError(errs, needle) {
+ t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
+ }
+ needle = `status.conditions[0].reason: Invalid value: "invalid;val": a condition reason must start with alphabetic character, optionally followed by a string of alphanumeric characters or '_,:', and must end with an alphanumeric character or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', or 'ReasonA,ReasonB', or 'ReasonA:ReasonB', regex used for validation is '[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?')`
+ if !hasError(errs, needle) {
+ t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
+ }
},
- {
- name: "duplicates",
- conditions: []metav1.Condition{{
- Type: "First",
- },
- {
- Type: "Second",
- },
- {
- Type: "First",
- },
- },
- validateErrs: func(t *testing.T, errs field.ErrorList) {
- needle := `status.conditions[2].type: Duplicate value: "First"`
- if !hasError(errs, needle) {
- t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
- }
- },
+ }, {
+ name: "duplicates",
+ conditions: []metav1.Condition{{
+ Type: "First",
+ }, {
+ Type: "Second",
+ }, {
+ Type: "First",
+ }},
+ validateErrs: func(t *testing.T, errs field.ErrorList) {
+ needle := `status.conditions[2].type: Duplicate value: "First"`
+ if !hasError(errs, needle) {
+ t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
+ }
},
- {
- name: "colon-allowed-in-reason",
- conditions: []metav1.Condition{{
- Type: "First",
- Reason: "valid:val",
- }},
- validateErrs: func(t *testing.T, errs field.ErrorList) {
- needle := `status.conditions[0].reason`
- if hasPrefixError(errs, needle) {
- t.Errorf("has %q in\n%v", needle, errorsAsString(errs))
- }
- },
+ }, {
+ name: "colon-allowed-in-reason",
+ conditions: []metav1.Condition{{
+ Type: "First",
+ Reason: "valid:val",
+ }},
+ validateErrs: func(t *testing.T, errs field.ErrorList) {
+ needle := `status.conditions[0].reason`
+ if hasPrefixError(errs, needle) {
+ t.Errorf("has %q in\n%v", needle, errorsAsString(errs))
+ }
},
- {
- name: "comma-allowed-in-reason",
- conditions: []metav1.Condition{{
- Type: "First",
- Reason: "valid,val",
- }},
- validateErrs: func(t *testing.T, errs field.ErrorList) {
- needle := `status.conditions[0].reason`
- if hasPrefixError(errs, needle) {
- t.Errorf("has %q in\n%v", needle, errorsAsString(errs))
- }
- },
+ }, {
+ name: "comma-allowed-in-reason",
+ conditions: []metav1.Condition{{
+ Type: "First",
+ Reason: "valid,val",
+ }},
+ validateErrs: func(t *testing.T, errs field.ErrorList) {
+ needle := `status.conditions[0].reason`
+ if hasPrefixError(errs, needle) {
+ t.Errorf("has %q in\n%v", needle, errorsAsString(errs))
+ }
},
- {
- name: "reason-does-not-end-in-delimiter",
- conditions: []metav1.Condition{{
- Type: "First",
- Reason: "valid,val:",
- }},
- validateErrs: func(t *testing.T, errs field.ErrorList) {
- needle := `status.conditions[0].reason: Invalid value: "valid,val:": a condition reason must start with alphabetic character, optionally followed by a string of alphanumeric characters or '_,:', and must end with an alphanumeric character or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', or 'ReasonA,ReasonB', or 'ReasonA:ReasonB', regex used for validation is '[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?')`
- if !hasError(errs, needle) {
- t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
- }
- },
+ }, {
+ name: "reason-does-not-end-in-delimiter",
+ conditions: []metav1.Condition{{
+ Type: "First",
+ Reason: "valid,val:",
+ }},
+ validateErrs: func(t *testing.T, errs field.ErrorList) {
+ needle := `status.conditions[0].reason: Invalid value: "valid,val:": a condition reason must start with alphabetic character, optionally followed by a string of alphanumeric characters or '_,:', and must end with an alphanumeric character or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', or 'ReasonA,ReasonB', or 'ReasonA:ReasonB', regex used for validation is '[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?')`
+ if !hasError(errs, needle) {
+ t.Errorf("missing %q in\n%v", needle, errorsAsString(errs))
+ }
},
- }
+ }}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@@ -433,79 +408,66 @@ func TestLabelSelectorMatchExpression(t *testing.T) {
labelSelector *metav1.LabelSelector
wantErrorNumber int
validateErrs func(t *testing.T, errs field.ErrorList)
- }{
- {
- name: "Valid LabelSelector",
- labelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key",
- Operator: metav1.LabelSelectorOpIn,
- Values: []string{"value"},
- },
- },
- },
- wantErrorNumber: 0,
- validateErrs: nil,
+ }{{
+ name: "Valid LabelSelector",
+ labelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key",
+ Operator: metav1.LabelSelectorOpIn,
+ Values: []string{"value"},
+ }},
},
- {
- name: "MatchExpression's key name isn't valid",
- labelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "-key",
- Operator: metav1.LabelSelectorOpIn,
- Values: []string{"value"},
- },
- },
- },
- wantErrorNumber: 1,
- validateErrs: func(t *testing.T, errs field.ErrorList) {
- errMessage := "name part must consist of alphanumeric characters"
- if !partStringInErrorMessage(errs, errMessage) {
- t.Errorf("missing %q in\n%v", errMessage, errorsAsString(errs))
- }
- },
+ wantErrorNumber: 0,
+ validateErrs: nil,
+ }, {
+ name: "MatchExpression's key name isn't valid",
+ labelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "-key",
+ Operator: metav1.LabelSelectorOpIn,
+ Values: []string{"value"},
+ }},
},
- {
- name: "MatchExpression's operator isn't valid",
- labelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key",
- Operator: "abc",
- Values: []string{"value"},
- },
- },
- },
- wantErrorNumber: 1,
- validateErrs: func(t *testing.T, errs field.ErrorList) {
- errMessage := "not a valid selector operator"
- if !partStringInErrorMessage(errs, errMessage) {
- t.Errorf("missing %q in\n%v", errMessage, errorsAsString(errs))
- }
- },
+ wantErrorNumber: 1,
+ validateErrs: func(t *testing.T, errs field.ErrorList) {
+ errMessage := "name part must consist of alphanumeric characters"
+ if !partStringInErrorMessage(errs, errMessage) {
+ t.Errorf("missing %q in\n%v", errMessage, errorsAsString(errs))
+ }
},
- {
- name: "MatchExpression's value name isn't valid",
- labelSelector: &metav1.LabelSelector{
- MatchExpressions: []metav1.LabelSelectorRequirement{
- {
- Key: "key",
- Operator: metav1.LabelSelectorOpIn,
- Values: []string{"-value"},
- },
- },
- },
- wantErrorNumber: 1,
- validateErrs: func(t *testing.T, errs field.ErrorList) {
- errMessage := "a valid label must be an empty string or consist of"
- if !partStringInErrorMessage(errs, errMessage) {
- t.Errorf("missing %q in\n%v", errMessage, errorsAsString(errs))
- }
- },
+ }, {
+ name: "MatchExpression's operator isn't valid",
+ labelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key",
+ Operator: "abc",
+ Values: []string{"value"},
+ }},
},
- }
+ wantErrorNumber: 1,
+ validateErrs: func(t *testing.T, errs field.ErrorList) {
+ errMessage := "not a valid selector operator"
+ if !partStringInErrorMessage(errs, errMessage) {
+ t.Errorf("missing %q in\n%v", errMessage, errorsAsString(errs))
+ }
+ },
+ }, {
+ name: "MatchExpression's value name isn't valid",
+ labelSelector: &metav1.LabelSelector{
+ MatchExpressions: []metav1.LabelSelectorRequirement{{
+ Key: "key",
+ Operator: metav1.LabelSelectorOpIn,
+ Values: []string{"-value"},
+ }},
+ },
+ wantErrorNumber: 1,
+ validateErrs: func(t *testing.T, errs field.ErrorList) {
+ errMessage := "a valid label must be an empty string or consist of"
+ if !partStringInErrorMessage(errs, errMessage) {
+ t.Errorf("missing %q in\n%v", errMessage, errorsAsString(errs))
+ }
+ },
+ }}
for index, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
allErrs := ValidateLabelSelector(testCase.labelSelector, LabelSelectorValidationOptions{false}, field.NewPath("labelSelector"))
diff --git a/staging/src/k8s.io/apimachinery/pkg/util/validation/validation_test.go b/staging/src/k8s.io/apimachinery/pkg/util/validation/validation_test.go
index 142d546c514..499b13c737a 100644
--- a/staging/src/k8s.io/apimachinery/pkg/util/validation/validation_test.go
+++ b/staging/src/k8s.io/apimachinery/pkg/util/validation/validation_test.go
@@ -630,33 +630,27 @@ func TestIsFullyQualifiedName(t *testing.T) {
name string
targetName string
err string
- }{
- {
- name: "name needs to be fully qualified, i.e., contains at least 2 dots",
- targetName: "k8s.io",
- err: "should be a domain with at least three segments separated by dots",
- },
- {
- name: "name should not include scheme",
- targetName: "http://foo.k8s.io",
- err: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters",
- },
- {
- name: "email should be invalid",
- targetName: "example@foo.k8s.io",
- err: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters",
- },
- {
- name: "name cannot be empty",
- targetName: "",
- err: "Required value",
- },
- {
- name: "name must conform to RFC 1123",
- targetName: "A.B.C",
- err: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters",
- },
- }
+ }{{
+ name: "name needs to be fully qualified, i.e., contains at least 2 dots",
+ targetName: "k8s.io",
+ err: "should be a domain with at least three segments separated by dots",
+ }, {
+ name: "name should not include scheme",
+ targetName: "http://foo.k8s.io",
+ err: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters",
+ }, {
+ name: "email should be invalid",
+ targetName: "example@foo.k8s.io",
+ err: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters",
+ }, {
+ name: "name cannot be empty",
+ targetName: "",
+ err: "Required value",
+ }, {
+ name: "name must conform to RFC 1123",
+ targetName: "A.B.C",
+ err: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters",
+ }}
for _, tc := range messageTests {
err := IsFullyQualifiedName(field.NewPath(""), tc.targetName).ToAggregate()
switch {
diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/validation/validation_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/validation/validation_test.go
index 3e7c53c4d67..a163eb6314e 100644
--- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/validation/validation_test.go
+++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/validation/validation_test.go
@@ -23,23 +23,17 @@ import (
)
func TestValidateConfiguration(t *testing.T) {
- successCases := []resourcequotaapi.Configuration{
- {
- LimitedResources: []resourcequotaapi.LimitedResource{
- {
- Resource: "pods",
- MatchContains: []string{"requests.cpu"},
- },
- },
- },
- {
- LimitedResources: []resourcequotaapi.LimitedResource{
- {
- Resource: "persistentvolumeclaims",
- MatchContains: []string{"requests.storage"},
- },
- },
- },
+ successCases := []resourcequotaapi.Configuration{{
+ LimitedResources: []resourcequotaapi.LimitedResource{{
+ Resource: "pods",
+ MatchContains: []string{"requests.cpu"},
+ }},
+ }, {
+ LimitedResources: []resourcequotaapi.LimitedResource{{
+ Resource: "persistentvolumeclaims",
+ MatchContains: []string{"requests.storage"},
+ }},
+ },
}
for i := range successCases {
configuration := successCases[i]
diff --git a/staging/src/k8s.io/apiserver/pkg/apis/audit/validation/validation_test.go b/staging/src/k8s.io/apiserver/pkg/apis/audit/validation/validation_test.go
index a25d5dfcaa6..dfda29a8f57 100644
--- a/staging/src/k8s.io/apiserver/pkg/apis/audit/validation/validation_test.go
+++ b/staging/src/k8s.io/apiserver/pkg/apis/audit/validation/validation_test.go
@@ -134,11 +134,9 @@ func TestValidatePolicy(t *testing.T) {
policy := audit.Policy{OmitStages: []audit.Stage{
audit.Stage("foo"),
},
- Rules: []audit.PolicyRule{
- {
- Level: audit.LevelMetadata,
- },
- },
+ Rules: []audit.PolicyRule{{
+ Level: audit.LevelMetadata,
+ }},
}
errorCases = append(errorCases, policy)
diff --git a/staging/src/k8s.io/apiserver/pkg/apis/config/validation/validation_test.go b/staging/src/k8s.io/apiserver/pkg/apis/config/validation/validation_test.go
index d57a3f5294b..b5337cadf86 100644
--- a/staging/src/k8s.io/apiserver/pkg/apis/config/validation/validation_test.go
+++ b/staging/src/k8s.io/apiserver/pkg/apis/config/validation/validation_test.go
@@ -37,1072 +37,887 @@ func TestStructure(t *testing.T) {
in *config.EncryptionConfiguration
reload bool
want field.ErrorList
- }{
- {
- desc: "nil encryption config",
- in: nil,
- want: field.ErrorList{
- field.Required(root, encryptionConfigNilErr),
- },
+ }{{
+ desc: "nil encryption config",
+ in: nil,
+ want: field.ErrorList{
+ field.Required(root, encryptionConfigNilErr),
},
- {
- desc: "empty encryption config",
- in: &config.EncryptionConfiguration{},
- want: field.ErrorList{
- field.Required(root, fmt.Sprintf(atLeastOneRequiredErrFmt, root)),
- },
+ }, {
+ desc: "empty encryption config",
+ in: &config.EncryptionConfiguration{},
+ want: field.ErrorList{
+ field.Required(root, fmt.Sprintf(atLeastOneRequiredErrFmt, root)),
},
- {
- desc: "no k8s resources",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Providers: []config.ProviderConfiguration{
- {
- AESCBC: &config.AESConfiguration{
- Keys: []config.Key{
- {
- Name: "foo",
- Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
- },
- },
- },
- },
- },
+ }, {
+ desc: "no k8s resources",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Providers: []config.ProviderConfiguration{{
+ AESCBC: &config.AESConfiguration{
+ Keys: []config.Key{{
+ Name: "foo",
+ Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
+ }},
+ },
+ }},
+ }},
+ },
+ want: field.ErrorList{
+ field.Required(firstResourcePath.Child("resources"), fmt.Sprintf(atLeastOneRequiredErrFmt, root.Index(0).Child("resources"))),
+ },
+ }, {
+ desc: "no providers",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{"secrets"},
+ }},
+ },
+ want: field.ErrorList{
+ field.Required(firstResourcePath.Child("providers"), fmt.Sprintf(atLeastOneRequiredErrFmt, root.Index(0).Child("providers"))),
+ },
+ }, {
+ desc: "multiple providers",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ AESGCM: &config.AESConfiguration{
+ Keys: []config.Key{{
+ Name: "foo",
+ Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
+ }},
+ },
+ AESCBC: &config.AESConfiguration{
+ Keys: []config.Key{{
+ Name: "foo",
+ Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
+ }},
+ },
+ }},
+ }},
+ },
+ want: field.ErrorList{
+ field.Invalid(
+ firstResourcePath.Child("providers").Index(0),
+ config.ProviderConfiguration{
+ AESGCM: &config.AESConfiguration{
+ Keys: []config.Key{{
+ Name: "foo",
+ Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
+ }},
+ },
+ AESCBC: &config.AESConfiguration{
+ Keys: []config.Key{{
+ Name: "foo",
+ Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
+ }},
},
},
- },
- want: field.ErrorList{
- field.Required(firstResourcePath.Child("resources"), fmt.Sprintf(atLeastOneRequiredErrFmt, root.Index(0).Child("resources"))),
- },
+ moreThanOneElementErr),
},
- {
- desc: "no providers",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{"secrets"},
+ }, {
+ desc: "valid config",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ AESGCM: &config.AESConfiguration{
+ Keys: []config.Key{{
+ Name: "foo",
+ Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
+ }},
},
- },
- },
- want: field.ErrorList{
- field.Required(firstResourcePath.Child("providers"), fmt.Sprintf(atLeastOneRequiredErrFmt, root.Index(0).Child("providers"))),
- },
+ }},
+ }},
},
- {
- desc: "multiple providers",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- AESGCM: &config.AESConfiguration{
- Keys: []config.Key{
- {
- Name: "foo",
- Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
- },
- },
- },
- AESCBC: &config.AESConfiguration{
- Keys: []config.Key{
- {
- Name: "foo",
- Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
- },
- },
- },
- },
- },
+ want: field.ErrorList{},
+ }, {
+ desc: "duplicate kms v2 config name with kms v1 config",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-1.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
- },
- },
- want: field.ErrorList{
- field.Invalid(
- firstResourcePath.Child("providers").Index(0),
- config.ProviderConfiguration{
- AESGCM: &config.AESConfiguration{
- Keys: []config.Key{
- {
- Name: "foo",
- Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
- },
- },
- },
- AESCBC: &config.AESConfiguration{
- Keys: []config.Key{
- {
- Name: "foo",
- Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
- },
- },
- },
+ }, {
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-2.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ APIVersion: "v2",
},
- moreThanOneElementErr),
- },
+ }},
+ }},
},
- {
- desc: "valid config",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- AESGCM: &config.AESConfiguration{
- Keys: []config.Key{
- {
- Name: "foo",
- Secret: "A/j5CnrWGB83ylcPkuUhm/6TSyrQtsNJtDPwPHNOj4Q=",
- },
- },
- },
- },
- },
- },
- },
- },
- want: field.ErrorList{},
+ want: field.ErrorList{
+ field.Invalid(firstResourcePath.Child("providers").Index(1).Child("kms").Child("name"),
+ "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
},
- {
- desc: "duplicate kms v2 config name with kms v1 config",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-1.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-2.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- APIVersion: "v2",
- },
- },
- },
+ }, {
+ desc: "duplicate kms v2 config names",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-1.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ APIVersion: "v2",
},
- },
- },
- want: field.ErrorList{
- field.Invalid(firstResourcePath.Child("providers").Index(1).Child("kms").Child("name"),
- "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
- },
+ }, {
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-2.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ APIVersion: "v2",
+ },
+ }},
+ }},
},
- {
- desc: "duplicate kms v2 config names",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-1.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- APIVersion: "v2",
- },
- },
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-2.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- APIVersion: "v2",
- },
- },
- },
- },
- },
- },
- want: field.ErrorList{
- field.Invalid(firstResourcePath.Child("providers").Index(1).Child("kms").Child("name"),
- "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
- },
+ want: field.ErrorList{
+ field.Invalid(firstResourcePath.Child("providers").Index(1).Child("kms").Child("name"),
+ "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
},
- {
- desc: "duplicate kms v2 config name across providers",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-1.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- APIVersion: "v2",
- },
- },
- },
+ }, {
+ desc: "duplicate kms v2 config name across providers",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-1.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ APIVersion: "v2",
},
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-2.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- APIVersion: "v2",
- },
- },
- },
+ }},
+ }, {
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-2.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ APIVersion: "v2",
},
- },
- },
- want: field.ErrorList{
- field.Invalid(root.Index(1).Child("providers").Index(0).Child("kms").Child("name"),
- "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
- },
+ }},
+ }},
},
- {
- desc: "duplicate kms config name with v1 and v2 across providers",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-1.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-2.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- APIVersion: "v2",
- },
- },
- },
- },
- },
- },
- want: field.ErrorList{
- field.Invalid(root.Index(1).Child("providers").Index(0).Child("kms").Child("name"),
- "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
- },
+ want: field.ErrorList{
+ field.Invalid(root.Index(1).Child("providers").Index(0).Child("kms").Child("name"),
+ "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
},
- {
- desc: "duplicate kms v1 config names shouldn't error",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-1.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-2.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
+ }, {
+ desc: "duplicate kms config name with v1 and v2 across providers",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-1.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
- },
- },
- want: field.ErrorList{},
+ }},
+ }, {
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-2.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ APIVersion: "v2",
+ },
+ }},
+ }},
},
- {
- desc: "duplicate kms v1 config names should error when reload=true",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{"secrets"},
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-1.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider-2.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
- },
- },
- reload: true,
- want: field.ErrorList{
- field.Invalid(root.Index(0).Child("providers").Index(1).Child("kms").Child("name"),
- "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
- },
+ want: field.ErrorList{
+ field.Invalid(root.Index(1).Child("providers").Index(0).Child("kms").Child("name"),
+ "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
},
- {
- desc: "config should error when events.k8s.io group is used",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "events.events.k8s.io",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
+ }, {
+ desc: "duplicate kms v1 config names shouldn't error",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-1.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
- },
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(0),
+ }, {
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-2.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ want: field.ErrorList{},
+ }, {
+ desc: "duplicate kms v1 config names should error when reload=true",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{"secrets"},
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-1.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }, {
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider-2.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: true,
+ want: field.ErrorList{
+ field.Invalid(root.Index(0).Child("providers").Index(1).Child("kms").Child("name"),
+ "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
+ },
+ }, {
+ desc: "config should error when events.k8s.io group is used",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
"events.events.k8s.io",
- eventsGroupErr,
- ),
- },
- }, {
- desc: "config should error when events.k8s.io group is used later in the list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "secrets",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
- {
- Resources: []string{
- "secret",
- "events.events.k8s.io",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(1).Child("resources").Index(1),
- "events.events.k8s.io",
- eventsGroupErr,
- ),
- },
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
},
- {
- desc: "config should error when *.events.k8s.io group is used",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "*.events.k8s.io",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(0),
+ "events.events.k8s.io",
+ eventsGroupErr,
+ ),
+ },
+ }, {
+ desc: "config should error when events.k8s.io group is used later in the list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "secrets",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(0),
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }, {
+ Resources: []string{
+ "secret",
+ "events.events.k8s.io",
+ },
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(1).Child("resources").Index(1),
+ "events.events.k8s.io",
+ eventsGroupErr,
+ ),
+ },
+ }, {
+ desc: "config should error when *.events.k8s.io group is used",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
"*.events.k8s.io",
- eventsGroupErr,
- ),
- },
- },
- {
- desc: "config should error when extensions group is used",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "*.extensions",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(0),
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(0),
+ "*.events.k8s.io",
+ eventsGroupErr,
+ ),
+ },
+ }, {
+ desc: "config should error when extensions group is used",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
"*.extensions",
- extensionsGroupErr,
- ),
- },
- },
- {
- desc: "config should error when foo.extensions group is used",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "foo.extensions",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(0),
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(0),
+ "*.extensions",
+ extensionsGroupErr,
+ ),
+ },
+ }, {
+ desc: "config should error when foo.extensions group is used",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
"foo.extensions",
- extensionsGroupErr,
- ),
- },
- },
- {
- desc: "config should error when '*' resource is used",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "*",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(0),
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(0),
+ "foo.extensions",
+ extensionsGroupErr,
+ ),
+ },
+ }, {
+ desc: "config should error when '*' resource is used",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
"*",
- starResourceErr,
- ),
- },
- },
- {
- desc: "should error when resource name has capital letters",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "apiServerIPInfo",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(0),
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(0),
+ "*",
+ starResourceErr,
+ ),
+ },
+ }, {
+ desc: "should error when resource name has capital letters",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
"apiServerIPInfo",
- resourceNameErr,
- ),
- },
- },
- {
- desc: "should error when resource name is apiserveripinfo",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "apiserveripinfo",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(0),
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(0),
+ "apiServerIPInfo",
+ resourceNameErr,
+ ),
+ },
+ }, {
+ desc: "should error when resource name is apiserveripinfo",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
"apiserveripinfo",
- nonRESTAPIResourceErr,
- ),
- },
- },
- {
- desc: "should error when resource name is serviceipallocations",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "serviceipallocations",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(0),
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(0),
+ "apiserveripinfo",
+ nonRESTAPIResourceErr,
+ ),
+ },
+ }, {
+ desc: "should error when resource name is serviceipallocations",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
"serviceipallocations",
- nonRESTAPIResourceErr,
- ),
- },
- },
- {
- desc: "should error when resource name is servicenodeportallocations",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "servicenodeportallocations",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(0),
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(0),
+ "serviceipallocations",
+ nonRESTAPIResourceErr,
+ ),
+ },
+ }, {
+ desc: "should error when resource name is servicenodeportallocations",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
"servicenodeportallocations",
- nonRESTAPIResourceErr,
- ),
- },
- },
- {
- desc: "should not error when '*.apps' and '*.' are used within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "*.apps",
- "*.",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{},
- },
- {
- desc: "should error when the same resource across groups is encrypted",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "*.",
- "foos.*",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(0),
+ "servicenodeportallocations",
+ nonRESTAPIResourceErr,
+ ),
+ },
+ }, {
+ desc: "should not error when '*.apps' and '*.' are used within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "*.apps",
+ "*.",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources").Index(1),
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{},
+ }, {
+ desc: "should error when the same resource across groups is encrypted",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "*.",
"foos.*",
- resourceAcrossGroupErr,
- ),
- },
- },
- {
- desc: "should error when secrets are specified twice within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "secrets",
- "secrets",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources"),
- []string{
- "secrets",
- "secrets",
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
- duplicateResourceErr,
- ),
- },
+ }},
+ }},
},
- {
- desc: "should error once when secrets are specified many times within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "secrets",
- "secrets",
- "secrets",
- "secrets",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources").Index(1),
+ "foos.*",
+ resourceAcrossGroupErr,
+ ),
+ },
+ }, {
+ desc: "should error when secrets are specified twice within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "secrets",
+ "secrets",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources"),
- []string{
- "secrets",
- "secrets",
- "secrets",
- "secrets",
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
- duplicateResourceErr,
- ),
- },
+ }},
+ }},
},
- {
- desc: "should error when secrets are specified twice within the same resource list, via dot",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "secrets",
- "secrets.",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources"),
+ []string{
+ "secrets",
+ "secrets",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources"),
- []string{
- "secrets",
- "secrets.",
- },
- duplicateResourceErr,
- ),
- },
+ duplicateResourceErr,
+ ),
},
- {
- desc: "should error when '*.apps' and '*.' and '*.*' are used within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "*.apps",
- "*.",
- "*.*",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ }, {
+ desc: "should error once when secrets are specified many times within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "secrets",
+ "secrets",
+ "secrets",
+ "secrets",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources"),
- []string{
- "*.apps",
- "*.",
- "*.*",
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
- overlapErr,
- ),
- },
+ }},
+ }},
},
- {
- desc: "should not error when deployments.apps are specified with '*.' within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "deployments.apps",
- "*.",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources"),
+ []string{
+ "secrets",
+ "secrets",
+ "secrets",
+ "secrets",
},
- },
- reload: false,
- want: field.ErrorList{},
+ duplicateResourceErr,
+ ),
},
- {
- desc: "should error when deployments.apps are specified with '*.apps' within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "deployments.apps",
- "*.apps",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ }, {
+ desc: "should error when secrets are specified twice within the same resource list, via dot",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "secrets",
+ "secrets.",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources"),
- []string{
- "deployments.apps",
- "*.apps",
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
- overlapErr,
- ),
- },
+ }},
+ }},
},
- {
- desc: "should error when secrets are specified with '*.' within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "secrets",
- "*.",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources"),
+ []string{
+ "secrets",
+ "secrets.",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources"),
- []string{
- "secrets",
- "*.",
- },
- overlapErr,
- ),
- },
+ duplicateResourceErr,
+ ),
},
- {
- desc: "should error when pods are specified with '*.' within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "pods",
- "*.",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ }, {
+ desc: "should error when '*.apps' and '*.' and '*.*' are used within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "*.apps",
+ "*.",
+ "*.*",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources"),
- []string{
- "pods",
- "*.",
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
- overlapErr,
- ),
- },
+ }},
+ }},
},
- {
- desc: "should error when other resources are specified with '*.*' within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "secrets",
- "*.*",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources"),
+ []string{
+ "*.apps",
+ "*.",
+ "*.*",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources"),
- []string{
- "secrets",
- "*.*",
- },
- overlapErr,
- ),
- },
+ overlapErr,
+ ),
},
- {
- desc: "should error when both '*.' and '*.*' are used within the same resource list",
- in: &config.EncryptionConfiguration{
- Resources: []config.ResourceConfiguration{
- {
- Resources: []string{
- "*.",
- "*.*",
- },
- Providers: []config.ProviderConfiguration{
- {
- KMS: &config.KMSConfiguration{
- Name: "foo",
- Endpoint: "unix:///tmp/kms-provider.socket",
- Timeout: &metav1.Duration{Duration: 3 * time.Second},
- CacheSize: &cacheSize,
- APIVersion: "v1",
- },
- },
- },
- },
+ }, {
+ desc: "should not error when deployments.apps are specified with '*.' within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "deployments.apps",
+ "*.",
},
- },
- reload: false,
- want: field.ErrorList{
- field.Invalid(
- root.Index(0).Child("resources"),
- []string{
- "*.",
- "*.*",
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
},
- overlapErr,
- ),
- },
+ }},
+ }},
},
- }
+ reload: false,
+ want: field.ErrorList{},
+ }, {
+ desc: "should error when deployments.apps are specified with '*.apps' within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "deployments.apps",
+ "*.apps",
+ },
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources"),
+ []string{
+ "deployments.apps",
+ "*.apps",
+ },
+ overlapErr,
+ ),
+ },
+ }, {
+ desc: "should error when secrets are specified with '*.' within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "secrets",
+ "*.",
+ },
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources"),
+ []string{
+ "secrets",
+ "*.",
+ },
+ overlapErr,
+ ),
+ },
+ }, {
+ desc: "should error when pods are specified with '*.' within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "pods",
+ "*.",
+ },
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources"),
+ []string{
+ "pods",
+ "*.",
+ },
+ overlapErr,
+ ),
+ },
+ }, {
+ desc: "should error when other resources are specified with '*.*' within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "secrets",
+ "*.*",
+ },
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources"),
+ []string{
+ "secrets",
+ "*.*",
+ },
+ overlapErr,
+ ),
+ },
+ }, {
+ desc: "should error when both '*.' and '*.*' are used within the same resource list",
+ in: &config.EncryptionConfiguration{
+ Resources: []config.ResourceConfiguration{{
+ Resources: []string{
+ "*.",
+ "*.*",
+ },
+ Providers: []config.ProviderConfiguration{{
+ KMS: &config.KMSConfiguration{
+ Name: "foo",
+ Endpoint: "unix:///tmp/kms-provider.socket",
+ Timeout: &metav1.Duration{Duration: 3 * time.Second},
+ CacheSize: &cacheSize,
+ APIVersion: "v1",
+ },
+ }},
+ }},
+ },
+ reload: false,
+ want: field.ErrorList{
+ field.Invalid(
+ root.Index(0).Child("resources"),
+ []string{
+ "*.",
+ "*.*",
+ },
+ overlapErr,
+ ),
+ },
+ }}
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
@@ -1120,41 +935,35 @@ func TestKey(t *testing.T) {
desc string
in config.Key
want field.ErrorList
- }{
- {
- desc: "valid key",
- in: config.Key{Name: "foo", Secret: "c2VjcmV0IGlzIHNlY3VyZQ=="},
- want: field.ErrorList{},
+ }{{
+ desc: "valid key",
+ in: config.Key{Name: "foo", Secret: "c2VjcmV0IGlzIHNlY3VyZQ=="},
+ want: field.ErrorList{},
+ }, {
+ desc: "key without name",
+ in: config.Key{Secret: "c2VjcmV0IGlzIHNlY3VyZQ=="},
+ want: field.ErrorList{
+ field.Required(path.Child("name"), fmt.Sprintf(mandatoryFieldErrFmt, "name", "key")),
},
- {
- desc: "key without name",
- in: config.Key{Secret: "c2VjcmV0IGlzIHNlY3VyZQ=="},
- want: field.ErrorList{
- field.Required(path.Child("name"), fmt.Sprintf(mandatoryFieldErrFmt, "name", "key")),
- },
+ }, {
+ desc: "key without secret",
+ in: config.Key{Name: "foo"},
+ want: field.ErrorList{
+ field.Required(path.Child("secret"), fmt.Sprintf(mandatoryFieldErrFmt, "secret", "key")),
},
- {
- desc: "key without secret",
- in: config.Key{Name: "foo"},
- want: field.ErrorList{
- field.Required(path.Child("secret"), fmt.Sprintf(mandatoryFieldErrFmt, "secret", "key")),
- },
+ }, {
+ desc: "key is not base64 encoded",
+ in: config.Key{Name: "foo", Secret: "P@ssword"},
+ want: field.ErrorList{
+ field.Invalid(path.Child("secret"), "REDACTED", base64EncodingErr),
},
- {
- desc: "key is not base64 encoded",
- in: config.Key{Name: "foo", Secret: "P@ssword"},
- want: field.ErrorList{
- field.Invalid(path.Child("secret"), "REDACTED", base64EncodingErr),
- },
+ }, {
+ desc: "key is not of expected length",
+ in: config.Key{Name: "foo", Secret: "cGFzc3dvcmQK"},
+ want: field.ErrorList{
+ field.Invalid(path.Child("secret"), "REDACTED", fmt.Sprintf(keyLenErrFmt, 9, aesKeySizes)),
},
- {
- desc: "key is not of expected length",
- in: config.Key{Name: "foo", Secret: "cGFzc3dvcmQK"},
- want: field.ErrorList{
- field.Invalid(path.Child("secret"), "REDACTED", fmt.Sprintf(keyLenErrFmt, 9, aesKeySizes)),
- },
- },
- }
+ }}
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
@@ -1175,27 +984,23 @@ func TestKMSProviderTimeout(t *testing.T) {
desc string
in *config.KMSConfiguration
want field.ErrorList
- }{
- {
- desc: "valid timeout",
- in: &config.KMSConfiguration{Timeout: &metav1.Duration{Duration: 1 * time.Minute}},
- want: field.ErrorList{},
+ }{{
+ desc: "valid timeout",
+ in: &config.KMSConfiguration{Timeout: &metav1.Duration{Duration: 1 * time.Minute}},
+ want: field.ErrorList{},
+ }, {
+ desc: "negative timeout",
+ in: &config.KMSConfiguration{Timeout: negativeTimeout},
+ want: field.ErrorList{
+ field.Invalid(timeoutField, negativeTimeout, fmt.Sprintf(zeroOrNegativeErrFmt, "timeout")),
},
- {
- desc: "negative timeout",
- in: &config.KMSConfiguration{Timeout: negativeTimeout},
- want: field.ErrorList{
- field.Invalid(timeoutField, negativeTimeout, fmt.Sprintf(zeroOrNegativeErrFmt, "timeout")),
- },
+ }, {
+ desc: "zero timeout",
+ in: &config.KMSConfiguration{Timeout: zeroTimeout},
+ want: field.ErrorList{
+ field.Invalid(timeoutField, zeroTimeout, fmt.Sprintf(zeroOrNegativeErrFmt, "timeout")),
},
- {
- desc: "zero timeout",
- in: &config.KMSConfiguration{Timeout: zeroTimeout},
- want: field.ErrorList{
- field.Invalid(timeoutField, zeroTimeout, fmt.Sprintf(zeroOrNegativeErrFmt, "timeout")),
- },
- },
- }
+ }}
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
@@ -1213,34 +1018,29 @@ func TestKMSEndpoint(t *testing.T) {
desc string
in *config.KMSConfiguration
want field.ErrorList
- }{
- {
- desc: "valid endpoint",
- in: &config.KMSConfiguration{Endpoint: "unix:///socket.sock"},
- want: field.ErrorList{},
+ }{{
+ desc: "valid endpoint",
+ in: &config.KMSConfiguration{Endpoint: "unix:///socket.sock"},
+ want: field.ErrorList{},
+ }, {
+ desc: "empty endpoint",
+ in: &config.KMSConfiguration{},
+ want: field.ErrorList{
+ field.Invalid(endpointField, "", fmt.Sprintf(mandatoryFieldErrFmt, "endpoint", "kms")),
},
- {
- desc: "empty endpoint",
- in: &config.KMSConfiguration{},
- want: field.ErrorList{
- field.Invalid(endpointField, "", fmt.Sprintf(mandatoryFieldErrFmt, "endpoint", "kms")),
- },
+ }, {
+ desc: "non unix endpoint",
+ in: &config.KMSConfiguration{Endpoint: "https://www.foo.com"},
+ want: field.ErrorList{
+ field.Invalid(endpointField, "https://www.foo.com", fmt.Sprintf(unsupportedSchemeErrFmt, "https")),
},
- {
- desc: "non unix endpoint",
- in: &config.KMSConfiguration{Endpoint: "https://www.foo.com"},
- want: field.ErrorList{
- field.Invalid(endpointField, "https://www.foo.com", fmt.Sprintf(unsupportedSchemeErrFmt, "https")),
- },
+ }, {
+ desc: "invalid url",
+ in: &config.KMSConfiguration{Endpoint: "unix:///foo\n.socket"},
+ want: field.ErrorList{
+ field.Invalid(endpointField, "unix:///foo\n.socket", fmt.Sprintf(invalidURLErrFmt, `parse "unix:///foo\n.socket": net/url: invalid control character in URL`)),
},
- {
- desc: "invalid url",
- in: &config.KMSConfiguration{Endpoint: "unix:///foo\n.socket"},
- want: field.ErrorList{
- field.Invalid(endpointField, "unix:///foo\n.socket", fmt.Sprintf(invalidURLErrFmt, `parse "unix:///foo\n.socket": net/url: invalid control character in URL`)),
- },
- },
- }
+ }}
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
@@ -1262,32 +1062,27 @@ func TestKMSProviderCacheSize(t *testing.T) {
desc string
in *config.KMSConfiguration
want field.ErrorList
- }{
- {
- desc: "valid positive cache size",
- in: &config.KMSConfiguration{APIVersion: "v1", CacheSize: &positiveCacheSize},
- want: field.ErrorList{},
+ }{{
+ desc: "valid positive cache size",
+ in: &config.KMSConfiguration{APIVersion: "v1", CacheSize: &positiveCacheSize},
+ want: field.ErrorList{},
+ }, {
+ desc: "invalid zero cache size",
+ in: &config.KMSConfiguration{APIVersion: "v1", CacheSize: &zeroCacheSize},
+ want: field.ErrorList{
+ field.Invalid(cacheField, int32(0), fmt.Sprintf(nonZeroErrFmt, "cachesize")),
},
- {
- desc: "invalid zero cache size",
- in: &config.KMSConfiguration{APIVersion: "v1", CacheSize: &zeroCacheSize},
- want: field.ErrorList{
- field.Invalid(cacheField, int32(0), fmt.Sprintf(nonZeroErrFmt, "cachesize")),
- },
+ }, {
+ desc: "valid negative caches size",
+ in: &config.KMSConfiguration{APIVersion: "v1", CacheSize: &negativeCacheSize},
+ want: field.ErrorList{},
+ }, {
+ desc: "cache size set with v2 provider",
+ in: &config.KMSConfiguration{CacheSize: &positiveCacheSize, APIVersion: "v2"},
+ want: field.ErrorList{
+ field.Invalid(cacheField, positiveCacheSize, "cachesize is not supported in v2"),
},
- {
- desc: "valid negative caches size",
- in: &config.KMSConfiguration{APIVersion: "v1", CacheSize: &negativeCacheSize},
- want: field.ErrorList{},
- },
- {
- desc: "cache size set with v2 provider",
- in: &config.KMSConfiguration{CacheSize: &positiveCacheSize, APIVersion: "v2"},
- want: field.ErrorList{
- field.Invalid(cacheField, positiveCacheSize, "cachesize is not supported in v2"),
- },
- },
- }
+ }}
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
@@ -1306,25 +1101,21 @@ func TestKMSProviderAPIVersion(t *testing.T) {
desc string
in *config.KMSConfiguration
want field.ErrorList
- }{
- {
- desc: "valid v1 api version",
- in: &config.KMSConfiguration{APIVersion: "v1"},
- want: field.ErrorList{},
+ }{{
+ desc: "valid v1 api version",
+ in: &config.KMSConfiguration{APIVersion: "v1"},
+ want: field.ErrorList{},
+ }, {
+ desc: "valid v2 api version",
+ in: &config.KMSConfiguration{APIVersion: "v2"},
+ want: field.ErrorList{},
+ }, {
+ desc: "invalid api version",
+ in: &config.KMSConfiguration{APIVersion: "v3"},
+ want: field.ErrorList{
+ field.Invalid(apiVersionField, "v3", fmt.Sprintf(unsupportedKMSAPIVersionErrFmt, "apiVersion")),
},
- {
- desc: "valid v2 api version",
- in: &config.KMSConfiguration{APIVersion: "v2"},
- want: field.ErrorList{},
- },
- {
- desc: "invalid api version",
- in: &config.KMSConfiguration{APIVersion: "v3"},
- want: field.ErrorList{
- field.Invalid(apiVersionField, "v3", fmt.Sprintf(unsupportedKMSAPIVersionErrFmt, "apiVersion")),
- },
- },
- }
+ }}
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
@@ -1345,64 +1136,55 @@ func TestKMSProviderName(t *testing.T) {
reload bool
kmsProviderNames sets.String
want field.ErrorList
- }{
- {
- desc: "valid name",
- in: &config.KMSConfiguration{Name: "foo"},
- want: field.ErrorList{},
+ }{{
+ desc: "valid name",
+ in: &config.KMSConfiguration{Name: "foo"},
+ want: field.ErrorList{},
+ }, {
+ desc: "empty name",
+ in: &config.KMSConfiguration{},
+ want: field.ErrorList{
+ field.Required(nameField, fmt.Sprintf(mandatoryFieldErrFmt, "name", "provider")),
},
- {
- desc: "empty name",
- in: &config.KMSConfiguration{},
- want: field.ErrorList{
- field.Required(nameField, fmt.Sprintf(mandatoryFieldErrFmt, "name", "provider")),
- },
+ }, {
+ desc: "invalid name with :",
+ in: &config.KMSConfiguration{Name: "foo:bar"},
+ want: field.ErrorList{
+ field.Invalid(nameField, "foo:bar", fmt.Sprintf(invalidKMSConfigNameErrFmt, "foo:bar")),
},
- {
- desc: "invalid name with :",
- in: &config.KMSConfiguration{Name: "foo:bar"},
- want: field.ErrorList{
- field.Invalid(nameField, "foo:bar", fmt.Sprintf(invalidKMSConfigNameErrFmt, "foo:bar")),
- },
+ }, {
+ desc: "invalid name with : but api version is v1",
+ in: &config.KMSConfiguration{Name: "foo:bar", APIVersion: "v1"},
+ want: field.ErrorList{},
+ }, {
+ desc: "duplicate name, kms v2, reload=false",
+ in: &config.KMSConfiguration{APIVersion: "v2", Name: "foo"},
+ kmsProviderNames: sets.NewString("foo"),
+ want: field.ErrorList{
+ field.Invalid(nameField, "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
},
- {
- desc: "invalid name with : but api version is v1",
- in: &config.KMSConfiguration{Name: "foo:bar", APIVersion: "v1"},
- want: field.ErrorList{},
+ }, {
+ desc: "duplicate name, kms v2, reload=true",
+ in: &config.KMSConfiguration{APIVersion: "v2", Name: "foo"},
+ reload: true,
+ kmsProviderNames: sets.NewString("foo"),
+ want: field.ErrorList{
+ field.Invalid(nameField, "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
},
- {
- desc: "duplicate name, kms v2, reload=false",
- in: &config.KMSConfiguration{APIVersion: "v2", Name: "foo"},
- kmsProviderNames: sets.NewString("foo"),
- want: field.ErrorList{
- field.Invalid(nameField, "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
- },
+ }, {
+ desc: "duplicate name, kms v1, reload=false",
+ in: &config.KMSConfiguration{APIVersion: "v1", Name: "foo"},
+ kmsProviderNames: sets.NewString("foo"),
+ want: field.ErrorList{},
+ }, {
+ desc: "duplicate name, kms v1, reload=true",
+ in: &config.KMSConfiguration{APIVersion: "v1", Name: "foo"},
+ reload: true,
+ kmsProviderNames: sets.NewString("foo"),
+ want: field.ErrorList{
+ field.Invalid(nameField, "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
},
- {
- desc: "duplicate name, kms v2, reload=true",
- in: &config.KMSConfiguration{APIVersion: "v2", Name: "foo"},
- reload: true,
- kmsProviderNames: sets.NewString("foo"),
- want: field.ErrorList{
- field.Invalid(nameField, "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
- },
- },
- {
- desc: "duplicate name, kms v1, reload=false",
- in: &config.KMSConfiguration{APIVersion: "v1", Name: "foo"},
- kmsProviderNames: sets.NewString("foo"),
- want: field.ErrorList{},
- },
- {
- desc: "duplicate name, kms v1, reload=true",
- in: &config.KMSConfiguration{APIVersion: "v1", Name: "foo"},
- reload: true,
- kmsProviderNames: sets.NewString("foo"),
- want: field.ErrorList{
- field.Invalid(nameField, "foo", fmt.Sprintf(duplicateKMSConfigNameErrFmt, "foo")),
- },
- },
- }
+ }}
for _, tt := range testCases {
t.Run(tt.desc, func(t *testing.T) {
diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/validation_test.go b/staging/src/k8s.io/client-go/tools/clientcmd/validation_test.go
index 7b8e2fd5c6c..3c56498a44f 100644
--- a/staging/src/k8s.io/client-go/tools/clientcmd/validation_test.go
+++ b/staging/src/k8s.io/client-go/tools/clientcmd/validation_test.go
@@ -748,37 +748,31 @@ func TestErrConfigurationInvalidWithErrorsIs(t *testing.T) {
err error
matchAgainst error
expectMatch bool
- }{
- {
- name: "no match",
- err: errConfigurationInvalid{errors.New("my-error"), errors.New("my-other-error")},
- matchAgainst: fmt.Errorf("no entry %s", "here"),
- },
- {
- name: "match via .Is()",
- err: errConfigurationInvalid{errors.New("forbidden"), alwaysMatchingError{}},
- matchAgainst: errors.New("unauthorized"),
- expectMatch: true,
- },
- {
- name: "match via equality",
- err: errConfigurationInvalid{errors.New("err"), someError{}},
- matchAgainst: someError{},
- expectMatch: true,
- },
- {
- name: "match via nested aggregate",
- err: errConfigurationInvalid{errors.New("closed today"), errConfigurationInvalid{errConfigurationInvalid{someError{}}}},
- matchAgainst: someError{},
- expectMatch: true,
- },
- {
- name: "match via wrapped aggregate",
- err: fmt.Errorf("wrap: %w", errConfigurationInvalid{errors.New("err"), someError{}}),
- matchAgainst: someError{},
- expectMatch: true,
- },
- }
+ }{{
+ name: "no match",
+ err: errConfigurationInvalid{errors.New("my-error"), errors.New("my-other-error")},
+ matchAgainst: fmt.Errorf("no entry %s", "here"),
+ }, {
+ name: "match via .Is()",
+ err: errConfigurationInvalid{errors.New("forbidden"), alwaysMatchingError{}},
+ matchAgainst: errors.New("unauthorized"),
+ expectMatch: true,
+ }, {
+ name: "match via equality",
+ err: errConfigurationInvalid{errors.New("err"), someError{}},
+ matchAgainst: someError{},
+ expectMatch: true,
+ }, {
+ name: "match via nested aggregate",
+ err: errConfigurationInvalid{errors.New("closed today"), errConfigurationInvalid{errConfigurationInvalid{someError{}}}},
+ matchAgainst: someError{},
+ expectMatch: true,
+ }, {
+ name: "match via wrapped aggregate",
+ err: fmt.Errorf("wrap: %w", errConfigurationInvalid{errors.New("err"), someError{}}),
+ matchAgainst: someError{},
+ expectMatch: true,
+ }}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {