From ab3e7a73ec085d66a8d0e15e1dcaa4766b440810 Mon Sep 17 00:00:00 2001
From: Chao Xu
Date: Fri, 26 May 2017 16:05:51 -0700
Subject: [PATCH 1/3] validation of subresources
---
.../validation/validation.go | 69 ++++++++++--
.../validation/validation_test.go | 106 +++++++++++++++++-
2 files changed, 161 insertions(+), 14 deletions(-)
diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go
index 5425b16bebb..aa8a0e0a68f 100644
--- a/pkg/apis/admissionregistration/validation/validation.go
+++ b/pkg/apis/admissionregistration/validation/validation.go
@@ -17,6 +17,7 @@ limitations under the License.
package validation
import (
+ "fmt"
"strings"
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
@@ -66,6 +67,62 @@ func hasWildcard(slice []string) bool {
return false
}
+func validateResources(resources []string, fldPath *field.Path) field.ErrorList {
+ var allErrors field.ErrorList
+ if len(resources) == 0 {
+ allErrors = append(allErrors, field.Required(fldPath, ""))
+ }
+
+ // */x
+ resourcesWithWildcardSubresoures := sets.String{}
+ // x/*
+ subResoucesWithWildcardResource := sets.String{}
+ // */*
+ hasDoubleWildcard := false
+ // *
+ hasSingleWildcard := false
+ // x
+ hasResourceWithoutSubresource := false
+
+ for i, resSub := range resources {
+ if resSub == "" {
+ allErrors = append(allErrors, field.Required(fldPath.Index(i), ""))
+ continue
+ }
+ if resSub == "*/*" {
+ hasDoubleWildcard = true
+ }
+ if resSub == "*" {
+ hasSingleWildcard = true
+ }
+ parts := strings.SplitN(resSub, "/", 2)
+ if len(parts) == 1 {
+ hasResourceWithoutSubresource = resSub != "*"
+ continue
+ }
+ res, sub := parts[0], parts[1]
+ if _, ok := resourcesWithWildcardSubresoures[res]; ok {
+ allErrors = append(allErrors, field.Invalid(fldPath.Index(i), resources[i], fmt.Sprintf("if '%s/*' is present, must not specify %s", res, resources[i])))
+ }
+ if _, ok := subResoucesWithWildcardResource[sub]; ok {
+ allErrors = append(allErrors, field.Invalid(fldPath.Index(i), resources[i], fmt.Sprintf("if '*/%s' is present, must not specify %s", sub, resources[i])))
+ }
+ if sub == "*" {
+ resourcesWithWildcardSubresoures[res] = struct{}{}
+ }
+ if res == "*" {
+ subResoucesWithWildcardResource[sub] = struct{}{}
+ }
+ }
+ if len(resources) > 1 && hasDoubleWildcard {
+ allErrors = append(allErrors, field.Invalid(fldPath, resources, "if '*/*' is present, must not specify other resources"))
+ }
+ if hasSingleWildcard && hasResourceWithoutSubresource {
+ allErrors = append(allErrors, field.Invalid(fldPath, resources, "if '*' is present, must not specify other resources without subresources"))
+ }
+ return allErrors
+}
+
func validateRule(rule *admissionregistration.Rule, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
if len(rule.APIGroups) == 0 {
@@ -86,17 +143,7 @@ func validateRule(rule *admissionregistration.Rule, fldPath *field.Path) field.E
allErrors = append(allErrors, field.Required(fldPath.Child("apiVersions").Index(i), ""))
}
}
- if len(rule.Resources) == 0 {
- allErrors = append(allErrors, field.Required(fldPath.Child("resources"), ""))
- }
- if len(rule.Resources) > 1 && hasWildcard(rule.Resources) {
- allErrors = append(allErrors, field.Invalid(fldPath.Child("Resources"), rule.Resources, "if '*' is present, must not specify other resources"))
- }
- for i, resource := range rule.Resources {
- if resource == "" {
- allErrors = append(allErrors, field.Required(fldPath.Child("resources").Index(i), ""))
- }
- }
+ allErrors = append(allErrors, validateResources(rule.Resources, fldPath.Child("resources"))...)
return allErrors
}
diff --git a/pkg/apis/admissionregistration/validation/validation_test.go b/pkg/apis/admissionregistration/validation/validation_test.go
index e700d6a3807..d25cb880a7b 100644
--- a/pkg/apis/admissionregistration/validation/validation_test.go
+++ b/pkg/apis/admissionregistration/validation/validation_test.go
@@ -181,7 +181,7 @@ func TestValidateInitializerConfiguration(t *testing.T) {
expectedError: "resources[1]: Required value",
},
{
- name: "wildcard cannot be mixed with other strings",
+ name: "wildcard cannot be mixed with other strings for APIGroups or APIVersions",
config: getInitializerConfiguration(
[]admissionregistration.Initializer{
{
@@ -190,12 +190,112 @@ func TestValidateInitializerConfiguration(t *testing.T) {
{
APIGroups: []string{"a", "*"},
APIVersions: []string{"a", "*"},
- Resources: []string{"a", "*"},
+ Resources: []string{"a"},
},
},
},
}),
- expectedError: `initializers[0].rules[0].apiGroups: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API groups, initializers[0].rules[0].apiVersions: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API versions, initializers[0].rules[0].Resources: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other resources`,
+ expectedError: `initializers[0].rules[0].apiGroups: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API groups, initializers[0].rules[0].apiVersions: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API versions`,
+ },
+ {
+ name: `resource "*" can co-exist with resources that have subresources`,
+ config: getInitializerConfiguration(
+ []admissionregistration.Initializer{
+ {
+ Name: "initializer.k8s.io",
+ Rules: []admissionregistration.Rule{
+ {
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a/b", "a/*", "*/b"},
+ },
+ },
+ },
+ }),
+ },
+ {
+ name: `resource "*" cannot mix with resources that don't have subresources`,
+ config: getInitializerConfiguration(
+ []admissionregistration.Initializer{
+ {
+ Name: "initializer.k8s.io",
+ Rules: []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: getInitializerConfiguration(
+ []admissionregistration.Initializer{
+ {
+ Name: "initializer.k8s.io",
+ Rules: []admissionregistration.Rule{
+ {
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a/x"},
+ },
+ },
+ },
+ }),
+ expectedError: `initializers[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: getInitializerConfiguration(
+ []admissionregistration.Initializer{
+ {
+ Name: "initializer.k8s.io",
+ Rules: []admissionregistration.Rule{
+ {
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a"},
+ },
+ },
+ },
+ }),
+ },
+ {
+ name: "resource */a cannot mix with x/a",
+ config: getInitializerConfiguration(
+ []admissionregistration.Initializer{
+ {
+ Name: "initializer.k8s.io",
+ Rules: []admissionregistration.Rule{
+ {
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/a", "x/a"},
+ },
+ },
+ },
+ }),
+ expectedError: `initializers[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: getInitializerConfiguration(
+ []admissionregistration.Initializer{
+ {
+ Name: "initializer.k8s.io",
+ Rules: []admissionregistration.Rule{
+ {
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/*", "a"},
+ },
+ },
+ },
+ }),
+ expectedError: `initializers[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
},
{
name: "FailurePolicy can only be \"Ignore\"",
From 80a53d52fd32c49a5facb34453b6d1cd1d868434 Mon Sep 17 00:00:00 2001
From: Chao Xu
Date: Wed, 31 May 2017 15:14:31 -0700
Subject: [PATCH 2/3] do not allow subresources in initializer rules
---
pkg/apis/admissionregistration/types.go | 6 +-
.../admissionregistration/v1alpha1/types.go | 6 +-
.../validation/validation.go | 39 +++-
.../validation/validation_test.go | 221 ++++++++++--------
4 files changed, 170 insertions(+), 102 deletions(-)
diff --git a/pkg/apis/admissionregistration/types.go b/pkg/apis/admissionregistration/types.go
index 037c0757709..a294deead58 100644
--- a/pkg/apis/admissionregistration/types.go
+++ b/pkg/apis/admissionregistration/types.go
@@ -64,6 +64,7 @@ type Initializer struct {
// Rules describes what resources/subresources the initializer cares about.
// The initializer cares about an operation if it matches _any_ Rule.
+ // Rule.Resources must not include subresources.
Rules []Rule
// FailurePolicy defines what happens if the responsible initializer controller
@@ -97,7 +98,10 @@ type Rule struct {
// '*/scale' means all scale subresources.
// '*/*' means all resources and their subresources.
//
- // If '*' or '*/*' is present, the length of the slice must be one.
+ // If wildcard is present, the validation rule will ensure resources do not
+ // overlap with each other.
+ //
+ // Depending on the enclosing object, subresources might not be allowed.
// Required.
Resources []string
}
diff --git a/pkg/apis/admissionregistration/v1alpha1/types.go b/pkg/apis/admissionregistration/v1alpha1/types.go
index 67a76dff981..e5456c97a30 100644
--- a/pkg/apis/admissionregistration/v1alpha1/types.go
+++ b/pkg/apis/admissionregistration/v1alpha1/types.go
@@ -66,6 +66,7 @@ type Initializer struct {
// Rules describes what resources/subresources the initializer cares about.
// The initializer cares about an operation if it matches _any_ Rule.
+ // Rule.Resources must not include subresources.
Rules []Rule `json:"rules,omitempty" protobuf:"bytes,2,rep,name=rules"`
// FailurePolicy defines what happens if the responsible initializer controller
@@ -99,7 +100,10 @@ type Rule struct {
// '*/scale' means all scale subresources.
// '*/*' means all resources and their subresources.
//
- // If '*' or '*/*' is present, the length of the slice must be one.
+ // If wildcard is present, the validation rule will ensure resources do not
+ // overlap with each other.
+ //
+ // Depending on the enclosing object, subresources might not be allowed.
// Required.
Resources []string `json:"resources,omitempty" protobuf:"bytes,3,rep,name=resources"`
}
diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go
index aa8a0e0a68f..4c478f457e0 100644
--- a/pkg/apis/admissionregistration/validation/validation.go
+++ b/pkg/apis/admissionregistration/validation/validation.go
@@ -49,7 +49,8 @@ func validateInitializer(initializer *admissionregistration.Initializer, fldPath
}
for i, rule := range initializer.Rules {
- allErrors = append(allErrors, validateRule(&rule, fldPath.Child("rules").Index(i))...)
+ notAllowSubresources := false
+ allErrors = append(allErrors, validateRule(&rule, fldPath.Child("rules").Index(i), notAllowSubresources)...)
}
// TODO: relax the validation rule when admissionregistration is beta.
if initializer.FailurePolicy != nil && *initializer.FailurePolicy != admissionregistration.Ignore {
@@ -102,10 +103,10 @@ func validateResources(resources []string, fldPath *field.Path) field.ErrorList
}
res, sub := parts[0], parts[1]
if _, ok := resourcesWithWildcardSubresoures[res]; ok {
- allErrors = append(allErrors, field.Invalid(fldPath.Index(i), resources[i], fmt.Sprintf("if '%s/*' is present, must not specify %s", res, resources[i])))
+ allErrors = append(allErrors, field.Invalid(fldPath.Index(i), resSub, fmt.Sprintf("if '%s/*' is present, must not specify %s", res, resSub)))
}
if _, ok := subResoucesWithWildcardResource[sub]; ok {
- allErrors = append(allErrors, field.Invalid(fldPath.Index(i), resources[i], fmt.Sprintf("if '*/%s' is present, must not specify %s", sub, resources[i])))
+ allErrors = append(allErrors, field.Invalid(fldPath.Index(i), resSub, fmt.Sprintf("if '*/%s' is present, must not specify %s", sub, resSub)))
}
if sub == "*" {
resourcesWithWildcardSubresoures[res] = struct{}{}
@@ -123,7 +124,26 @@ func validateResources(resources []string, fldPath *field.Path) field.ErrorList
return allErrors
}
-func validateRule(rule *admissionregistration.Rule, fldPath *field.Path) field.ErrorList {
+func validateResourcesNoSubResources(resources []string, fldPath *field.Path) field.ErrorList {
+ var allErrors field.ErrorList
+ if len(resources) == 0 {
+ allErrors = append(allErrors, field.Required(fldPath, ""))
+ }
+ for i, resource := range resources {
+ if resource == "" {
+ allErrors = append(allErrors, field.Required(fldPath.Index(i), ""))
+ }
+ if strings.Contains(resource, "/") {
+ allErrors = append(allErrors, field.Invalid(fldPath.Index(i), resource, "must not specify subresources"))
+ }
+ }
+ if len(resources) > 1 && hasWildcard(resources) {
+ allErrors = append(allErrors, field.Invalid(fldPath, resources, "if '*' is present, must not specify other resources"))
+ }
+ return allErrors
+}
+
+func validateRule(rule *admissionregistration.Rule, fldPath *field.Path, allowSubResource bool) field.ErrorList {
var allErrors field.ErrorList
if len(rule.APIGroups) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("apiGroups"), ""))
@@ -143,7 +163,11 @@ func validateRule(rule *admissionregistration.Rule, fldPath *field.Path) field.E
allErrors = append(allErrors, field.Required(fldPath.Child("apiVersions").Index(i), ""))
}
}
- allErrors = append(allErrors, validateResources(rule.Resources, fldPath.Child("resources"))...)
+ if allowSubResource {
+ allErrors = append(allErrors, validateResources(rule.Resources, fldPath.Child("resources"))...)
+ } else {
+ allErrors = append(allErrors, validateResourcesNoSubResources(rule.Resources, fldPath.Child("resources"))...)
+ }
return allErrors
}
@@ -154,7 +178,7 @@ func ValidateInitializerConfigurationUpdate(newIC, oldIC *admissionregistration.
func ValidateExternalAdmissionHookConfiguration(e *admissionregistration.ExternalAdmissionHookConfiguration) field.ErrorList {
allErrors := genericvalidation.ValidateObjectMeta(&e.ObjectMeta, false, genericvalidation.NameIsDNSSubdomain, field.NewPath("metadata"))
for i, hook := range e.ExternalAdmissionHooks {
- allErrors = append(allErrors, validateExternalAdmissionHook(&hook, field.NewPath("externalAdmissionHook").Index(i))...)
+ allErrors = append(allErrors, validateExternalAdmissionHook(&hook, field.NewPath("externalAdmissionHooks").Index(i))...)
}
return allErrors
}
@@ -212,7 +236,8 @@ func validateRuleWithOperations(ruleWithOperations *admissionregistration.RuleWi
allErrors = append(allErrors, field.NotSupported(fldPath.Child("operations").Index(i), operation, supportedOperations.List()))
}
}
- allErrors = append(allErrors, validateRule(&ruleWithOperations.Rule, fldPath)...)
+ allowSubResource := true
+ allErrors = append(allErrors, validateRule(&ruleWithOperations.Rule, fldPath, allowSubResource)...)
return allErrors
}
diff --git a/pkg/apis/admissionregistration/validation/validation_test.go b/pkg/apis/admissionregistration/validation/validation_test.go
index d25cb880a7b..a864ac3a866 100644
--- a/pkg/apis/admissionregistration/validation/validation_test.go
+++ b/pkg/apis/admissionregistration/validation/validation_test.go
@@ -181,7 +181,7 @@ func TestValidateInitializerConfiguration(t *testing.T) {
expectedError: "resources[1]: Required value",
},
{
- name: "wildcard cannot be mixed with other strings for APIGroups or APIVersions",
+ name: "wildcard cannot be mixed with other strings for APIGroups or APIVersions or Resources",
config: getInitializerConfiguration(
[]admissionregistration.Initializer{
{
@@ -190,15 +190,15 @@ func TestValidateInitializerConfiguration(t *testing.T) {
{
APIGroups: []string{"a", "*"},
APIVersions: []string{"a", "*"},
- Resources: []string{"a"},
+ Resources: []string{"a", "*"},
},
},
},
}),
- expectedError: `initializers[0].rules[0].apiGroups: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API groups, initializers[0].rules[0].apiVersions: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API versions`,
+ expectedError: `[initializers[0].rules[0].apiGroups: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API groups, initializers[0].rules[0].apiVersions: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other API versions, initializers[0].rules[0].resources: Invalid value: []string{"a", "*"}: if '*' is present, must not specify other resources]`,
},
{
- name: `resource "*" can co-exist with resources that have subresources`,
+ name: "Subresource not allowed",
config: getInitializerConfiguration(
[]admissionregistration.Initializer{
{
@@ -207,95 +207,12 @@ func TestValidateInitializerConfiguration(t *testing.T) {
{
APIGroups: []string{"a"},
APIVersions: []string{"a"},
- Resources: []string{"*", "a/b", "a/*", "*/b"},
+ Resources: []string{"a/b"},
},
},
},
}),
- },
- {
- name: `resource "*" cannot mix with resources that don't have subresources`,
- config: getInitializerConfiguration(
- []admissionregistration.Initializer{
- {
- Name: "initializer.k8s.io",
- Rules: []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: getInitializerConfiguration(
- []admissionregistration.Initializer{
- {
- Name: "initializer.k8s.io",
- Rules: []admissionregistration.Rule{
- {
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a/x"},
- },
- },
- },
- }),
- expectedError: `initializers[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: getInitializerConfiguration(
- []admissionregistration.Initializer{
- {
- Name: "initializer.k8s.io",
- Rules: []admissionregistration.Rule{
- {
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"a/*", "a"},
- },
- },
- },
- }),
- },
- {
- name: "resource */a cannot mix with x/a",
- config: getInitializerConfiguration(
- []admissionregistration.Initializer{
- {
- Name: "initializer.k8s.io",
- Rules: []admissionregistration.Rule{
- {
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/a", "x/a"},
- },
- },
- },
- }),
- expectedError: `initializers[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: getInitializerConfiguration(
- []admissionregistration.Initializer{
- {
- Name: "initializer.k8s.io",
- Rules: []admissionregistration.Rule{
- {
- APIGroups: []string{"a"},
- APIVersions: []string{"a"},
- Resources: []string{"*/*", "a"},
- },
- },
- },
- }),
- expectedError: `initializers[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
+ expectedError: ` "a/b": must not specify subresources`,
},
{
name: "FailurePolicy can only be \"Ignore\"",
@@ -357,7 +274,7 @@ func TestValidateExternalAdmissionHookConfiguration(t *testing.T) {
Name: "",
},
}),
- expectedError: `externalAdmissionHook[1].name: Invalid value: "k8s.io": should be a domain with at least two dots, externalAdmissionHook[2].name: Required value`,
+ expectedError: `externalAdmissionHooks[1].name: Invalid value: "k8s.io": should be a domain with at least two dots, externalAdmissionHooks[2].name: Required value`,
},
{
name: "Operations must not be empty or nil",
@@ -385,7 +302,7 @@ func TestValidateExternalAdmissionHookConfiguration(t *testing.T) {
},
},
}),
- expectedError: `externalAdmissionHook[0].rules[0].operations: Required value, externalAdmissionHook[0].rules[1].operations: Required value`,
+ expectedError: `externalAdmissionHooks[0].rules[0].operations: Required value, externalAdmissionHooks[0].rules[1].operations: Required value`,
},
{
name: "\"\" is NOT a valid operation",
@@ -428,7 +345,7 @@ func TestValidateExternalAdmissionHookConfiguration(t *testing.T) {
expectedError: `Unsupported value: "PATCH"`,
},
{
- name: "wildcard cannot be mixed with other strings",
+ name: "wildcard operation cannot be mixed with other strings",
config: getExternalAdmissionHookConfiguration(
[]admissionregistration.ExternalAdmissionHook{
{
@@ -447,12 +364,130 @@ func TestValidateExternalAdmissionHookConfiguration(t *testing.T) {
}),
expectedError: `if '*' is present, must not specify other operations`,
},
+ {
+ name: `resource "*" can co-exist with resources that have subresources`,
+ config: getExternalAdmissionHookConfiguration(
+ []admissionregistration.ExternalAdmissionHook{
+ {
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{
+ {
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*", "a/b", "a/*", "*/b"},
+ },
+ },
+ },
+ },
+ }),
+ },
+ {
+ name: `resource "*" cannot mix with resources that don't have subresources`,
+ config: getExternalAdmissionHookConfiguration(
+ []admissionregistration.ExternalAdmissionHook{
+ {
+ Name: "webhook.k8s.io",
+ Rules: []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: getExternalAdmissionHookConfiguration(
+ []admissionregistration.ExternalAdmissionHook{
+ {
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{
+ {
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a/x"},
+ },
+ },
+ },
+ },
+ }),
+ expectedError: `externalAdmissionHooks[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: getExternalAdmissionHookConfiguration(
+ []admissionregistration.ExternalAdmissionHook{
+ {
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{
+ {
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"a/*", "a"},
+ },
+ },
+ },
+ },
+ }),
+ },
+ {
+ name: "resource */a cannot mix with x/a",
+ config: getExternalAdmissionHookConfiguration(
+ []admissionregistration.ExternalAdmissionHook{
+ {
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{
+ {
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/a", "x/a"},
+ },
+ },
+ },
+ },
+ }),
+ expectedError: `externalAdmissionHooks[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: getExternalAdmissionHookConfiguration(
+ []admissionregistration.ExternalAdmissionHook{
+ {
+ Name: "webhook.k8s.io",
+ Rules: []admissionregistration.RuleWithOperations{
+ {
+ Operations: []admissionregistration.OperationType{"CREATE"},
+ Rule: admissionregistration.Rule{
+ APIGroups: []string{"a"},
+ APIVersions: []string{"a"},
+ Resources: []string{"*/*", "a"},
+ },
+ },
+ },
+ },
+ }),
+ expectedError: `externalAdmissionHooks[0].rules[0].resources: Invalid value: []string{"*/*", "a"}: if '*/*' is present, must not specify other resources`,
+ },
{
name: "FailurePolicy can only be \"Ignore\"",
config: getExternalAdmissionHookConfiguration(
[]admissionregistration.ExternalAdmissionHook{
{
- Name: "initializer.k8s.io",
+ Name: "webhook.k8s.io",
FailurePolicy: func() *admissionregistration.FailurePolicyType {
r := admissionregistration.Fail
return &r
From e9d9b05234b46d2419112adb2009a9802998027c Mon Sep 17 00:00:00 2001
From: Chao Xu
Date: Wed, 31 May 2017 15:36:49 -0700
Subject: [PATCH 3/3] generated
---
api/openapi-spec/swagger.json | 6 +++---
.../admissionregistration.k8s.io_v1alpha1.json | 6 +++---
.../v1alpha1/definitions.html | 16 ++++++++++------
.../v1alpha1/generated.proto | 6 +++++-
.../v1alpha1/types_swagger_doc_generated.go | 4 ++--
.../pkg/apis/admissionregistration/types.go | 6 +++++-
.../v1alpha1/generated.proto | 6 +++++-
.../apis/admissionregistration/v1alpha1/types.go | 6 +++++-
.../v1alpha1/types_swagger_doc_generated.go | 4 ++--
9 files changed, 40 insertions(+), 20 deletions(-)
diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index c9ff98d6f94..28b8351bc55 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -49367,7 +49367,7 @@
"type": "string"
},
"rules": {
- "description": "Rules describes what resources/subresources the initializer cares about. The initializer cares about an operation if it matches _any_ Rule.",
+ "description": "Rules describes what resources/subresources the initializer cares about. The initializer cares about an operation if it matches _any_ Rule. Rule.Resources must not include subresources.",
"type": "array",
"items": {
"$ref": "#/definitions/io.k8s.kubernetes.pkg.apis.admissionregistration.v1alpha1.Rule"
@@ -49460,7 +49460,7 @@
}
},
"resources": {
- "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf '*' or '*/*' is present, the length of the slice must be one. Required.",
+ "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf wildcard is present, the validation rule will ensure resources do not overlap with each other.\n\nDepending on the enclosing object, subresources might not be allowed. Required.",
"type": "array",
"items": {
"type": "string"
@@ -49493,7 +49493,7 @@
}
},
"resources": {
- "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf '*' or '*/*' is present, the length of the slice must be one. Required.",
+ "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf wildcard is present, the validation rule will ensure resources do not overlap with each other.\n\nDepending on the enclosing object, subresources might not be allowed. Required.",
"type": "array",
"items": {
"type": "string"
diff --git a/api/swagger-spec/admissionregistration.k8s.io_v1alpha1.json b/api/swagger-spec/admissionregistration.k8s.io_v1alpha1.json
index a85b226b177..6c85ef361fb 100644
--- a/api/swagger-spec/admissionregistration.k8s.io_v1alpha1.json
+++ b/api/swagger-spec/admissionregistration.k8s.io_v1alpha1.json
@@ -1663,7 +1663,7 @@
"items": {
"type": "string"
},
- "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf '*' or '*/*' is present, the length of the slice must be one. Required."
+ "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf wildcard is present, the validation rule will ensure resources do not overlap with each other.\n\nDepending on the enclosing object, subresources might not be allowed. Required."
}
}
},
@@ -1813,7 +1813,7 @@
"items": {
"$ref": "v1alpha1.Rule"
},
- "description": "Rules describes what resources/subresources the initializer cares about. The initializer cares about an operation if it matches _any_ Rule."
+ "description": "Rules describes what resources/subresources the initializer cares about. The initializer cares about an operation if it matches _any_ Rule. Rule.Resources must not include subresources."
},
"failurePolicy": {
"$ref": "v1alpha1.FailurePolicyType",
@@ -1844,7 +1844,7 @@
"items": {
"type": "string"
},
- "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf '*' or '*/*' is present, the length of the slice must be one. Required."
+ "description": "Resources is a list of resources this rule applies to.\n\nFor example: 'pods' means pods. 'pods/log' means the log subresource of pods. '*' means all resources, but not subresources. 'pods/*' means all subresources of pods. '*/scale' means all scale subresources. '*/*' means all resources and their subresources.\n\nIf wildcard is present, the validation rule will ensure resources do not overlap with each other.\n\nDepending on the enclosing object, subresources might not be allowed. Required."
}
}
},
diff --git a/docs/api-reference/admissionregistration.k8s.io/v1alpha1/definitions.html b/docs/api-reference/admissionregistration.k8s.io/v1alpha1/definitions.html
index ecd774eec48..650cb5f5540 100755
--- a/docs/api-reference/admissionregistration.k8s.io/v1alpha1/definitions.html
+++ b/docs/api-reference/admissionregistration.k8s.io/v1alpha1/definitions.html
@@ -492,9 +492,11 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
resources |
Resources is a list of resources this rule applies to.
-For example: pods means pods. pods/log means the log subresource of pods. means all resources, but not subresources. pods/ means all subresources of pods. /scale means all scale subresources. / means all resources and their subresources.
+For example: pods means pods. pods/log means the log subresource of pods. means all resources, but not subresources. pods/ means all subresources of pods. /scale means all scale subresources. /* means all resources and their subresources.
-If or / is present, the length of the slice must be one. Required. |
+If wildcard is present, the validation rule will ensure resources do not overlap with each other.
+
+Depending on the enclosing object, subresources might not be allowed. Required.
false |
string array |
|
@@ -551,9 +553,11 @@ If or / is present, the length of t
resources |
Resources is a list of resources this rule applies to.
-For example: pods means pods. pods/log means the log subresource of pods. means all resources, but not subresources. pods/ means all subresources of pods. /scale means all scale subresources. / means all resources and their subresources.
+For example: pods means pods. pods/log means the log subresource of pods. means all resources, but not subresources. pods/ means all subresources of pods. /scale means all scale subresources. /* means all resources and their subresources.
-If or / is present, the length of the slice must be one. Required. |
+If wildcard is present, the validation rule will ensure resources do not overlap with each other.
+
+Depending on the enclosing object, subresources might not be allowed. Required.
false |
string array |
|
@@ -968,7 +972,7 @@ If or / is present, the length of t
rules |
-Rules describes what resources/subresources the initializer cares about. The initializer cares about an operation if it matches any Rule. |
+Rules describes what resources/subresources the initializer cares about. The initializer cares about an operation if it matches any Rule. Rule.Resources must not include subresources. |
false |
v1alpha1.Rule array |
|
@@ -1746,7 +1750,7 @@ Examples: