Promote ValidatingAdmissionPolicy to GA.
This commit is contained in:
@@ -299,7 +299,7 @@ func CreateKubeAPIServerConfig(opts options.CompletedOptions) (
|
||||
CloudConfigFile: opts.CloudProvider.CloudConfigFile,
|
||||
}
|
||||
serviceResolver := buildServiceResolver(opts.EnableAggregatorRouting, genericConfig.LoopbackClientConfig.Host, versionedInformers)
|
||||
pluginInitializers, admissionPostStartHook, err := admissionConfig.New(proxyTransport, genericConfig.EgressSelector, serviceResolver, genericConfig.TracerProvider)
|
||||
pluginInitializers, err := admissionConfig.New(proxyTransport, genericConfig.EgressSelector, serviceResolver, genericConfig.TracerProvider)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to create admission plugin initializer: %v", err)
|
||||
}
|
||||
@@ -321,9 +321,6 @@ func CreateKubeAPIServerConfig(opts options.CompletedOptions) (
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to apply admission: %w", err)
|
||||
}
|
||||
if err := config.GenericConfig.AddPostStartHook("start-kube-apiserver-admission-initializer", admissionPostStartHook); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
if config.GenericConfig.EgressSelector != nil {
|
||||
// Use the config.GenericConfig.EgressSelector lookup to find the dialer to connect to the kubelet
|
||||
|
@@ -52,8 +52,8 @@ func startValidatingAdmissionPolicyStatusController(ctx context.Context, control
|
||||
RestMapper: controllerContext.RESTMapper,
|
||||
}
|
||||
c, err := validatingadmissionpolicystatus.NewController(
|
||||
controllerContext.InformerFactory.Admissionregistration().V1beta1().ValidatingAdmissionPolicies(),
|
||||
controllerContext.ClientBuilder.ClientOrDie(names.ValidatingAdmissionPolicyStatusController).AdmissionregistrationV1beta1().ValidatingAdmissionPolicies(),
|
||||
controllerContext.InformerFactory.Admissionregistration().V1().ValidatingAdmissionPolicies(),
|
||||
controllerContext.ClientBuilder.ClientOrDie(names.ValidatingAdmissionPolicyStatusController).AdmissionregistrationV1().ValidatingAdmissionPolicies(),
|
||||
typeChecker,
|
||||
)
|
||||
|
||||
|
@@ -151,6 +151,10 @@ func TestDefaulting(t *testing.T) {
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicyList"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicyBinding"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1beta1", Kind: "ValidatingAdmissionPolicyBindingList"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingAdmissionPolicy"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingAdmissionPolicyList"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingAdmissionPolicyBinding"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingAdmissionPolicyBindingList"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfiguration"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "ValidatingWebhookConfigurationList"}: {},
|
||||
{Group: "admissionregistration.k8s.io", Version: "v1", Kind: "MutatingWebhookConfiguration"}: {},
|
||||
|
@@ -93,3 +93,27 @@ func SetDefaults_ServiceReference(obj *admissionregistrationv1.ServiceReference)
|
||||
obj.Port = utilpointer.Int32(443)
|
||||
}
|
||||
}
|
||||
|
||||
// SetDefaults_ValidatingAdmissionPolicySpec sets defaults for ValidatingAdmissionPolicySpec
|
||||
func SetDefaults_ValidatingAdmissionPolicySpec(obj *admissionregistrationv1.ValidatingAdmissionPolicySpec) {
|
||||
if obj.FailurePolicy == nil {
|
||||
policy := admissionregistrationv1.Fail
|
||||
obj.FailurePolicy = &policy
|
||||
}
|
||||
}
|
||||
|
||||
// SetDefaults_MatchResources sets defaults for MatchResources
|
||||
func SetDefaults_MatchResources(obj *admissionregistrationv1.MatchResources) {
|
||||
if obj.MatchPolicy == nil {
|
||||
policy := admissionregistrationv1.Equivalent
|
||||
obj.MatchPolicy = &policy
|
||||
}
|
||||
if obj.NamespaceSelector == nil {
|
||||
selector := metav1.LabelSelector{}
|
||||
obj.NamespaceSelector = &selector
|
||||
}
|
||||
if obj.ObjectSelector == nil {
|
||||
selector := metav1.LabelSelector{}
|
||||
obj.ObjectSelector = &selector
|
||||
}
|
||||
}
|
||||
|
@@ -132,3 +132,91 @@ func TestDefaultAdmissionWebhook(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultAdmissionPolicy(t *testing.T) {
|
||||
fail := v1.Fail
|
||||
equivalent := v1.Equivalent
|
||||
allScopes := v1.AllScopes
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
original runtime.Object
|
||||
expected runtime.Object
|
||||
}{
|
||||
{
|
||||
name: "ValidatingAdmissionPolicy",
|
||||
original: &v1.ValidatingAdmissionPolicy{
|
||||
Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1.MatchResources{},
|
||||
},
|
||||
},
|
||||
expected: &v1.ValidatingAdmissionPolicy{
|
||||
Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1.MatchResources{
|
||||
MatchPolicy: &equivalent,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
},
|
||||
FailurePolicy: &fail,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ValidatingAdmissionPolicyBinding",
|
||||
original: &v1.ValidatingAdmissionPolicyBinding{
|
||||
Spec: v1.ValidatingAdmissionPolicyBindingSpec{
|
||||
MatchResources: &v1.MatchResources{},
|
||||
},
|
||||
},
|
||||
expected: &v1.ValidatingAdmissionPolicyBinding{
|
||||
Spec: v1.ValidatingAdmissionPolicyBindingSpec{
|
||||
MatchResources: &v1.MatchResources{
|
||||
MatchPolicy: &equivalent,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "scope=*",
|
||||
original: &v1.ValidatingAdmissionPolicy{
|
||||
Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1.MatchResources{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &v1.ValidatingAdmissionPolicy{
|
||||
Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1.MatchResources{
|
||||
MatchPolicy: &equivalent,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
Scope: &allScopes, // defaulted
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
FailurePolicy: &fail,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
original := test.original
|
||||
expected := test.expected
|
||||
legacyscheme.Scheme.Default(original)
|
||||
if !apiequality.Semantic.DeepEqual(original, expected) {
|
||||
t.Error(cmp.Diff(expected, original))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -21,15 +21,15 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
validatingadmissionpolicy "k8s.io/apiserver/pkg/admission/plugin/policy/validating"
|
||||
admissionregistrationv1beta1apply "k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1"
|
||||
informerv1beta1 "k8s.io/client-go/informers/admissionregistration/v1beta1"
|
||||
admissionregistrationv1beta1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1"
|
||||
admissionregistrationv1apply "k8s.io/client-go/applyconfigurations/admissionregistration/v1"
|
||||
informerv1 "k8s.io/client-go/informers/admissionregistration/v1"
|
||||
admissionregistrationv1 "k8s.io/client-go/kubernetes/typed/admissionregistration/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
)
|
||||
@@ -40,10 +40,10 @@ const ControllerName = "validatingadmissionpolicy-status"
|
||||
// Controller is the ValidatingAdmissionPolicy Status controller that reconciles the Status field of each policy object.
|
||||
// This controller runs type checks against referred types for each policy definition.
|
||||
type Controller struct {
|
||||
policyInformer informerv1beta1.ValidatingAdmissionPolicyInformer
|
||||
policyInformer informerv1.ValidatingAdmissionPolicyInformer
|
||||
policyQueue workqueue.RateLimitingInterface
|
||||
policySynced cache.InformerSynced
|
||||
policyClient admissionregistrationv1beta1.ValidatingAdmissionPolicyInterface
|
||||
policyClient admissionregistrationv1.ValidatingAdmissionPolicyInterface
|
||||
|
||||
// typeChecker checks the policy's expressions for type errors.
|
||||
// Type of params is defined in policy.Spec.ParamsKind
|
||||
@@ -66,7 +66,7 @@ func (c *Controller) Run(ctx context.Context, workers int) {
|
||||
<-ctx.Done()
|
||||
}
|
||||
|
||||
func NewController(policyInformer informerv1beta1.ValidatingAdmissionPolicyInformer, policyClient admissionregistrationv1beta1.ValidatingAdmissionPolicyInterface, typeChecker *validatingadmissionpolicy.TypeChecker) (*Controller, error) {
|
||||
func NewController(policyInformer informerv1.ValidatingAdmissionPolicyInformer, policyClient admissionregistrationv1.ValidatingAdmissionPolicyInterface, typeChecker *validatingadmissionpolicy.TypeChecker) (*Controller, error) {
|
||||
c := &Controller{
|
||||
policyInformer: policyInformer,
|
||||
policyQueue: workqueue.NewRateLimitingQueueWithConfig(workqueue.DefaultControllerRateLimiter(), workqueue.RateLimitingQueueConfig{Name: ControllerName}),
|
||||
@@ -89,7 +89,7 @@ func NewController(policyInformer informerv1beta1.ValidatingAdmissionPolicyInfor
|
||||
}
|
||||
|
||||
func (c *Controller) enqueuePolicy(policy any) {
|
||||
if policy, ok := policy.(*v1beta1.ValidatingAdmissionPolicy); ok {
|
||||
if policy, ok := policy.(*v1.ValidatingAdmissionPolicy); ok {
|
||||
// policy objects are cluster-scoped, no point include its namespace.
|
||||
key := policy.ObjectMeta.Name
|
||||
if key == "" {
|
||||
@@ -138,7 +138,7 @@ func (c *Controller) processNextWorkItem(ctx context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Controller) reconcile(ctx context.Context, policy *v1beta1.ValidatingAdmissionPolicy) error {
|
||||
func (c *Controller) reconcile(ctx context.Context, policy *v1.ValidatingAdmissionPolicy) error {
|
||||
if policy == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -146,16 +146,16 @@ func (c *Controller) reconcile(ctx context.Context, policy *v1beta1.ValidatingAd
|
||||
return nil
|
||||
}
|
||||
warnings := c.typeChecker.Check(policy)
|
||||
warningsConfig := make([]*admissionregistrationv1beta1apply.ExpressionWarningApplyConfiguration, 0, len(warnings))
|
||||
warningsConfig := make([]*admissionregistrationv1apply.ExpressionWarningApplyConfiguration, 0, len(warnings))
|
||||
for _, warning := range warnings {
|
||||
warningsConfig = append(warningsConfig, admissionregistrationv1beta1apply.ExpressionWarning().
|
||||
warningsConfig = append(warningsConfig, admissionregistrationv1apply.ExpressionWarning().
|
||||
WithFieldRef(warning.FieldRef).
|
||||
WithWarning(warning.Warning))
|
||||
}
|
||||
applyConfig := admissionregistrationv1beta1apply.ValidatingAdmissionPolicy(policy.Name).
|
||||
WithStatus(admissionregistrationv1beta1apply.ValidatingAdmissionPolicyStatus().
|
||||
applyConfig := admissionregistrationv1apply.ValidatingAdmissionPolicy(policy.Name).
|
||||
WithStatus(admissionregistrationv1apply.ValidatingAdmissionPolicyStatus().
|
||||
WithObservedGeneration(policy.Generation).
|
||||
WithTypeChecking(admissionregistrationv1beta1apply.TypeChecking().
|
||||
WithTypeChecking(admissionregistrationv1apply.TypeChecking().
|
||||
WithExpressionWarnings(warningsConfig...)))
|
||||
_, err := c.policyClient.ApplyStatus(ctx, applyConfig, metav1.ApplyOptions{FieldManager: ControllerName, Force: true})
|
||||
return err
|
||||
|
@@ -23,7 +23,6 @@ import (
|
||||
"time"
|
||||
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/meta/testrestmapper"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
@@ -38,13 +37,13 @@ import (
|
||||
func TestTypeChecking(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy
|
||||
assertFieldRef func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) // warning.fieldRef
|
||||
assertWarnings func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) // warning.warning
|
||||
policy *admissionregistrationv1.ValidatingAdmissionPolicy
|
||||
assertFieldRef func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) // warning.fieldRef
|
||||
assertWarnings func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) // warning.warning
|
||||
}{
|
||||
{
|
||||
name: "deployment with correct expression",
|
||||
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1beta1.Validation{
|
||||
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1.Validation{
|
||||
{
|
||||
Expression: "object.spec.replicas > 1",
|
||||
},
|
||||
@@ -54,7 +53,7 @@ func TestTypeChecking(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "deployment with type confusion",
|
||||
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1beta1.Validation{
|
||||
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1.Validation{
|
||||
{
|
||||
Expression: "object.spec.replicas < 100", // this one passes
|
||||
},
|
||||
@@ -67,7 +66,7 @@ func TestTypeChecking(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "two expressions different type checking errors",
|
||||
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1beta1.Validation{
|
||||
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1.Validation{
|
||||
{
|
||||
Expression: "object.spec.nonExistingFirst > 1",
|
||||
},
|
||||
@@ -83,7 +82,7 @@ func TestTypeChecking(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "one expression, two warnings",
|
||||
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1beta1.Validation{
|
||||
policy: withGVRMatch([]string{"apps"}, []string{"v1"}, []string{"deployments"}, withValidations([]admissionregistrationv1.Validation{
|
||||
{
|
||||
Expression: "object.spec.replicas < 100", // this one passes
|
||||
},
|
||||
@@ -107,8 +106,8 @@ func TestTypeChecking(t *testing.T) {
|
||||
RestMapper: testrestmapper.TestOnlyStaticRESTMapper(scheme.Scheme),
|
||||
}
|
||||
controller, err := NewController(
|
||||
informerFactory.Admissionregistration().V1beta1().ValidatingAdmissionPolicies(),
|
||||
client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies(),
|
||||
informerFactory.Admissionregistration().V1().ValidatingAdmissionPolicies(),
|
||||
client.AdmissionregistrationV1().ValidatingAdmissionPolicies(),
|
||||
typeChecker,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -120,7 +119,7 @@ func TestTypeChecking(t *testing.T) {
|
||||
name := policy.Name
|
||||
// wait until the typeChecking is set, which means the type checking
|
||||
// is complete.
|
||||
updated, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Get(ctx, name, metav1.GetOptions{})
|
||||
updated, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Get(ctx, name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -143,8 +142,8 @@ func TestTypeChecking(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func toBe(expected ...string) func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) {
|
||||
func toBe(expected ...string) func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) {
|
||||
if len(expected) != len(warnings) {
|
||||
t.Fatalf("mismatched length, expect %d, got %d", len(expected), len(warnings))
|
||||
}
|
||||
@@ -156,8 +155,8 @@ func toBe(expected ...string) func(warnings []admissionregistrationv1beta1.Expre
|
||||
}
|
||||
}
|
||||
|
||||
func toHaveSubstring(substrings ...string) func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) {
|
||||
func toHaveSubstring(substrings ...string) func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) {
|
||||
if len(substrings) != len(warnings) {
|
||||
t.Fatalf("mismatched length, expect %d, got %d", len(substrings), len(warnings))
|
||||
}
|
||||
@@ -169,8 +168,8 @@ func toHaveSubstring(substrings ...string) func(warnings []admissionregistration
|
||||
}
|
||||
}
|
||||
|
||||
func toHaveMultipleSubstrings(substrings ...[]string) func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) {
|
||||
func toHaveMultipleSubstrings(substrings ...[]string) func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) {
|
||||
if len(substrings) != len(warnings) {
|
||||
t.Fatalf("mismatched length, expect %d, got %d", len(substrings), len(warnings))
|
||||
}
|
||||
@@ -184,19 +183,19 @@ func toHaveMultipleSubstrings(substrings ...[]string) func(warnings []admissionr
|
||||
}
|
||||
}
|
||||
|
||||
func toHaveLengthOf(n int) func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []admissionregistrationv1beta1.ExpressionWarning, t *testing.T) {
|
||||
func toHaveLengthOf(n int) func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []admissionregistrationv1.ExpressionWarning, t *testing.T) {
|
||||
if n != len(warnings) {
|
||||
t.Fatalf("mismatched length, expect %d, got %d", n, len(warnings))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func withGVRMatch(groups []string, versions []string, resources []string, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
|
||||
policy.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{
|
||||
ResourceRules: []admissionregistrationv1beta1.NamedRuleWithOperations{
|
||||
func withGVRMatch(groups []string, versions []string, resources []string, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
|
||||
policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{
|
||||
ResourceRules: []admissionregistrationv1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
|
||||
RuleWithOperations: admissionregistrationv1.RuleWithOperations{
|
||||
Operations: []admissionregistrationv1.OperationType{
|
||||
"*",
|
||||
},
|
||||
@@ -212,13 +211,13 @@ func withGVRMatch(groups []string, versions []string, resources []string, policy
|
||||
return policy
|
||||
}
|
||||
|
||||
func withValidations(validations []admissionregistrationv1beta1.Validation, policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
|
||||
func withValidations(validations []admissionregistrationv1.Validation, policy *admissionregistrationv1.ValidatingAdmissionPolicy) *admissionregistrationv1.ValidatingAdmissionPolicy {
|
||||
policy.Spec.Validations = validations
|
||||
return policy
|
||||
}
|
||||
|
||||
func makePolicy(name string) *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
|
||||
return &admissionregistrationv1beta1.ValidatingAdmissionPolicy{
|
||||
func makePolicy(name string) *admissionregistrationv1.ValidatingAdmissionPolicy {
|
||||
return &admissionregistrationv1.ValidatingAdmissionPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name},
|
||||
}
|
||||
}
|
||||
|
@@ -81,6 +81,8 @@ var GVRToStorageVersionHash = map[string]string{
|
||||
"apps/v1/statefulsets": "H+vl74LkKdo=",
|
||||
"admissionregistration.k8s.io/v1/mutatingwebhookconfigurations": "Sqi0GUgDaX0=",
|
||||
"admissionregistration.k8s.io/v1/validatingwebhookconfigurations": "B0wHjQmsGNk=",
|
||||
"admissionregistration.k8s.io/v1/validatingadmissionpolicies": "P/h9c6yIbaY=",
|
||||
"admissionregistration.k8s.io/v1/validatingadmissionpolicybindings": "XYju31JKYek=",
|
||||
"events.k8s.io/v1/events": "r2yiGXH7wu8=",
|
||||
"flowcontrol.apiserver.k8s.io/v1beta3/flowschemas": "GJVAJZSZBIw=",
|
||||
"flowcontrol.apiserver.k8s.io/v1beta3/prioritylevelconfigurations": "Kir5PVfvNeI=",
|
||||
|
@@ -1258,7 +1258,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
|
||||
genericfeatures.UnauthenticatedHTTP2DOSMitigation: {Default: true, PreRelease: featuregate.Beta},
|
||||
|
||||
genericfeatures.ValidatingAdmissionPolicy: {Default: false, PreRelease: featuregate.Beta},
|
||||
genericfeatures.ValidatingAdmissionPolicy: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32
|
||||
|
||||
genericfeatures.WatchBookmark: {Default: true, PreRelease: featuregate.GA, LockToDefault: true},
|
||||
|
||||
|
@@ -19,23 +19,17 @@ package admission
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
utilwait "k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
webhookinit "k8s.io/apiserver/pkg/admission/plugin/webhook/initializer"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/egressselector"
|
||||
"k8s.io/apiserver/pkg/util/webhook"
|
||||
cacheddiscovery "k8s.io/client-go/discovery/cached/memory"
|
||||
externalinformers "k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
"k8s.io/kubernetes/pkg/kubeapiserver/admission/exclusion"
|
||||
quotainstall "k8s.io/kubernetes/pkg/quota/v1/install"
|
||||
)
|
||||
@@ -48,7 +42,7 @@ type Config struct {
|
||||
}
|
||||
|
||||
// New sets up the plugins and admission start hooks needed for admission
|
||||
func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselector.EgressSelector, serviceResolver webhook.ServiceResolver, tp trace.TracerProvider) ([]admission.PluginInitializer, genericapiserver.PostStartHookFunc, error) {
|
||||
func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselector.EgressSelector, serviceResolver webhook.ServiceResolver, tp trace.TracerProvider) ([]admission.PluginInitializer, error) {
|
||||
webhookAuthResolverWrapper := webhook.NewDefaultAuthenticationInfoResolverWrapper(proxyTransport, egressSelector, c.LoopbackClientConfig, tp)
|
||||
webhookPluginInitializer := webhookinit.NewPluginInitializer(webhookAuthResolverWrapper, serviceResolver)
|
||||
|
||||
@@ -60,24 +54,11 @@ func (c *Config) New(proxyTransport *http.Transport, egressSelector *egressselec
|
||||
klog.Fatalf("Error reading from cloud configuration file %s: %#v", c.CloudConfigFile, err)
|
||||
}
|
||||
}
|
||||
clientset, err := kubernetes.NewForConfig(c.LoopbackClientConfig)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
discoveryClient := cacheddiscovery.NewMemCacheClient(clientset.Discovery())
|
||||
discoveryRESTMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient)
|
||||
kubePluginInitializer := NewPluginInitializer(
|
||||
cloudConfig,
|
||||
discoveryRESTMapper,
|
||||
quotainstall.NewQuotaConfigurationForAdmission(),
|
||||
exclusion.Excluded(),
|
||||
)
|
||||
|
||||
admissionPostStartHook := func(context genericapiserver.PostStartHookContext) error {
|
||||
discoveryRESTMapper.Reset()
|
||||
go utilwait.Until(discoveryRESTMapper.Reset, 30*time.Second, context.StopCh)
|
||||
return nil
|
||||
}
|
||||
|
||||
return []admission.PluginInitializer{webhookPluginInitializer, kubePluginInitializer}, admissionPostStartHook, nil
|
||||
return []admission.PluginInitializer{webhookPluginInitializer, kubePluginInitializer}, nil
|
||||
}
|
||||
|
@@ -17,7 +17,6 @@ limitations under the License.
|
||||
package admission
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/initializer"
|
||||
@@ -34,7 +33,6 @@ type WantsCloudConfig interface {
|
||||
// PluginInitializer is used for initialization of the Kubernetes specific admission plugins.
|
||||
type PluginInitializer struct {
|
||||
cloudConfig []byte
|
||||
restMapper meta.RESTMapper
|
||||
quotaConfiguration quota.Configuration
|
||||
excludedAdmissionResources []schema.GroupResource
|
||||
}
|
||||
@@ -46,13 +44,11 @@ var _ admission.PluginInitializer = &PluginInitializer{}
|
||||
// all public, this construction method is pointless boilerplate.
|
||||
func NewPluginInitializer(
|
||||
cloudConfig []byte,
|
||||
restMapper meta.RESTMapper,
|
||||
quotaConfiguration quota.Configuration,
|
||||
excludedAdmissionResources []schema.GroupResource,
|
||||
) *PluginInitializer {
|
||||
return &PluginInitializer{
|
||||
cloudConfig: cloudConfig,
|
||||
restMapper: restMapper,
|
||||
quotaConfiguration: quotaConfiguration,
|
||||
excludedAdmissionResources: excludedAdmissionResources,
|
||||
}
|
||||
@@ -65,10 +61,6 @@ func (i *PluginInitializer) Initialize(plugin admission.Interface) {
|
||||
wants.SetCloudConfig(i.cloudConfig)
|
||||
}
|
||||
|
||||
if wants, ok := plugin.(initializer.WantsRESTMapper); ok {
|
||||
wants.SetRESTMapper(i.restMapper)
|
||||
}
|
||||
|
||||
if wants, ok := plugin.(initializer.WantsQuotaConfiguration); ok {
|
||||
wants.SetQuotaConfiguration(i.quotaConfiguration)
|
||||
}
|
||||
|
@@ -20,7 +20,6 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
quota "k8s.io/apiserver/pkg/quota/v1"
|
||||
@@ -49,7 +48,7 @@ func (p *WantsCloudConfigAdmissionPlugin) SetCloudConfig(cloudConfig []byte) {
|
||||
|
||||
func TestCloudConfigAdmissionPlugin(t *testing.T) {
|
||||
cloudConfig := []byte("cloud-configuration")
|
||||
initializer := NewPluginInitializer(cloudConfig, nil, nil, nil)
|
||||
initializer := NewPluginInitializer(cloudConfig, nil, nil)
|
||||
wantsCloudConfigAdmission := &WantsCloudConfigAdmissionPlugin{}
|
||||
initializer.Initialize(wantsCloudConfigAdmission)
|
||||
|
||||
@@ -58,51 +57,6 @@ func TestCloudConfigAdmissionPlugin(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type doNothingRESTMapper struct{}
|
||||
|
||||
func (doNothingRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
|
||||
return schema.GroupVersionKind{}, nil
|
||||
}
|
||||
func (doNothingRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (doNothingRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
|
||||
return schema.GroupVersionResource{}, nil
|
||||
}
|
||||
func (doNothingRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (doNothingRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (doNothingRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (doNothingRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
type WantsRESTMapperAdmissionPlugin struct {
|
||||
doNothingAdmission
|
||||
doNothingPluginInitialization
|
||||
mapper meta.RESTMapper
|
||||
}
|
||||
|
||||
func (p *WantsRESTMapperAdmissionPlugin) SetRESTMapper(mapper meta.RESTMapper) {
|
||||
p.mapper = mapper
|
||||
}
|
||||
|
||||
func TestRESTMapperAdmissionPlugin(t *testing.T) {
|
||||
mapper := doNothingRESTMapper{}
|
||||
initializer := NewPluginInitializer(nil, mapper, nil, nil)
|
||||
wantsRESTMapperAdmission := &WantsRESTMapperAdmissionPlugin{}
|
||||
initializer.Initialize(wantsRESTMapperAdmission)
|
||||
|
||||
if wantsRESTMapperAdmission.mapper == nil {
|
||||
t.Errorf("Expected REST mapper to be initialized but found nil")
|
||||
}
|
||||
}
|
||||
|
||||
type doNothingQuotaConfiguration struct{}
|
||||
|
||||
func (doNothingQuotaConfiguration) IgnoredResources() map[schema.GroupResource]struct{} { return nil }
|
||||
@@ -121,7 +75,7 @@ func (p *WantsQuotaConfigurationAdmissionPlugin) SetQuotaConfiguration(config qu
|
||||
|
||||
func TestQuotaConfigurationAdmissionPlugin(t *testing.T) {
|
||||
config := doNothingQuotaConfiguration{}
|
||||
initializer := NewPluginInitializer(nil, nil, config, nil)
|
||||
initializer := NewPluginInitializer(nil, config, nil)
|
||||
wantsQuotaConfigurationAdmission := &WantsQuotaConfigurationAdmissionPlugin{}
|
||||
initializer.Initialize(wantsQuotaConfigurationAdmission)
|
||||
|
||||
|
@@ -68,6 +68,14 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
|
||||
func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
|
||||
storage := map[string]rest.Storage{}
|
||||
|
||||
// use a simple wrapper so that initialization order won't cause a nil getter
|
||||
var policyGetter rest.Getter
|
||||
|
||||
r, err := resolver.NewDiscoveryResourceResolver(p.DiscoveryClient)
|
||||
if err != nil {
|
||||
return storage, err
|
||||
}
|
||||
|
||||
// validatingwebhookconfigurations
|
||||
if resource := "validatingwebhookconfigurations"; apiResourceConfigSource.ResourceEnabled(admissionregistrationv1.SchemeGroupVersion.WithResource(resource)) {
|
||||
validatingStorage, err := validatingwebhookconfigurationstorage.NewREST(restOptionsGetter)
|
||||
@@ -86,6 +94,26 @@ func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.API
|
||||
storage[resource] = mutatingStorage
|
||||
}
|
||||
|
||||
// validatingadmissionpolicies
|
||||
if resource := "validatingadmissionpolicies"; apiResourceConfigSource.ResourceEnabled(admissionregistrationv1.SchemeGroupVersion.WithResource(resource)) {
|
||||
policyStorage, policyStatusStorage, err := validatingadmissionpolicystorage.NewREST(restOptionsGetter, p.Authorizer, r)
|
||||
if err != nil {
|
||||
return storage, err
|
||||
}
|
||||
policyGetter = policyStorage
|
||||
storage[resource] = policyStorage
|
||||
storage[resource+"/status"] = policyStatusStorage
|
||||
}
|
||||
|
||||
// validatingadmissionpolicybindings
|
||||
if resource := "validatingadmissionpolicybindings"; apiResourceConfigSource.ResourceEnabled(admissionregistrationv1.SchemeGroupVersion.WithResource(resource)) {
|
||||
policyBindingStorage, err := policybindingstorage.NewREST(restOptionsGetter, p.Authorizer, &policybindingstorage.DefaultPolicyGetter{Getter: policyGetter}, r)
|
||||
if err != nil {
|
||||
return storage, err
|
||||
}
|
||||
storage[resource] = policyBindingStorage
|
||||
}
|
||||
|
||||
return storage, nil
|
||||
}
|
||||
|
||||
|
@@ -138,6 +138,9 @@ func (v *validatingAdmissionPolicyStrategy) GetResetFields() map[fieldpath.APIVe
|
||||
"admissionregistration.k8s.io/v1beta1": fieldpath.NewSet(
|
||||
fieldpath.MakePathOrDie("status"),
|
||||
),
|
||||
"admissionregistration.k8s.io/v1": fieldpath.NewSet(
|
||||
fieldpath.MakePathOrDie("status"),
|
||||
),
|
||||
}
|
||||
|
||||
return fields
|
||||
@@ -177,6 +180,10 @@ func (s *validatingAdmissionPolicyStatusStrategy) GetResetFields() map[fieldpath
|
||||
fieldpath.MakePathOrDie("spec"),
|
||||
fieldpath.MakePathOrDie("metadata"),
|
||||
),
|
||||
"admissionregistration.k8s.io/v1": fieldpath.NewSet(
|
||||
fieldpath.MakePathOrDie("spec"),
|
||||
fieldpath.MakePathOrDie("metadata"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -115,7 +115,6 @@ func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) {
|
||||
whiteList: whiteList,
|
||||
}
|
||||
|
||||
genericPluginInitializer := initializer.New(nil, nil, nil, fakeAuthorizer{}, nil, nil)
|
||||
fakeDiscoveryClient := &fakediscovery.FakeDiscovery{Fake: &coretesting.Fake{}}
|
||||
fakeDiscoveryClient.Resources = []*metav1.APIResourceList{
|
||||
{
|
||||
@@ -133,13 +132,14 @@ func newGCPermissionsEnforcement() (*gcPermissionsEnforcement, error) {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
restMapperRes, err := restmapper.GetAPIGroupResources(fakeDiscoveryClient)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unexpected error while constructing resource list from fake discovery client: %v", err)
|
||||
}
|
||||
restMapper := restmapper.NewDiscoveryRESTMapper(restMapperRes)
|
||||
pluginInitializer := kubeadmission.NewPluginInitializer(nil, restMapper, nil, nil)
|
||||
genericPluginInitializer := initializer.New(nil, nil, nil, fakeAuthorizer{}, nil, nil, restMapper)
|
||||
|
||||
pluginInitializer := kubeadmission.NewPluginInitializer(nil, nil, nil)
|
||||
initializersChain := admission.PluginInitializers{}
|
||||
initializersChain = append(initializersChain, genericPluginInitializer)
|
||||
initializersChain = append(initializersChain, pluginInitializer)
|
||||
|
@@ -817,7 +817,7 @@ func newHandlerForTest(c clientset.Interface) (*LimitRanger, informers.SharedInf
|
||||
if err != nil {
|
||||
return nil, f, err
|
||||
}
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil)
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
|
||||
pluginInitializer.Initialize(handler)
|
||||
err = admission.ValidateInitialization(handler)
|
||||
return handler, f, err
|
||||
|
@@ -41,7 +41,7 @@ import (
|
||||
func newHandlerForTest(c clientset.Interface) (admission.MutationInterface, informers.SharedInformerFactory, error) {
|
||||
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
|
||||
handler := NewProvision()
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil)
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
|
||||
pluginInitializer.Initialize(handler)
|
||||
err := admission.ValidateInitialization(handler)
|
||||
return handler, f, err
|
||||
|
@@ -39,7 +39,7 @@ import (
|
||||
func newHandlerForTest(c kubernetes.Interface) (admission.ValidationInterface, informers.SharedInformerFactory, error) {
|
||||
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
|
||||
handler := NewExists()
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil)
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
|
||||
pluginInitializer.Initialize(handler)
|
||||
err := admission.ValidateInitialization(handler)
|
||||
return handler, f, err
|
||||
|
@@ -198,7 +198,7 @@ func TestHandles(t *testing.T) {
|
||||
func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInformerFactory, error) {
|
||||
f := informers.NewSharedInformerFactory(c, 5*time.Minute)
|
||||
handler := NewPodNodeSelector(nil)
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil)
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
|
||||
pluginInitializer.Initialize(handler)
|
||||
err := admission.ValidateInitialization(handler)
|
||||
return handler, f, err
|
||||
|
@@ -355,7 +355,7 @@ func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInforme
|
||||
return nil, nil, err
|
||||
}
|
||||
handler := NewPodTolerationsPlugin(pluginConfig)
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil)
|
||||
pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
|
||||
pluginInitializer.Initialize(handler)
|
||||
err = admission.ValidateInitialization(handler)
|
||||
return handler, f, err
|
||||
|
@@ -114,8 +114,8 @@ func createHandlerWithConfig(kubeClient kubernetes.Interface, informerFactory in
|
||||
}
|
||||
|
||||
initializers := admission.PluginInitializers{
|
||||
genericadmissioninitializer.New(kubeClient, nil, informerFactory, nil, nil, stopCh),
|
||||
kubeapiserveradmission.NewPluginInitializer(nil, nil, quotaConfiguration, nil),
|
||||
genericadmissioninitializer.New(kubeClient, nil, informerFactory, nil, nil, stopCh, nil),
|
||||
kubeapiserveradmission.NewPluginInitializer(nil, quotaConfiguration, nil),
|
||||
}
|
||||
initializers.Initialize(handler)
|
||||
|
||||
|
@@ -544,5 +544,22 @@ items:
|
||||
- kind: ServiceAccount
|
||||
name: ttl-controller
|
||||
namespace: kube-system
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:validatingadmissionpolicy-status-controller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:controller:validatingadmissionpolicy-status-controller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: validatingadmissionpolicy-status-controller
|
||||
namespace: kube-system
|
||||
kind: List
|
||||
metadata: {}
|
||||
|
@@ -1543,5 +1543,40 @@ items:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
- apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
name: system:controller:validatingadmissionpolicy-status-controller
|
||||
rules:
|
||||
- apiGroups:
|
||||
- admissionregistration.k8s.io
|
||||
resources:
|
||||
- validatingadmissionpolicies
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- admissionregistration.k8s.io
|
||||
resources:
|
||||
- validatingadmissionpolicies/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- ""
|
||||
- events.k8s.io
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
- update
|
||||
kind: List
|
||||
metadata: {}
|
||||
|
@@ -50,6 +50,10 @@ func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
&ValidatingWebhookConfigurationList{},
|
||||
&MutatingWebhookConfiguration{},
|
||||
&MutatingWebhookConfigurationList{},
|
||||
&ValidatingAdmissionPolicy{},
|
||||
&ValidatingAdmissionPolicyList{},
|
||||
&ValidatingAdmissionPolicyBinding{},
|
||||
&ValidatingAdmissionPolicyBindingList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
|
@@ -91,6 +91,18 @@ const (
|
||||
Fail FailurePolicyType = "Fail"
|
||||
)
|
||||
|
||||
// ParameterNotFoundActionType specifies a failure policy that defines how a binding
|
||||
// is evaluated when the param referred by its perNamespaceParamRef is not found.
|
||||
type ParameterNotFoundActionType string
|
||||
|
||||
const (
|
||||
// Allow means all requests will be admitted if no param resources
|
||||
// could be found.
|
||||
AllowAction ParameterNotFoundActionType = "Allow"
|
||||
// Deny means all requests will be denied if no param resources are found.
|
||||
DenyAction ParameterNotFoundActionType = "Deny"
|
||||
)
|
||||
|
||||
// MatchPolicyType specifies the type of match policy.
|
||||
// +enum
|
||||
type MatchPolicyType string
|
||||
@@ -120,6 +132,584 @@ const (
|
||||
SideEffectClassNoneOnDryRun SideEffectClass = "NoneOnDryRun"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +k8s:prerelease-lifecycle-gen:introduced=1.30
|
||||
// ValidatingAdmissionPolicy describes the definition of an admission validation policy that accepts or rejects an object without changing it.
|
||||
type ValidatingAdmissionPolicy struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
// Specification of the desired behavior of the ValidatingAdmissionPolicy.
|
||||
Spec ValidatingAdmissionPolicySpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
|
||||
// The status of the ValidatingAdmissionPolicy, including warnings that are useful to determine if the policy
|
||||
// behaves in the expected way.
|
||||
// Populated by the system.
|
||||
// Read-only.
|
||||
// +optional
|
||||
Status ValidatingAdmissionPolicyStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
|
||||
}
|
||||
|
||||
// ValidatingAdmissionPolicyStatus represents the status of an admission validation policy.
|
||||
type ValidatingAdmissionPolicyStatus struct {
|
||||
// The generation observed by the controller.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty" protobuf:"varint,1,opt,name=observedGeneration"`
|
||||
// The results of type checking for each expression.
|
||||
// Presence of this field indicates the completion of the type checking.
|
||||
// +optional
|
||||
TypeChecking *TypeChecking `json:"typeChecking,omitempty" protobuf:"bytes,2,opt,name=typeChecking"`
|
||||
// The conditions represent the latest available observations of a policy's current state.
|
||||
// +optional
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty" protobuf:"bytes,3,rep,name=conditions"`
|
||||
}
|
||||
|
||||
// ValidatingAdmissionPolicyConditionType is the condition type of admission validation policy.
|
||||
type ValidatingAdmissionPolicyConditionType string
|
||||
|
||||
// TypeChecking contains results of type checking the expressions in the
|
||||
// ValidatingAdmissionPolicy
|
||||
type TypeChecking struct {
|
||||
// The type checking warnings for each expression.
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
ExpressionWarnings []ExpressionWarning `json:"expressionWarnings,omitempty" protobuf:"bytes,1,rep,name=expressionWarnings"`
|
||||
}
|
||||
|
||||
// ExpressionWarning is a warning information that targets a specific expression.
|
||||
type ExpressionWarning struct {
|
||||
// The path to the field that refers the expression.
|
||||
// For example, the reference to the expression of the first item of
|
||||
// validations is "spec.validations[0].expression"
|
||||
FieldRef string `json:"fieldRef" protobuf:"bytes,2,opt,name=fieldRef"`
|
||||
// The content of type checking information in a human-readable form.
|
||||
// Each line of the warning contains the type that the expression is checked
|
||||
// against, followed by the type check error from the compiler.
|
||||
Warning string `json:"warning" protobuf:"bytes,3,opt,name=warning"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +k8s:prerelease-lifecycle-gen:introduced=1.30
|
||||
// ValidatingAdmissionPolicyList is a list of ValidatingAdmissionPolicy.
|
||||
type ValidatingAdmissionPolicyList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard list metadata.
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
// List of ValidatingAdmissionPolicy.
|
||||
Items []ValidatingAdmissionPolicy `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
||||
}
|
||||
|
||||
// ValidatingAdmissionPolicySpec is the specification of the desired behavior of the AdmissionPolicy.
|
||||
type ValidatingAdmissionPolicySpec struct {
|
||||
// ParamKind specifies the kind of resources used to parameterize this policy.
|
||||
// If absent, there are no parameters for this policy and the param CEL variable will not be provided to validation expressions.
|
||||
// If ParamKind refers to a non-existent kind, this policy definition is mis-configured and the FailurePolicy is applied.
|
||||
// If paramKind is specified but paramRef is unset in ValidatingAdmissionPolicyBinding, the params variable will be null.
|
||||
// +optional
|
||||
ParamKind *ParamKind `json:"paramKind,omitempty" protobuf:"bytes,1,rep,name=paramKind"`
|
||||
|
||||
// MatchConstraints specifies what resources this policy is designed to validate.
|
||||
// The AdmissionPolicy cares about a request if it matches _all_ Constraints.
|
||||
// However, in order to prevent clusters from being put into an unstable state that cannot be recovered from via the API
|
||||
// ValidatingAdmissionPolicy cannot match ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding.
|
||||
// Required.
|
||||
MatchConstraints *MatchResources `json:"matchConstraints,omitempty" protobuf:"bytes,2,rep,name=matchConstraints"`
|
||||
|
||||
// Validations contain CEL expressions which is used to apply the validation.
|
||||
// Validations and AuditAnnotations may not both be empty; a minimum of one Validations or AuditAnnotations is
|
||||
// required.
|
||||
// +listType=atomic
|
||||
// +optional
|
||||
Validations []Validation `json:"validations,omitempty" protobuf:"bytes,3,rep,name=validations"`
|
||||
|
||||
// failurePolicy defines how to handle failures for the admission policy. Failures can
|
||||
// occur from CEL expression parse errors, type check errors, runtime errors and invalid
|
||||
// or mis-configured policy definitions or bindings.
|
||||
//
|
||||
// A policy is invalid if spec.paramKind refers to a non-existent Kind.
|
||||
// A binding is invalid if spec.paramRef.name refers to a non-existent resource.
|
||||
//
|
||||
// failurePolicy does not define how validations that evaluate to false are handled.
|
||||
//
|
||||
// When failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions
|
||||
// define how failures are enforced.
|
||||
//
|
||||
// Allowed values are Ignore or Fail. Defaults to Fail.
|
||||
// +optional
|
||||
FailurePolicy *FailurePolicyType `json:"failurePolicy,omitempty" protobuf:"bytes,4,opt,name=failurePolicy,casttype=FailurePolicyType"`
|
||||
|
||||
// auditAnnotations contains CEL expressions which are used to produce audit
|
||||
// annotations for the audit event of the API request.
|
||||
// validations and auditAnnotations may not both be empty; a least one of validations or auditAnnotations is
|
||||
// required.
|
||||
// +listType=atomic
|
||||
// +optional
|
||||
AuditAnnotations []AuditAnnotation `json:"auditAnnotations,omitempty" protobuf:"bytes,5,rep,name=auditAnnotations"`
|
||||
|
||||
// MatchConditions is a list of conditions that must be met for a request to be validated.
|
||||
// Match conditions filter requests that have already been matched by the rules,
|
||||
// namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests.
|
||||
// There are a maximum of 64 match conditions allowed.
|
||||
//
|
||||
// If a parameter object is provided, it can be accessed via the `params` handle in the same
|
||||
// manner as validation expressions.
|
||||
//
|
||||
// The exact matching logic is (in order):
|
||||
// 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.
|
||||
// 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.
|
||||
// 3. If any matchCondition evaluates to an error (but none are FALSE):
|
||||
// - If failurePolicy=Fail, reject the request
|
||||
// - If failurePolicy=Ignore, the policy is skipped
|
||||
//
|
||||
// +patchMergeKey=name
|
||||
// +patchStrategy=merge
|
||||
// +listType=map
|
||||
// +listMapKey=name
|
||||
// +optional
|
||||
MatchConditions []MatchCondition `json:"matchConditions,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,6,rep,name=matchConditions"`
|
||||
|
||||
// Variables contain definitions of variables that can be used in composition of other expressions.
|
||||
// Each variable is defined as a named CEL expression.
|
||||
// The variables defined here will be available under `variables` in other expressions of the policy
|
||||
// except MatchConditions because MatchConditions are evaluated before the rest of the policy.
|
||||
//
|
||||
// The expression of a variable can refer to other variables defined earlier in the list but not those after.
|
||||
// Thus, Variables must be sorted by the order of first appearance and acyclic.
|
||||
// +patchMergeKey=name
|
||||
// +patchStrategy=merge
|
||||
// +listType=map
|
||||
// +listMapKey=name
|
||||
// +optional
|
||||
Variables []Variable `json:"variables,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,7,rep,name=variables"`
|
||||
}
|
||||
|
||||
// ParamKind is a tuple of Group Kind and Version.
|
||||
// +structType=atomic
|
||||
type ParamKind struct {
|
||||
// APIVersion is the API group version the resources belong to.
|
||||
// In format of "group/version".
|
||||
// Required.
|
||||
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,1,rep,name=apiVersion"`
|
||||
|
||||
// Kind is the API kind the resources belong to.
|
||||
// Required.
|
||||
Kind string `json:"kind,omitempty" protobuf:"bytes,2,rep,name=kind"`
|
||||
}
|
||||
|
||||
// Validation specifies the CEL expression which is used to apply the validation.
|
||||
type Validation struct {
|
||||
// Expression represents the expression which will be evaluated by CEL.
|
||||
// ref: https://github.com/google/cel-spec
|
||||
// CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:
|
||||
//
|
||||
// - 'object' - The object from the incoming request. The value is null for DELETE requests.
|
||||
// - 'oldObject' - The existing object. The value is null for CREATE requests.
|
||||
// - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).
|
||||
// - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
|
||||
// - 'namespaceObject' - The namespace object that the incoming object belongs to. The value is null for cluster-scoped resources.
|
||||
// - 'variables' - Map of composited variables, from its name to its lazily evaluated value.
|
||||
// For example, a variable named 'foo' can be accessed as 'variables.foo'.
|
||||
// - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.
|
||||
// See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
|
||||
// - 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the
|
||||
// request resource.
|
||||
//
|
||||
// The `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the
|
||||
// object. No other metadata properties are accessible.
|
||||
//
|
||||
// Only property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible.
|
||||
// Accessible property names are escaped according to the following rules when accessed in the expression:
|
||||
// - '__' escapes to '__underscores__'
|
||||
// - '.' escapes to '__dot__'
|
||||
// - '-' escapes to '__dash__'
|
||||
// - '/' escapes to '__slash__'
|
||||
// - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:
|
||||
// "true", "false", "null", "in", "as", "break", "const", "continue", "else", "for", "function", "if",
|
||||
// "import", "let", "loop", "package", "namespace", "return".
|
||||
// Examples:
|
||||
// - Expression accessing a property named "namespace": {"Expression": "object.__namespace__ > 0"}
|
||||
// - Expression accessing a property named "x-prop": {"Expression": "object.x__dash__prop > 0"}
|
||||
// - Expression accessing a property named "redact__d": {"Expression": "object.redact__underscores__d > 0"}
|
||||
//
|
||||
// Equality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1].
|
||||
// Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:
|
||||
// - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and
|
||||
// non-intersecting elements in `Y` are appended, retaining their partial order.
|
||||
// - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values
|
||||
// are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with
|
||||
// non-intersecting keys are appended, retaining their partial order.
|
||||
// Required.
|
||||
Expression string `json:"expression" protobuf:"bytes,1,opt,name=Expression"`
|
||||
// Message represents the message displayed when validation fails. The message is required if the Expression contains
|
||||
// line breaks. The message must not contain line breaks.
|
||||
// If unset, the message is "failed rule: {Rule}".
|
||||
// e.g. "must be a URL with the host matching spec.host"
|
||||
// If the Expression contains line breaks. Message is required.
|
||||
// The message must not contain line breaks.
|
||||
// If unset, the message is "failed Expression: {Expression}".
|
||||
// +optional
|
||||
Message string `json:"message,omitempty" protobuf:"bytes,2,opt,name=message"`
|
||||
// Reason represents a machine-readable description of why this validation failed.
|
||||
// If this is the first validation in the list to fail, this reason, as well as the
|
||||
// corresponding HTTP response code, are used in the
|
||||
// HTTP response to the client.
|
||||
// The currently supported reasons are: "Unauthorized", "Forbidden", "Invalid", "RequestEntityTooLarge".
|
||||
// If not set, StatusReasonInvalid is used in the response to the client.
|
||||
// +optional
|
||||
Reason *metav1.StatusReason `json:"reason,omitempty" protobuf:"bytes,3,opt,name=reason"`
|
||||
// messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails.
|
||||
// Since messageExpression is used as a failure message, it must evaluate to a string.
|
||||
// If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails.
|
||||
// If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced
|
||||
// as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string
|
||||
// that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and
|
||||
// the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged.
|
||||
// messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'.
|
||||
// Example:
|
||||
// "object.x must be less than max ("+string(params.max)+")"
|
||||
// +optional
|
||||
MessageExpression string `json:"messageExpression,omitempty" protobuf:"bytes,4,opt,name=messageExpression"`
|
||||
}
|
||||
|
||||
// Variable is the definition of a variable that is used for composition. A variable is defined as a named expression.
|
||||
// +structType=atomic
|
||||
type Variable struct {
|
||||
// Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables.
|
||||
// The variable can be accessed in other expressions through `variables`
|
||||
// For example, if name is "foo", the variable will be available as `variables.foo`
|
||||
Name string `json:"name" protobuf:"bytes,1,opt,name=Name"`
|
||||
|
||||
// Expression is the expression that will be evaluated as the value of the variable.
|
||||
// The CEL expression has access to the same identifiers as the CEL expressions in Validation.
|
||||
Expression string `json:"expression" protobuf:"bytes,2,opt,name=Expression"`
|
||||
}
|
||||
|
||||
// AuditAnnotation describes how to produce an audit annotation for an API request.
|
||||
type AuditAnnotation struct {
|
||||
// key specifies the audit annotation key. The audit annotation keys of
|
||||
// a ValidatingAdmissionPolicy must be unique. The key must be a qualified
|
||||
// name ([A-Za-z0-9][-A-Za-z0-9_.]*) no more than 63 bytes in length.
|
||||
//
|
||||
// The key is combined with the resource name of the
|
||||
// ValidatingAdmissionPolicy to construct an audit annotation key:
|
||||
// "{ValidatingAdmissionPolicy name}/{key}".
|
||||
//
|
||||
// If an admission webhook uses the same resource name as this ValidatingAdmissionPolicy
|
||||
// and the same audit annotation key, the annotation key will be identical.
|
||||
// In this case, the first annotation written with the key will be included
|
||||
// in the audit event and all subsequent annotations with the same key
|
||||
// will be discarded.
|
||||
//
|
||||
// Required.
|
||||
Key string `json:"key" protobuf:"bytes,1,opt,name=key"`
|
||||
|
||||
// valueExpression represents the expression which is evaluated by CEL to
|
||||
// produce an audit annotation value. The expression must evaluate to either
|
||||
// a string or null value. If the expression evaluates to a string, the
|
||||
// audit annotation is included with the string value. If the expression
|
||||
// evaluates to null or empty string the audit annotation will be omitted.
|
||||
// The valueExpression may be no longer than 5kb in length.
|
||||
// If the result of the valueExpression is more than 10kb in length, it
|
||||
// will be truncated to 10kb.
|
||||
//
|
||||
// If multiple ValidatingAdmissionPolicyBinding resources match an
|
||||
// API request, then the valueExpression will be evaluated for
|
||||
// each binding. All unique values produced by the valueExpressions
|
||||
// will be joined together in a comma-separated list.
|
||||
//
|
||||
// Required.
|
||||
ValueExpression string `json:"valueExpression" protobuf:"bytes,2,opt,name=valueExpression"`
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +k8s:prerelease-lifecycle-gen:introduced=1.30
|
||||
|
||||
// ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources.
|
||||
// ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.
|
||||
//
|
||||
// For a given admission request, each binding will cause its policy to be
|
||||
// evaluated N times, where N is 1 for policies/bindings that don't use
|
||||
// params, otherwise N is the number of parameters selected by the binding.
|
||||
//
|
||||
// The CEL expressions of a policy must have a computed CEL cost below the maximum
|
||||
// CEL budget. Each evaluation of the policy is given an independent CEL cost budget.
|
||||
// Adding/removing policies, bindings, or params can not affect whether a
|
||||
// given (policy, binding, param) combination is within its own CEL budget.
|
||||
type ValidatingAdmissionPolicyBinding struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
// Specification of the desired behavior of the ValidatingAdmissionPolicyBinding.
|
||||
Spec ValidatingAdmissionPolicyBindingSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +k8s:prerelease-lifecycle-gen:introduced=1.30
|
||||
|
||||
// ValidatingAdmissionPolicyBindingList is a list of ValidatingAdmissionPolicyBinding.
|
||||
type ValidatingAdmissionPolicyBindingList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard list metadata.
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
|
||||
// List of PolicyBinding.
|
||||
Items []ValidatingAdmissionPolicyBinding `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
||||
}
|
||||
|
||||
// ValidatingAdmissionPolicyBindingSpec is the specification of the ValidatingAdmissionPolicyBinding.
|
||||
type ValidatingAdmissionPolicyBindingSpec struct {
|
||||
// PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to.
|
||||
// If the referenced resource does not exist, this binding is considered invalid and will be ignored
|
||||
// Required.
|
||||
PolicyName string `json:"policyName,omitempty" protobuf:"bytes,1,rep,name=policyName"`
|
||||
|
||||
// paramRef specifies the parameter resource used to configure the admission control policy.
|
||||
// It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy.
|
||||
// If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
|
||||
// If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.
|
||||
// +optional
|
||||
ParamRef *ParamRef `json:"paramRef,omitempty" protobuf:"bytes,2,rep,name=paramRef"`
|
||||
|
||||
// MatchResources declares what resources match this binding and will be validated by it.
|
||||
// Note that this is intersected with the policy's matchConstraints, so only requests that are matched by the policy can be selected by this.
|
||||
// If this is unset, all resources matched by the policy are validated by this binding
|
||||
// When resourceRules is unset, it does not constrain resource matching. If a resource is matched by the other fields of this object, it will be validated.
|
||||
// Note that this is differs from ValidatingAdmissionPolicy matchConstraints, where resourceRules are required.
|
||||
// +optional
|
||||
MatchResources *MatchResources `json:"matchResources,omitempty" protobuf:"bytes,3,rep,name=matchResources"`
|
||||
|
||||
// validationActions declares how Validations of the referenced ValidatingAdmissionPolicy are enforced.
|
||||
// If a validation evaluates to false it is always enforced according to these actions.
|
||||
//
|
||||
// Failures defined by the ValidatingAdmissionPolicy's FailurePolicy are enforced according
|
||||
// to these actions only if the FailurePolicy is set to Fail, otherwise the failures are
|
||||
// ignored. This includes compilation errors, runtime errors and misconfigurations of the policy.
|
||||
//
|
||||
// validationActions is declared as a set of action values. Order does
|
||||
// not matter. validationActions may not contain duplicates of the same action.
|
||||
//
|
||||
// The supported actions values are:
|
||||
//
|
||||
// "Deny" specifies that a validation failure results in a denied request.
|
||||
//
|
||||
// "Warn" specifies that a validation failure is reported to the request client
|
||||
// in HTTP Warning headers, with a warning code of 299. Warnings can be sent
|
||||
// both for allowed or denied admission responses.
|
||||
//
|
||||
// "Audit" specifies that a validation failure is included in the published
|
||||
// audit event for the request. The audit event will contain a
|
||||
// `validation.policy.admission.k8s.io/validation_failure` audit annotation
|
||||
// with a value containing the details of the validation failures, formatted as
|
||||
// a JSON list of objects, each with the following fields:
|
||||
// - message: The validation failure message string
|
||||
// - policy: The resource name of the ValidatingAdmissionPolicy
|
||||
// - binding: The resource name of the ValidatingAdmissionPolicyBinding
|
||||
// - expressionIndex: The index of the failed validations in the ValidatingAdmissionPolicy
|
||||
// - validationActions: The enforcement actions enacted for the validation failure
|
||||
// Example audit annotation:
|
||||
// `"validation.policy.admission.k8s.io/validation_failure": "[{\"message\": \"Invalid value\", {\"policy\": \"policy.example.com\", {\"binding\": \"policybinding.example.com\", {\"expressionIndex\": \"1\", {\"validationActions\": [\"Audit\"]}]"`
|
||||
//
|
||||
// Clients should expect to handle additional values by ignoring
|
||||
// any values not recognized.
|
||||
//
|
||||
// "Deny" and "Warn" may not be used together since this combination
|
||||
// needlessly duplicates the validation failure both in the
|
||||
// API response body and the HTTP warning headers.
|
||||
//
|
||||
// Required.
|
||||
// +listType=set
|
||||
ValidationActions []ValidationAction `json:"validationActions,omitempty" protobuf:"bytes,4,rep,name=validationActions"`
|
||||
}
|
||||
|
||||
// ParamRef describes how to locate the params to be used as input to
|
||||
// expressions of rules applied by a policy binding.
|
||||
// +structType=atomic
|
||||
type ParamRef struct {
|
||||
// name is the name of the resource being referenced.
|
||||
//
|
||||
// One of `name` or `selector` must be set, but `name` and `selector` are
|
||||
// mutually exclusive properties. If one is set, the other must be unset.
|
||||
//
|
||||
// A single parameter used for all admission requests can be configured
|
||||
// by setting the `name` field, leaving `selector` blank, and setting namespace
|
||||
// if `paramKind` is namespace-scoped.
|
||||
//
|
||||
Name string `json:"name,omitempty" protobuf:"bytes,1,rep,name=name"`
|
||||
|
||||
// namespace is the namespace of the referenced resource. Allows limiting
|
||||
// the search for params to a specific namespace. Applies to both `name` and
|
||||
// `selector` fields.
|
||||
//
|
||||
// A per-namespace parameter may be used by specifying a namespace-scoped
|
||||
// `paramKind` in the policy and leaving this field empty.
|
||||
//
|
||||
// - If `paramKind` is cluster-scoped, this field MUST be unset. Setting this
|
||||
// field results in a configuration error.
|
||||
//
|
||||
// - If `paramKind` is namespace-scoped, the namespace of the object being
|
||||
// evaluated for admission will be used when this field is left unset. Take
|
||||
// care that if this is left empty the binding must not match any cluster-scoped
|
||||
// resources, which will result in an error.
|
||||
//
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,rep,name=namespace"`
|
||||
|
||||
// selector can be used to match multiple param objects based on their labels.
|
||||
// Supply selector: {} to match all resources of the ParamKind.
|
||||
//
|
||||
// If multiple params are found, they are all evaluated with the policy expressions
|
||||
// and the results are ANDed together.
|
||||
//
|
||||
// One of `name` or `selector` must be set, but `name` and `selector` are
|
||||
// mutually exclusive properties. If one is set, the other must be unset.
|
||||
//
|
||||
// +optional
|
||||
Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,3,rep,name=selector"`
|
||||
|
||||
// `parameterNotFoundAction` controls the behavior of the binding when the resource
|
||||
// exists, and name or selector is valid, but there are no parameters
|
||||
// matched by the binding. If the value is set to `Allow`, then no
|
||||
// matched parameters will be treated as successful validation by the binding.
|
||||
// If set to `Deny`, then no matched parameters will be subject to the
|
||||
// `failurePolicy` of the policy.
|
||||
//
|
||||
// Allowed values are `Allow` or `Deny`
|
||||
//
|
||||
// Required
|
||||
ParameterNotFoundAction *ParameterNotFoundActionType `json:"parameterNotFoundAction,omitempty" protobuf:"bytes,4,rep,name=parameterNotFoundAction"`
|
||||
}
|
||||
|
||||
// MatchResources decides whether to run the admission control policy on an object based
|
||||
// on whether it meets the match criteria.
|
||||
// The exclude rules take precedence over include rules (if a resource matches both, it is excluded)
|
||||
// +structType=atomic
|
||||
type MatchResources struct {
|
||||
// NamespaceSelector decides whether to run the admission control policy on an object based
|
||||
// on whether the namespace for that object matches the selector. If the
|
||||
// object itself is a namespace, the matching is performed on
|
||||
// object.metadata.labels. If the object is another cluster scoped resource,
|
||||
// it never skips the policy.
|
||||
//
|
||||
// For example, to run the webhook on any objects whose namespace is not
|
||||
// associated with "runlevel" of "0" or "1"; you will set the selector as
|
||||
// follows:
|
||||
// "namespaceSelector": {
|
||||
// "matchExpressions": [
|
||||
// {
|
||||
// "key": "runlevel",
|
||||
// "operator": "NotIn",
|
||||
// "values": [
|
||||
// "0",
|
||||
// "1"
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// If instead you want to only run the policy on any objects whose
|
||||
// namespace is associated with the "environment" of "prod" or "staging";
|
||||
// you will set the selector as follows:
|
||||
// "namespaceSelector": {
|
||||
// "matchExpressions": [
|
||||
// {
|
||||
// "key": "environment",
|
||||
// "operator": "In",
|
||||
// "values": [
|
||||
// "prod",
|
||||
// "staging"
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// See
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
|
||||
// for more examples of label selectors.
|
||||
//
|
||||
// Default to the empty LabelSelector, which matches everything.
|
||||
// +optional
|
||||
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,1,opt,name=namespaceSelector"`
|
||||
// ObjectSelector decides whether to run the validation based on if the
|
||||
// object has matching labels. objectSelector is evaluated against both
|
||||
// the oldObject and newObject that would be sent to the cel validation, and
|
||||
// is considered to match if either object matches the selector. A null
|
||||
// object (oldObject in the case of create, or newObject in the case of
|
||||
// delete) or an object that cannot have labels (like a
|
||||
// DeploymentRollback or a PodProxyOptions object) is not considered to
|
||||
// match.
|
||||
// Use the object selector only if the webhook is opt-in, because end
|
||||
// users may skip the admission webhook by setting the labels.
|
||||
// Default to the empty LabelSelector, which matches everything.
|
||||
// +optional
|
||||
ObjectSelector *metav1.LabelSelector `json:"objectSelector,omitempty" protobuf:"bytes,2,opt,name=objectSelector"`
|
||||
// ResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy matches.
|
||||
// The policy cares about an operation if it matches _any_ Rule.
|
||||
// +listType=atomic
|
||||
// +optional
|
||||
ResourceRules []NamedRuleWithOperations `json:"resourceRules,omitempty" protobuf:"bytes,3,rep,name=resourceRules"`
|
||||
// ExcludeResourceRules describes what operations on what resources/subresources the ValidatingAdmissionPolicy should not care about.
|
||||
// The exclude rules take precedence over include rules (if a resource matches both, it is excluded)
|
||||
// +listType=atomic
|
||||
// +optional
|
||||
ExcludeResourceRules []NamedRuleWithOperations `json:"excludeResourceRules,omitempty" protobuf:"bytes,4,rep,name=excludeResourceRules"`
|
||||
// matchPolicy defines how the "MatchResources" list is used to match incoming requests.
|
||||
// Allowed values are "Exact" or "Equivalent".
|
||||
//
|
||||
// - Exact: match a request only if it exactly matches a specified rule.
|
||||
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
|
||||
// but "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
|
||||
// a request to apps/v1beta1 or extensions/v1beta1 would not be sent to the ValidatingAdmissionPolicy.
|
||||
//
|
||||
// - Equivalent: match a request if modifies a resource listed in rules, even via another API group or version.
|
||||
// For example, if deployments can be modified via apps/v1, apps/v1beta1, and extensions/v1beta1,
|
||||
// and "rules" only included `apiGroups:["apps"], apiVersions:["v1"], resources: ["deployments"]`,
|
||||
// a request to apps/v1beta1 or extensions/v1beta1 would be converted to apps/v1 and sent to the ValidatingAdmissionPolicy.
|
||||
//
|
||||
// Defaults to "Equivalent"
|
||||
// +optional
|
||||
MatchPolicy *MatchPolicyType `json:"matchPolicy,omitempty" protobuf:"bytes,7,opt,name=matchPolicy,casttype=MatchPolicyType"`
|
||||
}
|
||||
|
||||
// ValidationAction specifies a policy enforcement action.
|
||||
// +enum
|
||||
type ValidationAction string
|
||||
|
||||
const (
|
||||
// Deny specifies that a validation failure results in a denied request.
|
||||
Deny ValidationAction = "Deny"
|
||||
// Warn specifies that a validation failure is reported to the request client
|
||||
// in HTTP Warning headers, with a warning code of 299. Warnings can be sent
|
||||
// both for allowed or denied admission responses.
|
||||
Warn ValidationAction = "Warn"
|
||||
// Audit specifies that a validation failure is included in the published
|
||||
// audit event for the request. The audit event will contain a
|
||||
// `validation.policy.admission.k8s.io/validation_failure` audit annotation
|
||||
// with a value containing the details of the validation failure.
|
||||
Audit ValidationAction = "Audit"
|
||||
)
|
||||
|
||||
// NamedRuleWithOperations is a tuple of Operations and Resources with ResourceNames.
|
||||
// +structType=atomic
|
||||
type NamedRuleWithOperations struct {
|
||||
// ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed.
|
||||
// +listType=atomic
|
||||
// +optional
|
||||
ResourceNames []string `json:"resourceNames,omitempty" protobuf:"bytes,1,rep,name=resourceNames"`
|
||||
// RuleWithOperations is a tuple of Operations and Resources.
|
||||
RuleWithOperations `json:",inline" protobuf:"bytes,2,opt,name=ruleWithOperations"`
|
||||
}
|
||||
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
@@ -90,7 +90,7 @@ users:
|
||||
"--kubeconfig", fakeKubeConfig.Name(),
|
||||
// disable admission and filters that require talking to kube-apiserver
|
||||
"--enable-priority-and-fairness=false",
|
||||
"--disable-admission-plugins", "NamespaceLifecycle,MutatingAdmissionWebhook,ValidatingAdmissionWebhook"},
|
||||
"--disable-admission-plugins", "NamespaceLifecycle,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ValidatingAdmissionPolicy"},
|
||||
flags...,
|
||||
), nil)
|
||||
if err != nil {
|
||||
|
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package initializer
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/client-go/dynamic"
|
||||
@@ -32,6 +33,7 @@ type pluginInitializer struct {
|
||||
authorizer authorizer.Authorizer
|
||||
featureGates featuregate.FeatureGate
|
||||
stopCh <-chan struct{}
|
||||
restMapper meta.RESTMapper
|
||||
}
|
||||
|
||||
// New creates an instance of admission plugins initializer.
|
||||
@@ -44,6 +46,7 @@ func New(
|
||||
authz authorizer.Authorizer,
|
||||
featureGates featuregate.FeatureGate,
|
||||
stopCh <-chan struct{},
|
||||
restMapper meta.RESTMapper,
|
||||
) pluginInitializer {
|
||||
return pluginInitializer{
|
||||
externalClient: extClientset,
|
||||
@@ -52,6 +55,7 @@ func New(
|
||||
authorizer: authz,
|
||||
featureGates: featureGates,
|
||||
stopCh: stopCh,
|
||||
restMapper: restMapper,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +87,9 @@ func (i pluginInitializer) Initialize(plugin admission.Interface) {
|
||||
if wants, ok := plugin.(WantsAuthorizer); ok {
|
||||
wants.SetAuthorizer(i.authorizer)
|
||||
}
|
||||
if wants, ok := plugin.(WantsRESTMapper); ok {
|
||||
wants.SetRESTMapper(i.restMapper)
|
||||
}
|
||||
}
|
||||
|
||||
var _ admission.PluginInitializer = pluginInitializer{}
|
||||
|
@@ -21,6 +21,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/initializer"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
@@ -32,7 +34,7 @@ import (
|
||||
// TestWantsAuthorizer ensures that the authorizer is injected
|
||||
// when the WantsAuthorizer interface is implemented by a plugin.
|
||||
func TestWantsAuthorizer(t *testing.T) {
|
||||
target := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, nil)
|
||||
target := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, nil, nil)
|
||||
wantAuthorizerAdmission := &WantAuthorizerAdmission{}
|
||||
target.Initialize(wantAuthorizerAdmission)
|
||||
if wantAuthorizerAdmission.auth == nil {
|
||||
@@ -44,7 +46,7 @@ func TestWantsAuthorizer(t *testing.T) {
|
||||
// when the WantsExternalKubeClientSet interface is implemented by a plugin.
|
||||
func TestWantsExternalKubeClientSet(t *testing.T) {
|
||||
cs := &fake.Clientset{}
|
||||
target := initializer.New(cs, nil, nil, &TestAuthorizer{}, nil, nil)
|
||||
target := initializer.New(cs, nil, nil, &TestAuthorizer{}, nil, nil, nil)
|
||||
wantExternalKubeClientSet := &WantExternalKubeClientSet{}
|
||||
target.Initialize(wantExternalKubeClientSet)
|
||||
if wantExternalKubeClientSet.cs != cs {
|
||||
@@ -57,7 +59,7 @@ func TestWantsExternalKubeClientSet(t *testing.T) {
|
||||
func TestWantsExternalKubeInformerFactory(t *testing.T) {
|
||||
cs := &fake.Clientset{}
|
||||
sf := informers.NewSharedInformerFactory(cs, time.Duration(1)*time.Second)
|
||||
target := initializer.New(cs, nil, sf, &TestAuthorizer{}, nil, nil)
|
||||
target := initializer.New(cs, nil, sf, &TestAuthorizer{}, nil, nil, nil)
|
||||
wantExternalKubeInformerFactory := &WantExternalKubeInformerFactory{}
|
||||
target.Initialize(wantExternalKubeInformerFactory)
|
||||
if wantExternalKubeInformerFactory.sf != sf {
|
||||
@@ -69,7 +71,7 @@ func TestWantsExternalKubeInformerFactory(t *testing.T) {
|
||||
// when the WantsShutdownSignal interface is implemented by a plugin.
|
||||
func TestWantsShutdownNotification(t *testing.T) {
|
||||
stopCh := make(chan struct{})
|
||||
target := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, stopCh)
|
||||
target := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, stopCh, nil)
|
||||
wantDrainedNotification := &WantDrainedNotification{}
|
||||
target.Initialize(wantDrainedNotification)
|
||||
if wantDrainedNotification.stopCh == nil {
|
||||
@@ -149,3 +151,59 @@ type TestAuthorizer struct{}
|
||||
func (t *TestAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
|
||||
return authorizer.DecisionNoOpinion, "", nil
|
||||
}
|
||||
|
||||
func TestRESTMapperAdmissionPlugin(t *testing.T) {
|
||||
initializer := initializer.New(nil, nil, nil, &TestAuthorizer{}, nil, nil, &doNothingRESTMapper{})
|
||||
wantsRESTMapperAdmission := &WantsRESTMapperAdmissionPlugin{}
|
||||
initializer.Initialize(wantsRESTMapperAdmission)
|
||||
|
||||
if wantsRESTMapperAdmission.mapper == nil {
|
||||
t.Errorf("Expected REST mapper to be initialized but found nil")
|
||||
}
|
||||
}
|
||||
|
||||
type WantsRESTMapperAdmissionPlugin struct {
|
||||
doNothingAdmission
|
||||
doNothingPluginInitialization
|
||||
mapper meta.RESTMapper
|
||||
}
|
||||
|
||||
func (p *WantsRESTMapperAdmissionPlugin) SetRESTMapper(mapper meta.RESTMapper) {
|
||||
p.mapper = mapper
|
||||
}
|
||||
|
||||
type doNothingRESTMapper struct{}
|
||||
|
||||
func (doNothingRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
|
||||
return schema.GroupVersionKind{}, nil
|
||||
}
|
||||
func (doNothingRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (doNothingRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
|
||||
return schema.GroupVersionResource{}, nil
|
||||
}
|
||||
func (doNothingRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (doNothingRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (doNothingRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (doNothingRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
type doNothingAdmission struct{}
|
||||
|
||||
func (doNothingAdmission) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) error {
|
||||
return nil
|
||||
}
|
||||
func (doNothingAdmission) Handles(o admission.Operation) bool { return false }
|
||||
func (doNothingAdmission) Validate() error { return nil }
|
||||
|
||||
type doNothingPluginInitialization struct{}
|
||||
|
||||
func (doNothingPluginInitialization) ValidateInitialization() error { return nil }
|
||||
|
@@ -53,7 +53,7 @@ func newHandlerForTestWithClock(c clientset.Interface, cacheClock clock.Clock) (
|
||||
if err != nil {
|
||||
return nil, f, err
|
||||
}
|
||||
pluginInitializer := kubeadmission.New(c, nil, f, nil, nil, nil)
|
||||
pluginInitializer := kubeadmission.New(c, nil, f, nil, nil, nil, nil)
|
||||
pluginInitializer.Initialize(handler)
|
||||
err = admission.ValidateInitialization(handler)
|
||||
return handler, f, err
|
||||
|
@@ -17,15 +17,15 @@ limitations under the License.
|
||||
package generic
|
||||
|
||||
import (
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
type PolicyAccessor interface {
|
||||
GetName() string
|
||||
GetNamespace() string
|
||||
GetParamKind() *v1beta1.ParamKind
|
||||
GetMatchConstraints() *v1beta1.MatchResources
|
||||
GetParamKind() *v1.ParamKind
|
||||
GetMatchConstraints() *v1.MatchResources
|
||||
}
|
||||
|
||||
type BindingAccessor interface {
|
||||
@@ -36,7 +36,7 @@ type BindingAccessor interface {
|
||||
// which is cluster-scoped, so namespace is usually left blank.
|
||||
// But we leave the door open to add a namespaced vesion in the future
|
||||
GetPolicyName() types.NamespacedName
|
||||
GetParamRef() *v1beta1.ParamRef
|
||||
GetParamRef() *v1.ParamRef
|
||||
|
||||
GetMatchResources() *v1beta1.MatchResources
|
||||
GetMatchResources() *v1.MatchResources
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -217,10 +217,10 @@ func (d *policyDispatcher[P, B, E]) Dispatch(ctx context.Context, a admission.At
|
||||
// configuration. If the policy-binding has no param configuration, it
|
||||
// returns a single-element list with a nil param.
|
||||
func CollectParams(
|
||||
paramKind *v1beta1.ParamKind,
|
||||
paramKind *v1.ParamKind,
|
||||
paramInformer informers.GenericInformer,
|
||||
paramScope meta.RESTScope,
|
||||
paramRef *v1beta1.ParamRef,
|
||||
paramRef *v1.ParamRef,
|
||||
namespace string,
|
||||
) ([]runtime.Object, error) {
|
||||
// If definition has paramKind, paramRef is required in binding.
|
||||
@@ -326,7 +326,7 @@ func CollectParams(
|
||||
}
|
||||
|
||||
// Apply fail action for params not found case
|
||||
if len(params) == 0 && paramRef.ParameterNotFoundAction != nil && *paramRef.ParameterNotFoundAction == v1beta1.DenyAction {
|
||||
if len(params) == 0 && paramRef.ParameterNotFoundAction != nil && *paramRef.ParameterNotFoundAction == v1.DenyAction {
|
||||
return nil, errors.New("no params found for policy binding with `Deny` parameterNotFoundAction")
|
||||
}
|
||||
|
||||
|
@@ -19,7 +19,7 @@ package generic
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
@@ -89,7 +89,7 @@ func (c *matcher) GetNamespace(name string) (*corev1.Namespace, error) {
|
||||
var _ matching.MatchCriteria = &matchCriteria{}
|
||||
|
||||
type matchCriteria struct {
|
||||
constraints *v1beta1.MatchResources
|
||||
constraints *admissionregistrationv1.MatchResources
|
||||
}
|
||||
|
||||
// GetParsedNamespaceSelector returns the converted LabelSelector which implements labels.Selector
|
||||
@@ -103,6 +103,6 @@ func (m *matchCriteria) GetParsedObjectSelector() (labels.Selector, error) {
|
||||
}
|
||||
|
||||
// GetMatchResources returns the matchConstraints
|
||||
func (m *matchCriteria) GetMatchResources() v1beta1.MatchResources {
|
||||
func (m *matchCriteria) GetMatchResources() admissionregistrationv1.MatchResources {
|
||||
return *m.constraints
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -110,7 +110,7 @@ func TestPolicySourceHasSyncedInitialList(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "policy2",
|
||||
},
|
||||
ParamKind: &v1beta1.ParamKind{
|
||||
ParamKind: &v1.ParamKind{
|
||||
APIVersion: "policy.example.com/v1",
|
||||
Kind: "FakeParam",
|
||||
},
|
||||
@@ -177,7 +177,7 @@ type FakePolicy struct {
|
||||
metav1.TypeMeta
|
||||
metav1.ObjectMeta
|
||||
|
||||
ParamKind *v1beta1.ParamKind
|
||||
ParamKind *v1.ParamKind
|
||||
}
|
||||
|
||||
var _ generic.PolicyAccessor = &FakePolicy{}
|
||||
@@ -199,11 +199,11 @@ func (fp *FakePolicy) GetNamespace() string {
|
||||
return fp.Namespace
|
||||
}
|
||||
|
||||
func (fp *FakePolicy) GetParamKind() *v1beta1.ParamKind {
|
||||
func (fp *FakePolicy) GetParamKind() *v1.ParamKind {
|
||||
return fp.ParamKind
|
||||
}
|
||||
|
||||
func (fb *FakePolicy) GetMatchConstraints() *v1beta1.MatchResources {
|
||||
func (fb *FakePolicy) GetMatchConstraints() *v1.MatchResources {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -221,11 +221,11 @@ func (fb *FakeBinding) GetPolicyName() types.NamespacedName {
|
||||
}
|
||||
}
|
||||
|
||||
func (fb *FakeBinding) GetMatchResources() *v1beta1.MatchResources {
|
||||
func (fb *FakeBinding) GetMatchResources() *v1.MatchResources {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fb *FakeBinding) GetParamRef() *v1beta1.ParamRef {
|
||||
func (fb *FakeBinding) GetParamRef() *v1.ParamRef {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -216,6 +216,7 @@ func NewPolicyTestContext[P, B runtime.Object, E Evaluator](
|
||||
fakeAuthorizer{},
|
||||
featureGate,
|
||||
testContext.Done(),
|
||||
fakeRestMapper,
|
||||
)
|
||||
genericInitializer.Initialize(plugin)
|
||||
plugin.SetRESTMapper(fakeRestMapper)
|
||||
|
@@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
@@ -36,7 +35,7 @@ type MatchCriteria interface {
|
||||
namespace.NamespaceSelectorProvider
|
||||
object.ObjectSelectorProvider
|
||||
|
||||
GetMatchResources() v1beta1.MatchResources
|
||||
GetMatchResources() v1.MatchResources
|
||||
}
|
||||
|
||||
// Matcher decides if a request matches against matchCriteria
|
||||
@@ -121,7 +120,7 @@ func (m *Matcher) Matches(attr admission.Attributes, o admission.ObjectInterface
|
||||
return true, matchResource, matchKind, nil
|
||||
}
|
||||
|
||||
func matchesResourceRules(namedRules []v1beta1.NamedRuleWithOperations, matchPolicy *v1beta1.MatchPolicyType, attr admission.Attributes, o admission.ObjectInterfaces) (bool, schema.GroupVersionResource, schema.GroupVersionKind, error) {
|
||||
func matchesResourceRules(namedRules []v1.NamedRuleWithOperations, matchPolicy *v1.MatchPolicyType, attr admission.Attributes, o admission.ObjectInterfaces) (bool, schema.GroupVersionResource, schema.GroupVersionKind, error) {
|
||||
matchKind := attr.GetKind()
|
||||
matchResource := attr.GetResource()
|
||||
|
||||
@@ -150,7 +149,7 @@ func matchesResourceRules(namedRules []v1beta1.NamedRuleWithOperations, matchPol
|
||||
|
||||
// if match policy is undefined or exact, don't perform fuzzy matching
|
||||
// note that defaulting to fuzzy matching is set by the API
|
||||
if matchPolicy == nil || *matchPolicy == v1beta1.Exact {
|
||||
if matchPolicy == nil || *matchPolicy == v1.Exact {
|
||||
return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, nil
|
||||
}
|
||||
|
||||
|
@@ -22,7 +22,6 @@ import (
|
||||
"testing"
|
||||
|
||||
v1 "k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -38,10 +37,10 @@ import (
|
||||
var _ MatchCriteria = &fakeCriteria{}
|
||||
|
||||
type fakeCriteria struct {
|
||||
matchResources v1beta1.MatchResources
|
||||
matchResources v1.MatchResources
|
||||
}
|
||||
|
||||
func (fc *fakeCriteria) GetMatchResources() v1beta1.MatchResources {
|
||||
func (fc *fakeCriteria) GetMatchResources() v1.MatchResources {
|
||||
return fc.matchResources
|
||||
}
|
||||
|
||||
@@ -65,8 +64,8 @@ func TestMatcher(t *testing.T) {
|
||||
a := &Matcher{namespaceMatcher: &namespace.Matcher{}, objectMatcher: &object.Matcher{}}
|
||||
|
||||
allScopes := v1.AllScopes
|
||||
exactMatch := v1beta1.Exact
|
||||
equivalentMatch := v1beta1.Equivalent
|
||||
exactMatch := v1.Exact
|
||||
equivalentMatch := v1.Equivalent
|
||||
|
||||
mapper := runtime.NewEquivalentResourceRegistryWithIdentity(func(resource schema.GroupResource) string {
|
||||
if resource.Resource == "deployments" {
|
||||
@@ -95,7 +94,7 @@ func TestMatcher(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
|
||||
criteria *v1beta1.MatchResources
|
||||
criteria *v1.MatchResources
|
||||
attrs admission.Attributes
|
||||
|
||||
expectMatches bool
|
||||
@@ -105,17 +104,17 @@ func TestMatcher(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "no rules (just write)",
|
||||
criteria: &v1beta1.MatchResources{NamespaceSelector: &metav1.LabelSelector{}, ResourceRules: []v1beta1.NamedRuleWithOperations{}},
|
||||
criteria: &v1.MatchResources{NamespaceSelector: &metav1.LabelSelector{}, ResourceRules: []v1.NamedRuleWithOperations{}},
|
||||
attrs: admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||
expectMatches: false,
|
||||
},
|
||||
{
|
||||
name: "wildcard rule, match as requested",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -126,21 +125,21 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, prefer exact match",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -151,16 +150,16 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, match miss",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -170,17 +169,17 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, exact match miss",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
MatchPolicy: &exactMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -190,17 +189,17 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, equivalent match, prefer extensions",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
MatchPolicy: &equivalentMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -212,17 +211,17 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, equivalent match, prefer apps",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
MatchPolicy: &equivalentMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -235,21 +234,21 @@ func TestMatcher(t *testing.T) {
|
||||
|
||||
{
|
||||
name: "specific rules, subresource prefer exact match",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -260,16 +259,16 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, subresource match miss",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -279,17 +278,17 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, subresource exact match miss",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
MatchPolicy: &exactMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -299,17 +298,17 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, subresource equivalent match, prefer extensions",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
MatchPolicy: &equivalentMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -321,17 +320,17 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, subresource equivalent match, prefer apps",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
MatchPolicy: &equivalentMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -343,12 +342,12 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, prefer exact match and name match",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
ResourceNames: []string{"name"},
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -359,12 +358,12 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, prefer exact match and name match miss",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
ResourceNames: []string{"wrong-name"},
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -374,13 +373,13 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, subresource equivalent match, prefer extensions and name match",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
MatchPolicy: &equivalentMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
ResourceNames: []string{"name"},
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -392,13 +391,13 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "specific rules, subresource equivalent match, prefer extensions and name match miss",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
MatchPolicy: &equivalentMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
ResourceNames: []string{"wrong-name"},
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -408,17 +407,17 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "exclude resource match on miss",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes},
|
||||
},
|
||||
}},
|
||||
ExcludeResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ExcludeResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -430,17 +429,17 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "exclude resource miss on match",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes},
|
||||
},
|
||||
}},
|
||||
ExcludeResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ExcludeResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -451,11 +450,11 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "treat empty ResourceRules as match",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ExcludeResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
ExcludeResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -466,23 +465,23 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "treat non-empty ResourceRules as no match",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{}},
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{}},
|
||||
},
|
||||
attrs: admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||
expectMatches: false,
|
||||
},
|
||||
{
|
||||
name: "erroring namespace selector on otherwise non-matching rule doesn't error",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key ", Operator: "In", Values: []string{"bad value"}}}},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"deployments"}},
|
||||
Operations: []v1beta1.OperationType{"*"},
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"deployments"}},
|
||||
Operations: []v1.OperationType{"*"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
@@ -492,13 +491,13 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "erroring namespace selector on otherwise matching rule errors",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key", Operator: "In", Values: []string{"bad value"}}}},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
|
||||
Operations: []v1beta1.OperationType{"*"},
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
|
||||
Operations: []v1.OperationType{"*"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
@@ -508,13 +507,13 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "erroring object selector on otherwise non-matching rule doesn't error",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key", Operator: "In", Values: []string{"bad value"}}}},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"deployments"}},
|
||||
Operations: []v1beta1.OperationType{"*"},
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"deployments"}},
|
||||
Operations: []v1.OperationType{"*"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
@@ -524,13 +523,13 @@ func TestMatcher(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "erroring object selector on otherwise matching rule errors",
|
||||
criteria: &v1beta1.MatchResources{
|
||||
criteria: &v1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key", Operator: "In", Values: []string{"bad value"}}}},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
|
||||
Operations: []v1beta1.OperationType{"*"},
|
||||
ResourceRules: []v1.NamedRuleWithOperations{{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
|
||||
Operations: []v1.OperationType{"*"},
|
||||
},
|
||||
}},
|
||||
},
|
||||
@@ -601,7 +600,7 @@ func (f fakeNamespaceLister) Get(name string) (*corev1.Namespace, error) {
|
||||
|
||||
func BenchmarkMatcher(b *testing.B) {
|
||||
allScopes := v1.AllScopes
|
||||
equivalentMatch := v1beta1.Equivalent
|
||||
equivalentMatch := v1.Equivalent
|
||||
|
||||
namespace1Labels := map[string]string{"ns": "ns1"}
|
||||
namespace1 := corev1.Namespace{
|
||||
@@ -642,19 +641,19 @@ func BenchmarkMatcher(b *testing.B) {
|
||||
nsSelector[fmt.Sprintf("key-%d", i)] = fmt.Sprintf("val-%d", i)
|
||||
}
|
||||
|
||||
mr := v1beta1.MatchResources{
|
||||
mr := v1.MatchResources{
|
||||
MatchPolicy: &equivalentMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{MatchLabels: nsSelector},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
},
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||
},
|
||||
@@ -674,7 +673,7 @@ func BenchmarkMatcher(b *testing.B) {
|
||||
|
||||
func BenchmarkShouldCallHookWithComplexRule(b *testing.B) {
|
||||
allScopes := v1.AllScopes
|
||||
equivalentMatch := v1beta1.Equivalent
|
||||
equivalentMatch := v1.Equivalent
|
||||
|
||||
namespace1Labels := map[string]string{"ns": "ns1"}
|
||||
namespace1 := corev1.Namespace{
|
||||
@@ -710,16 +709,16 @@ func BenchmarkShouldCallHookWithComplexRule(b *testing.B) {
|
||||
mapper.RegisterKindFor(gvr("apps", "v1beta1", "statefulset"), "scale", gvk("apps", "v1beta1", "Scale"))
|
||||
mapper.RegisterKindFor(gvr("apps", "v1alpha2", "statefulset"), "scale", gvk("apps", "v1beta2", "Scale"))
|
||||
|
||||
mr := v1beta1.MatchResources{
|
||||
mr := v1.MatchResources{
|
||||
MatchPolicy: &equivalentMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{},
|
||||
ResourceRules: []v1.NamedRuleWithOperations{},
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
rule := v1beta1.NamedRuleWithOperations{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
rule := v1.NamedRuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{fmt.Sprintf("app-%d", i)},
|
||||
@@ -744,7 +743,7 @@ func BenchmarkShouldCallHookWithComplexRule(b *testing.B) {
|
||||
|
||||
func BenchmarkShouldCallHookWithComplexSelectorAndRule(b *testing.B) {
|
||||
allScopes := v1.AllScopes
|
||||
equivalentMatch := v1beta1.Equivalent
|
||||
equivalentMatch := v1.Equivalent
|
||||
|
||||
namespace1Labels := map[string]string{"ns": "ns1"}
|
||||
namespace1 := corev1.Namespace{
|
||||
@@ -785,16 +784,16 @@ func BenchmarkShouldCallHookWithComplexSelectorAndRule(b *testing.B) {
|
||||
nsSelector[fmt.Sprintf("key-%d", i)] = fmt.Sprintf("val-%d", i)
|
||||
}
|
||||
|
||||
mr := v1beta1.MatchResources{
|
||||
mr := v1.MatchResources{
|
||||
MatchPolicy: &equivalentMatch,
|
||||
NamespaceSelector: &metav1.LabelSelector{MatchLabels: nsSelector},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
ResourceRules: []v1beta1.NamedRuleWithOperations{},
|
||||
ResourceRules: []v1.NamedRuleWithOperations{},
|
||||
}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
rule := v1beta1.NamedRuleWithOperations{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
rule := v1.NamedRuleWithOperations{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Operations: []v1.OperationType{"*"},
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{fmt.Sprintf("app-%d", i)},
|
||||
|
@@ -17,25 +17,25 @@ limitations under the License.
|
||||
package validating
|
||||
|
||||
import (
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/policy/generic"
|
||||
)
|
||||
|
||||
func NewValidatingAdmissionPolicyAccessor(obj *v1beta1.ValidatingAdmissionPolicy) generic.PolicyAccessor {
|
||||
func NewValidatingAdmissionPolicyAccessor(obj *v1.ValidatingAdmissionPolicy) generic.PolicyAccessor {
|
||||
return &validatingAdmissionPolicyAccessor{
|
||||
ValidatingAdmissionPolicy: obj,
|
||||
}
|
||||
}
|
||||
|
||||
func NewValidatingAdmissionPolicyBindingAccessor(obj *v1beta1.ValidatingAdmissionPolicyBinding) generic.BindingAccessor {
|
||||
func NewValidatingAdmissionPolicyBindingAccessor(obj *v1.ValidatingAdmissionPolicyBinding) generic.BindingAccessor {
|
||||
return &validatingAdmissionPolicyBindingAccessor{
|
||||
ValidatingAdmissionPolicyBinding: obj,
|
||||
}
|
||||
}
|
||||
|
||||
type validatingAdmissionPolicyAccessor struct {
|
||||
*v1beta1.ValidatingAdmissionPolicy
|
||||
*v1.ValidatingAdmissionPolicy
|
||||
}
|
||||
|
||||
func (v *validatingAdmissionPolicyAccessor) GetNamespace() string {
|
||||
@@ -46,16 +46,16 @@ func (v *validatingAdmissionPolicyAccessor) GetName() string {
|
||||
return v.Name
|
||||
}
|
||||
|
||||
func (v *validatingAdmissionPolicyAccessor) GetParamKind() *v1beta1.ParamKind {
|
||||
func (v *validatingAdmissionPolicyAccessor) GetParamKind() *v1.ParamKind {
|
||||
return v.Spec.ParamKind
|
||||
}
|
||||
|
||||
func (v *validatingAdmissionPolicyAccessor) GetMatchConstraints() *v1beta1.MatchResources {
|
||||
func (v *validatingAdmissionPolicyAccessor) GetMatchConstraints() *v1.MatchResources {
|
||||
return v.Spec.MatchConstraints
|
||||
}
|
||||
|
||||
type validatingAdmissionPolicyBindingAccessor struct {
|
||||
*v1beta1.ValidatingAdmissionPolicyBinding
|
||||
*v1.ValidatingAdmissionPolicyBinding
|
||||
}
|
||||
|
||||
func (v *validatingAdmissionPolicyBindingAccessor) GetNamespace() string {
|
||||
@@ -73,10 +73,10 @@ func (v *validatingAdmissionPolicyBindingAccessor) GetPolicyName() types.Namespa
|
||||
}
|
||||
}
|
||||
|
||||
func (v *validatingAdmissionPolicyBindingAccessor) GetMatchResources() *v1beta1.MatchResources {
|
||||
func (v *validatingAdmissionPolicyBindingAccessor) GetMatchResources() *v1.MatchResources {
|
||||
return v.Spec.MatchResources
|
||||
}
|
||||
|
||||
func (v *validatingAdmissionPolicyBindingAccessor) GetParamRef() *v1beta1.ParamRef {
|
||||
func (v *validatingAdmissionPolicyBindingAccessor) GetParamRef() *v1.ParamRef {
|
||||
return v.Spec.ParamRef
|
||||
}
|
||||
|
@@ -27,7 +27,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -61,18 +61,18 @@ var (
|
||||
}
|
||||
|
||||
// Common objects
|
||||
denyPolicy *v1beta1.ValidatingAdmissionPolicy = &v1beta1.ValidatingAdmissionPolicy{
|
||||
denyPolicy *admissionregistrationv1.ValidatingAdmissionPolicy = &admissionregistrationv1.ValidatingAdmissionPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denypolicy.example.com",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
ParamKind: &v1beta1.ParamKind{
|
||||
Spec: admissionregistrationv1.ValidatingAdmissionPolicySpec{
|
||||
ParamKind: &admissionregistrationv1.ParamKind{
|
||||
APIVersion: paramsGVK.GroupVersion().String(),
|
||||
Kind: paramsGVK.Kind,
|
||||
},
|
||||
FailurePolicy: ptrTo(v1beta1.Fail),
|
||||
Validations: []v1beta1.Validation{
|
||||
FailurePolicy: ptrTo(admissionregistrationv1.Fail),
|
||||
Validations: []admissionregistrationv1.Validation{
|
||||
{
|
||||
Expression: "messageId for deny policy",
|
||||
},
|
||||
@@ -93,61 +93,61 @@ var (
|
||||
},
|
||||
}
|
||||
|
||||
denyBinding *v1beta1.ValidatingAdmissionPolicyBinding = &v1beta1.ValidatingAdmissionPolicyBinding{
|
||||
denyBinding *admissionregistrationv1.ValidatingAdmissionPolicyBinding = &admissionregistrationv1.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denybinding.example.com",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: v1beta1.ValidatingAdmissionPolicyBindingSpec{
|
||||
Spec: admissionregistrationv1.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: denyPolicy.Name,
|
||||
ParamRef: &v1beta1.ParamRef{
|
||||
ParamRef: &admissionregistrationv1.ParamRef{
|
||||
Name: fakeParams.GetName(),
|
||||
Namespace: fakeParams.GetNamespace(),
|
||||
// fake object tracker does not populate defaults
|
||||
ParameterNotFoundAction: ptrTo(v1beta1.DenyAction),
|
||||
ParameterNotFoundAction: ptrTo(admissionregistrationv1.DenyAction),
|
||||
},
|
||||
ValidationActions: []v1beta1.ValidationAction{v1beta1.Deny},
|
||||
ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Deny},
|
||||
},
|
||||
}
|
||||
denyBindingWithNoParamRef *v1beta1.ValidatingAdmissionPolicyBinding = &v1beta1.ValidatingAdmissionPolicyBinding{
|
||||
denyBindingWithNoParamRef *admissionregistrationv1.ValidatingAdmissionPolicyBinding = &admissionregistrationv1.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denybinding.example.com",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: v1beta1.ValidatingAdmissionPolicyBindingSpec{
|
||||
Spec: admissionregistrationv1.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: denyPolicy.Name,
|
||||
ValidationActions: []v1beta1.ValidationAction{v1beta1.Deny},
|
||||
ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Deny},
|
||||
},
|
||||
}
|
||||
|
||||
denyBindingWithAudit = &v1beta1.ValidatingAdmissionPolicyBinding{
|
||||
denyBindingWithAudit = &admissionregistrationv1.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denybinding.example.com",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: v1beta1.ValidatingAdmissionPolicyBindingSpec{
|
||||
Spec: admissionregistrationv1.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: denyPolicy.Name,
|
||||
ValidationActions: []v1beta1.ValidationAction{v1beta1.Audit},
|
||||
ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Audit},
|
||||
},
|
||||
}
|
||||
denyBindingWithWarn = &v1beta1.ValidatingAdmissionPolicyBinding{
|
||||
denyBindingWithWarn = &admissionregistrationv1.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denybinding.example.com",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: v1beta1.ValidatingAdmissionPolicyBindingSpec{
|
||||
Spec: admissionregistrationv1.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: denyPolicy.Name,
|
||||
ValidationActions: []v1beta1.ValidationAction{v1beta1.Warn},
|
||||
ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Warn},
|
||||
},
|
||||
}
|
||||
denyBindingWithAll = &v1beta1.ValidatingAdmissionPolicyBinding{
|
||||
denyBindingWithAll = &admissionregistrationv1.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denybinding.example.com",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: v1beta1.ValidatingAdmissionPolicyBindingSpec{
|
||||
Spec: admissionregistrationv1.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: denyPolicy.Name,
|
||||
ValidationActions: []v1beta1.ValidationAction{v1beta1.Deny, v1beta1.Warn, v1beta1.Audit},
|
||||
ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Deny, admissionregistrationv1.Warn, admissionregistrationv1.Audit},
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -277,7 +277,7 @@ type fakeMatcher struct {
|
||||
BindingMatchFuncs map[types.NamespacedName]func(generic.BindingAccessor, admission.Attributes) bool
|
||||
}
|
||||
|
||||
func (f *fakeMatcher) RegisterDefinition(definition *v1beta1.ValidatingAdmissionPolicy, matchFunc func(generic.PolicyAccessor, admission.Attributes) bool) {
|
||||
func (f *fakeMatcher) RegisterDefinition(definition *admissionregistrationv1.ValidatingAdmissionPolicy, matchFunc func(generic.PolicyAccessor, admission.Attributes) bool) {
|
||||
namespace, name := definition.Namespace, definition.Name
|
||||
key := types.NamespacedName{
|
||||
Name: name,
|
||||
@@ -292,7 +292,7 @@ func (f *fakeMatcher) RegisterDefinition(definition *v1beta1.ValidatingAdmission
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeMatcher) RegisterBinding(binding *v1beta1.ValidatingAdmissionPolicyBinding, matchFunc func(generic.BindingAccessor, admission.Attributes) bool) {
|
||||
func (f *fakeMatcher) RegisterBinding(binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, matchFunc func(generic.BindingAccessor, admission.Attributes) bool) {
|
||||
namespace, name := binding.Namespace, binding.Name
|
||||
key := types.NamespacedName{
|
||||
Name: name,
|
||||
@@ -644,19 +644,19 @@ func TestReconfigureBinding(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
denyBinding2 := &v1beta1.ValidatingAdmissionPolicyBinding{
|
||||
denyBinding2 := &admissionregistrationv1.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "denybinding.example.com",
|
||||
ResourceVersion: "2",
|
||||
},
|
||||
Spec: v1beta1.ValidatingAdmissionPolicyBindingSpec{
|
||||
Spec: admissionregistrationv1.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: denyPolicy.Name,
|
||||
ParamRef: &v1beta1.ParamRef{
|
||||
ParamRef: &admissionregistrationv1.ParamRef{
|
||||
Name: fakeParams2.GetName(),
|
||||
Namespace: fakeParams2.GetNamespace(),
|
||||
ParameterNotFoundAction: ptrTo(v1beta1.DenyAction),
|
||||
ParameterNotFoundAction: ptrTo(admissionregistrationv1.DenyAction),
|
||||
},
|
||||
ValidationActions: []v1beta1.ValidationAction{v1beta1.Deny},
|
||||
ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Deny},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -805,7 +805,7 @@ func TestInvalidParamSourceGVK(t *testing.T) {
|
||||
passedParams := make(chan *unstructured.Unstructured)
|
||||
|
||||
badPolicy := *denyPolicy
|
||||
badPolicy.Spec.ParamKind = &v1beta1.ParamKind{
|
||||
badPolicy.Spec.ParamKind = &admissionregistrationv1.ParamKind{
|
||||
APIVersion: paramsGVK.GroupVersion().String(),
|
||||
Kind: "BadParamKind",
|
||||
}
|
||||
@@ -985,13 +985,13 @@ func TestMultiplePoliciesSharedParamType(t *testing.T) {
|
||||
// Use ConfigMap native-typed param
|
||||
policy1 := *denyPolicy
|
||||
policy1.Name = "denypolicy1.example.com"
|
||||
policy1.Spec = v1beta1.ValidatingAdmissionPolicySpec{
|
||||
ParamKind: &v1beta1.ParamKind{
|
||||
policy1.Spec = admissionregistrationv1.ValidatingAdmissionPolicySpec{
|
||||
ParamKind: &admissionregistrationv1.ParamKind{
|
||||
APIVersion: paramsGVK.GroupVersion().String(),
|
||||
Kind: paramsGVK.Kind,
|
||||
},
|
||||
FailurePolicy: ptrTo(v1beta1.Fail),
|
||||
Validations: []v1beta1.Validation{
|
||||
FailurePolicy: ptrTo(admissionregistrationv1.Fail),
|
||||
Validations: []admissionregistrationv1.Validation{
|
||||
{
|
||||
Expression: "policy1",
|
||||
},
|
||||
@@ -1000,13 +1000,13 @@ func TestMultiplePoliciesSharedParamType(t *testing.T) {
|
||||
|
||||
policy2 := *denyPolicy
|
||||
policy2.Name = "denypolicy2.example.com"
|
||||
policy2.Spec = v1beta1.ValidatingAdmissionPolicySpec{
|
||||
ParamKind: &v1beta1.ParamKind{
|
||||
policy2.Spec = admissionregistrationv1.ValidatingAdmissionPolicySpec{
|
||||
ParamKind: &admissionregistrationv1.ParamKind{
|
||||
APIVersion: paramsGVK.GroupVersion().String(),
|
||||
Kind: paramsGVK.Kind,
|
||||
},
|
||||
FailurePolicy: ptrTo(v1beta1.Fail),
|
||||
Validations: []v1beta1.Validation{
|
||||
FailurePolicy: ptrTo(admissionregistrationv1.Fail),
|
||||
Validations: []admissionregistrationv1.Validation{
|
||||
{
|
||||
Expression: "policy2",
|
||||
},
|
||||
@@ -1106,7 +1106,7 @@ func TestNativeTypeParam(t *testing.T) {
|
||||
|
||||
// Use ConfigMap native-typed param
|
||||
nativeTypeParamPolicy := *denyPolicy
|
||||
nativeTypeParamPolicy.Spec.ParamKind = &v1beta1.ParamKind{
|
||||
nativeTypeParamPolicy.Spec.ParamKind = &admissionregistrationv1.ParamKind{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
}
|
||||
@@ -1208,7 +1208,7 @@ func TestAuditValidationAction(t *testing.T) {
|
||||
expected := []validating.ValidationFailureValue{{
|
||||
ExpressionIndex: 0,
|
||||
Message: "I'm sorry Dave",
|
||||
ValidationActions: []v1beta1.ValidationAction{v1beta1.Audit},
|
||||
ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Audit},
|
||||
Binding: "denybinding.example.com",
|
||||
Policy: noParamSourcePolicy.Name,
|
||||
}}
|
||||
@@ -1305,7 +1305,7 @@ func TestAllValidationActions(t *testing.T) {
|
||||
expected := []validating.ValidationFailureValue{{
|
||||
ExpressionIndex: 0,
|
||||
Message: "I'm sorry Dave",
|
||||
ValidationActions: []v1beta1.ValidationAction{v1beta1.Deny, v1beta1.Warn, v1beta1.Audit},
|
||||
ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Deny, admissionregistrationv1.Warn, admissionregistrationv1.Audit},
|
||||
Binding: "denybinding.example.com",
|
||||
Policy: noParamSourcePolicy.Name,
|
||||
}}
|
||||
@@ -1325,13 +1325,13 @@ func TestNamespaceParamRefName(t *testing.T) {
|
||||
|
||||
// Use ConfigMap native-typed param
|
||||
nativeTypeParamPolicy := *denyPolicy
|
||||
nativeTypeParamPolicy.Spec.ParamKind = &v1beta1.ParamKind{
|
||||
nativeTypeParamPolicy.Spec.ParamKind = &admissionregistrationv1.ParamKind{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
}
|
||||
|
||||
namespaceParamBinding := *denyBinding
|
||||
namespaceParamBinding.Spec.ParamRef = &v1beta1.ParamRef{
|
||||
namespaceParamBinding.Spec.ParamRef = &admissionregistrationv1.ParamRef{
|
||||
Name: "replicas-test.example.com",
|
||||
}
|
||||
lock := sync.Mutex{}
|
||||
@@ -1543,7 +1543,7 @@ func testParamRefCase(t *testing.T, paramIsClusterScoped, nameIsSet, namespaceIs
|
||||
// Create a cluster scoped and a namespace scoped CRD
|
||||
policy := *denyPolicy
|
||||
binding := *denyBinding
|
||||
binding.Spec.ParamRef = &v1beta1.ParamRef{}
|
||||
binding.Spec.ParamRef = &admissionregistrationv1.ParamRef{}
|
||||
paramRef := binding.Spec.ParamRef
|
||||
|
||||
shouldErrorOnClusterScopedRequests := !namespaceIsSet && !paramIsClusterScoped
|
||||
@@ -1557,12 +1557,12 @@ func testParamRefCase(t *testing.T, paramIsClusterScoped, nameIsSet, namespaceIs
|
||||
otherNonmatchingLabels := labels.Set{"notaffiliated": "no"}
|
||||
|
||||
if paramIsClusterScoped {
|
||||
policy.Spec.ParamKind = &v1beta1.ParamKind{
|
||||
policy.Spec.ParamKind = &admissionregistrationv1.ParamKind{
|
||||
APIVersion: clusterScopedParamsGVK.GroupVersion().String(),
|
||||
Kind: clusterScopedParamsGVK.Kind,
|
||||
}
|
||||
} else {
|
||||
policy.Spec.ParamKind = &v1beta1.ParamKind{
|
||||
policy.Spec.ParamKind = &admissionregistrationv1.ParamKind{
|
||||
APIVersion: paramsGVK.GroupVersion().String(),
|
||||
Kind: paramsGVK.Kind,
|
||||
}
|
||||
@@ -1581,9 +1581,9 @@ func testParamRefCase(t *testing.T, paramIsClusterScoped, nameIsSet, namespaceIs
|
||||
}
|
||||
|
||||
if denyNotFound {
|
||||
paramRef.ParameterNotFoundAction = ptrTo(v1beta1.DenyAction)
|
||||
paramRef.ParameterNotFoundAction = ptrTo(admissionregistrationv1.DenyAction)
|
||||
} else {
|
||||
paramRef.ParameterNotFoundAction = ptrTo(v1beta1.AllowAction)
|
||||
paramRef.ParameterNotFoundAction = ptrTo(admissionregistrationv1.AllowAction)
|
||||
}
|
||||
|
||||
compiler := &fakeCompiler{}
|
||||
@@ -1815,20 +1815,20 @@ func TestNamespaceParamRefClusterScopedParamError(t *testing.T) {
|
||||
|
||||
// Use ValidatingAdmissionPolicy for param type since it is cluster-scoped
|
||||
nativeTypeParamPolicy := *denyPolicy
|
||||
nativeTypeParamPolicy.Spec.ParamKind = &v1beta1.ParamKind{
|
||||
nativeTypeParamPolicy.Spec.ParamKind = &admissionregistrationv1.ParamKind{
|
||||
APIVersion: "admissionregistration.k8s.io/v1beta1",
|
||||
Kind: "ValidatingAdmissionPolicy",
|
||||
}
|
||||
|
||||
namespaceParamBinding := *denyBinding
|
||||
namespaceParamBinding.Spec.ParamRef = &v1beta1.ParamRef{
|
||||
namespaceParamBinding.Spec.ParamRef = &admissionregistrationv1.ParamRef{
|
||||
Name: "other-param-to-use-with-no-label.example.com",
|
||||
Namespace: "mynamespace",
|
||||
}
|
||||
|
||||
compiler.RegisterDefinition(&nativeTypeParamPolicy, func(ctx context.Context, matchedResource schema.GroupVersionResource, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) validating.ValidateResult {
|
||||
evaluations.Add(1)
|
||||
if _, ok := versionedParams.(*v1beta1.ValidatingAdmissionPolicy); ok {
|
||||
if _, ok := versionedParams.(*admissionregistrationv1.ValidatingAdmissionPolicy); ok {
|
||||
return validating.ValidateResult{
|
||||
Decisions: []validating.PolicyDecision{
|
||||
{
|
||||
|
@@ -22,7 +22,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -59,8 +59,8 @@ func NewDispatcher(
|
||||
// that determined the decision
|
||||
type policyDecisionWithMetadata struct {
|
||||
PolicyDecision
|
||||
Definition *v1beta1.ValidatingAdmissionPolicy
|
||||
Binding *v1beta1.ValidatingAdmissionPolicyBinding
|
||||
Definition *admissionregistrationv1.ValidatingAdmissionPolicy
|
||||
Binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding
|
||||
}
|
||||
|
||||
// Dispatch implements generic.Dispatcher.
|
||||
@@ -68,21 +68,21 @@ func (c *dispatcher) Dispatch(ctx context.Context, a admission.Attributes, o adm
|
||||
|
||||
var deniedDecisions []policyDecisionWithMetadata
|
||||
|
||||
addConfigError := func(err error, definition *v1beta1.ValidatingAdmissionPolicy, binding *v1beta1.ValidatingAdmissionPolicyBinding) {
|
||||
addConfigError := func(err error, definition *admissionregistrationv1.ValidatingAdmissionPolicy, binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding) {
|
||||
// we always default the FailurePolicy if it is unset and validate it in API level
|
||||
var policy v1beta1.FailurePolicyType
|
||||
var policy admissionregistrationv1.FailurePolicyType
|
||||
if definition.Spec.FailurePolicy == nil {
|
||||
policy = v1beta1.Fail
|
||||
policy = admissionregistrationv1.Fail
|
||||
} else {
|
||||
policy = *definition.Spec.FailurePolicy
|
||||
}
|
||||
|
||||
// apply FailurePolicy specified in ValidatingAdmissionPolicy, the default would be Fail
|
||||
switch policy {
|
||||
case v1beta1.Ignore:
|
||||
case admissionregistrationv1.Ignore:
|
||||
// TODO: add metrics for ignored error here
|
||||
return
|
||||
case v1beta1.Fail:
|
||||
case admissionregistrationv1.Fail:
|
||||
var message string
|
||||
if binding == nil {
|
||||
message = fmt.Errorf("failed to configure policy: %w", err).Error()
|
||||
@@ -228,17 +228,17 @@ func (c *dispatcher) Dispatch(ctx context.Context, a admission.Attributes, o adm
|
||||
case ActionDeny:
|
||||
for _, action := range binding.Spec.ValidationActions {
|
||||
switch action {
|
||||
case v1beta1.Deny:
|
||||
case admissionregistrationv1.Deny:
|
||||
deniedDecisions = append(deniedDecisions, policyDecisionWithMetadata{
|
||||
Definition: definition,
|
||||
Binding: binding,
|
||||
PolicyDecision: decision,
|
||||
})
|
||||
celmetrics.Metrics.ObserveRejection(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
case v1beta1.Audit:
|
||||
case admissionregistrationv1.Audit:
|
||||
publishValidationFailureAnnotation(binding, i, decision, versionedAttr)
|
||||
celmetrics.Metrics.ObserveAudit(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
case v1beta1.Warn:
|
||||
case admissionregistrationv1.Warn:
|
||||
warning.AddWarning(ctx, "", fmt.Sprintf("Validation failed for ValidatingAdmissionPolicy '%s' with binding '%s': %s", definition.Name, binding.Name, decision.Message))
|
||||
celmetrics.Metrics.ObserveWarn(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
}
|
||||
@@ -302,7 +302,7 @@ func (c *dispatcher) Dispatch(ctx context.Context, a admission.Attributes, o adm
|
||||
return nil
|
||||
}
|
||||
|
||||
func publishValidationFailureAnnotation(binding *v1beta1.ValidatingAdmissionPolicyBinding, expressionIndex int, decision PolicyDecision, attributes admission.Attributes) {
|
||||
func publishValidationFailureAnnotation(binding *admissionregistrationv1.ValidatingAdmissionPolicyBinding, expressionIndex int, decision PolicyDecision, attributes admission.Attributes) {
|
||||
key := "validation.policy.admission.k8s.io/validation_failure"
|
||||
// Marshal to a list of failures since, in the future, we may need to support multiple failures
|
||||
valueJSON, err := utiljson.Marshal([]ValidationFailureValue{{
|
||||
@@ -330,7 +330,7 @@ type ValidationFailureValue struct {
|
||||
Policy string `json:"policy"`
|
||||
Binding string `json:"binding"`
|
||||
ExpressionIndex int `json:"expressionIndex"`
|
||||
ValidationActions []v1beta1.ValidationAction `json:"validationActions"`
|
||||
ValidationActions []admissionregistrationv1.ValidationAction `json:"validationActions"`
|
||||
}
|
||||
|
||||
type auditAnnotationCollector struct {
|
||||
|
@@ -21,7 +21,6 @@ import (
|
||||
"io"
|
||||
|
||||
v1 "k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/initializer"
|
||||
@@ -62,8 +61,8 @@ func Register(plugins *admission.Plugins) {
|
||||
}
|
||||
|
||||
// Plugin is an implementation of admission.Interface.
|
||||
type Policy = v1beta1.ValidatingAdmissionPolicy
|
||||
type PolicyBinding = v1beta1.ValidatingAdmissionPolicyBinding
|
||||
type Policy = v1.ValidatingAdmissionPolicy
|
||||
type PolicyBinding = v1.ValidatingAdmissionPolicyBinding
|
||||
type PolicyEvaluator = Validator
|
||||
type PolicyHook = generic.PolicyHook[*Policy, *PolicyBinding, PolicyEvaluator]
|
||||
|
||||
@@ -84,8 +83,8 @@ func NewPlugin(_ io.Reader) *Plugin {
|
||||
handler,
|
||||
func(f informers.SharedInformerFactory, client kubernetes.Interface, dynamicClient dynamic.Interface, restMapper meta.RESTMapper) generic.Source[PolicyHook] {
|
||||
return generic.NewPolicySource(
|
||||
f.Admissionregistration().V1beta1().ValidatingAdmissionPolicies().Informer(),
|
||||
f.Admissionregistration().V1beta1().ValidatingAdmissionPolicyBindings().Informer(),
|
||||
f.Admissionregistration().V1().ValidatingAdmissionPolicies().Informer(),
|
||||
f.Admissionregistration().V1().ValidatingAdmissionPolicyBindings().Informer(),
|
||||
NewValidatingAdmissionPolicyAccessor,
|
||||
NewValidatingAdmissionPolicyBindingAccessor,
|
||||
compilePolicy,
|
||||
@@ -117,7 +116,7 @@ func compilePolicy(policy *Policy) Validator {
|
||||
}
|
||||
optionalVars := cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: true}
|
||||
expressionOptionalVars := cel.OptionalVariableDeclarations{HasParams: hasParam, HasAuthorizer: false}
|
||||
failurePolicy := convertv1beta1FailurePolicyTypeTov1FailurePolicyType(policy.Spec.FailurePolicy)
|
||||
failurePolicy := policy.Spec.FailurePolicy
|
||||
var matcher matchconditions.Matcher = nil
|
||||
matchConditions := policy.Spec.MatchConditions
|
||||
|
||||
@@ -132,31 +131,17 @@ func compilePolicy(policy *Policy) Validator {
|
||||
matcher = matchconditions.NewMatcher(filterCompiler.Compile(matchExpressionAccessors, optionalVars, environment.StoredExpressions), failurePolicy, "policy", "validate", policy.Name)
|
||||
}
|
||||
res := NewValidator(
|
||||
filterCompiler.Compile(convertv1beta1Validations(policy.Spec.Validations), optionalVars, environment.StoredExpressions),
|
||||
filterCompiler.Compile(convertv1Validations(policy.Spec.Validations), optionalVars, environment.StoredExpressions),
|
||||
matcher,
|
||||
filterCompiler.Compile(convertv1beta1AuditAnnotations(policy.Spec.AuditAnnotations), optionalVars, environment.StoredExpressions),
|
||||
filterCompiler.Compile(convertv1beta1MessageExpressions(policy.Spec.Validations), expressionOptionalVars, environment.StoredExpressions),
|
||||
filterCompiler.Compile(convertv1AuditAnnotations(policy.Spec.AuditAnnotations), optionalVars, environment.StoredExpressions),
|
||||
filterCompiler.Compile(convertv1MessageExpressions(policy.Spec.Validations), expressionOptionalVars, environment.StoredExpressions),
|
||||
failurePolicy,
|
||||
)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func convertv1beta1FailurePolicyTypeTov1FailurePolicyType(policyType *v1beta1.FailurePolicyType) *v1.FailurePolicyType {
|
||||
if policyType == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var v1FailPolicy v1.FailurePolicyType
|
||||
if *policyType == v1beta1.Fail {
|
||||
v1FailPolicy = v1.Fail
|
||||
} else if *policyType == v1beta1.Ignore {
|
||||
v1FailPolicy = v1.Ignore
|
||||
}
|
||||
return &v1FailPolicy
|
||||
}
|
||||
|
||||
func convertv1beta1Validations(inputValidations []v1beta1.Validation) []cel.ExpressionAccessor {
|
||||
func convertv1Validations(inputValidations []v1.Validation) []cel.ExpressionAccessor {
|
||||
celExpressionAccessor := make([]cel.ExpressionAccessor, len(inputValidations))
|
||||
for i, validation := range inputValidations {
|
||||
validation := ValidationCondition{
|
||||
@@ -169,7 +154,7 @@ func convertv1beta1Validations(inputValidations []v1beta1.Validation) []cel.Expr
|
||||
return celExpressionAccessor
|
||||
}
|
||||
|
||||
func convertv1beta1MessageExpressions(inputValidations []v1beta1.Validation) []cel.ExpressionAccessor {
|
||||
func convertv1MessageExpressions(inputValidations []v1.Validation) []cel.ExpressionAccessor {
|
||||
celExpressionAccessor := make([]cel.ExpressionAccessor, len(inputValidations))
|
||||
for i, validation := range inputValidations {
|
||||
if validation.MessageExpression != "" {
|
||||
@@ -182,7 +167,7 @@ func convertv1beta1MessageExpressions(inputValidations []v1beta1.Validation) []c
|
||||
return celExpressionAccessor
|
||||
}
|
||||
|
||||
func convertv1beta1AuditAnnotations(inputValidations []v1beta1.AuditAnnotation) []cel.ExpressionAccessor {
|
||||
func convertv1AuditAnnotations(inputValidations []v1.AuditAnnotation) []cel.ExpressionAccessor {
|
||||
celExpressionAccessor := make([]cel.ExpressionAccessor, len(inputValidations))
|
||||
for i, validation := range inputValidations {
|
||||
validation := AuditAnnotationCondition{
|
||||
@@ -194,7 +179,7 @@ func convertv1beta1AuditAnnotations(inputValidations []v1beta1.AuditAnnotation)
|
||||
return celExpressionAccessor
|
||||
}
|
||||
|
||||
func convertv1beta1Variables(variables []v1beta1.Variable) []cel.NamedExpressionAccessor {
|
||||
func convertv1beta1Variables(variables []v1.Variable) []cel.NamedExpressionAccessor {
|
||||
namedExpressions := make([]cel.NamedExpressionAccessor, len(variables))
|
||||
for i, variable := range variables {
|
||||
namedExpressions[i] = &Variable{Name: variable.Name, Expression: variable.Expression}
|
||||
|
@@ -25,7 +25,7 @@ import (
|
||||
|
||||
"github.com/google/cel-go/cel"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
@@ -57,7 +57,7 @@ type TypeCheckingContext struct {
|
||||
paramGVK schema.GroupVersionKind
|
||||
paramDeclType *apiservercel.DeclType
|
||||
|
||||
variables []v1beta1.Variable
|
||||
variables []v1.Variable
|
||||
}
|
||||
|
||||
type typeOverwrite struct {
|
||||
@@ -105,18 +105,18 @@ func (r *TypeCheckingResult) String() string {
|
||||
// as []ExpressionWarning that is ready to be set in policy.Status
|
||||
// The result is nil if type checking returns no warning.
|
||||
// The policy object is NOT mutated. The caller should update Status accordingly
|
||||
func (c *TypeChecker) Check(policy *v1beta1.ValidatingAdmissionPolicy) []v1beta1.ExpressionWarning {
|
||||
func (c *TypeChecker) Check(policy *v1.ValidatingAdmissionPolicy) []v1.ExpressionWarning {
|
||||
ctx := c.CreateContext(policy)
|
||||
|
||||
// warnings to return, note that the capacity is optimistically set to zero
|
||||
var warnings []v1beta1.ExpressionWarning // intentionally not setting capacity
|
||||
var warnings []v1.ExpressionWarning // intentionally not setting capacity
|
||||
|
||||
// check main validation expressions and their message expressions, located in spec.validations[*]
|
||||
fieldRef := field.NewPath("spec", "validations")
|
||||
for i, v := range policy.Spec.Validations {
|
||||
results := c.CheckExpression(ctx, v.Expression)
|
||||
if len(results) != 0 {
|
||||
warnings = append(warnings, v1beta1.ExpressionWarning{
|
||||
warnings = append(warnings, v1.ExpressionWarning{
|
||||
FieldRef: fieldRef.Index(i).Child("expression").String(),
|
||||
Warning: results.String(),
|
||||
})
|
||||
@@ -127,7 +127,7 @@ func (c *TypeChecker) Check(policy *v1beta1.ValidatingAdmissionPolicy) []v1beta1
|
||||
}
|
||||
results = c.CheckExpression(ctx, v.MessageExpression)
|
||||
if len(results) != 0 {
|
||||
warnings = append(warnings, v1beta1.ExpressionWarning{
|
||||
warnings = append(warnings, v1.ExpressionWarning{
|
||||
FieldRef: fieldRef.Index(i).Child("messageExpression").String(),
|
||||
Warning: results.String(),
|
||||
})
|
||||
@@ -138,7 +138,7 @@ func (c *TypeChecker) Check(policy *v1beta1.ValidatingAdmissionPolicy) []v1beta1
|
||||
}
|
||||
|
||||
// CreateContext resolves all types and their schemas from a policy definition and creates the context.
|
||||
func (c *TypeChecker) CreateContext(policy *v1beta1.ValidatingAdmissionPolicy) *TypeCheckingContext {
|
||||
func (c *TypeChecker) CreateContext(policy *v1.ValidatingAdmissionPolicy) *TypeCheckingContext {
|
||||
ctx := new(TypeCheckingContext)
|
||||
allGvks := c.typesToCheck(policy)
|
||||
gvks := make([]schema.GroupVersionKind, 0, len(allGvks))
|
||||
@@ -250,7 +250,7 @@ func (c *TypeChecker) declType(gvk schema.GroupVersionKind) (*apiservercel.DeclT
|
||||
return common.SchemaDeclType(&openapi.Schema{Schema: s}, true).MaybeAssignTypeName(generateUniqueTypeName(gvk.Kind)), nil
|
||||
}
|
||||
|
||||
func (c *TypeChecker) paramsGVK(policy *v1beta1.ValidatingAdmissionPolicy) schema.GroupVersionKind {
|
||||
func (c *TypeChecker) paramsGVK(policy *v1.ValidatingAdmissionPolicy) schema.GroupVersionKind {
|
||||
if policy.Spec.ParamKind == nil {
|
||||
return schema.GroupVersionKind{}
|
||||
}
|
||||
@@ -263,7 +263,7 @@ func (c *TypeChecker) paramsGVK(policy *v1beta1.ValidatingAdmissionPolicy) schem
|
||||
|
||||
// typesToCheck extracts a list of GVKs that needs type checking from the policy
|
||||
// the result is sorted in the order of Group, Version, and Kind
|
||||
func (c *TypeChecker) typesToCheck(p *v1beta1.ValidatingAdmissionPolicy) []schema.GroupVersionKind {
|
||||
func (c *TypeChecker) typesToCheck(p *v1.ValidatingAdmissionPolicy) []schema.GroupVersionKind {
|
||||
gvks := sets.New[schema.GroupVersionKind]()
|
||||
if p.Spec.MatchConstraints == nil || len(p.Spec.MatchConstraints.ResourceRules) == 0 {
|
||||
return nil
|
||||
@@ -333,7 +333,7 @@ func (c *TypeChecker) typesToCheck(p *v1beta1.ValidatingAdmissionPolicy) []schem
|
||||
return sortGVKList(gvks.UnsortedList())
|
||||
}
|
||||
|
||||
func extractGroups(rule *v1beta1.Rule) []string {
|
||||
func extractGroups(rule *v1.Rule) []string {
|
||||
groups := make([]string, 0, len(rule.APIGroups))
|
||||
for _, group := range rule.APIGroups {
|
||||
// give up if wildcard
|
||||
@@ -345,7 +345,7 @@ func extractGroups(rule *v1beta1.Rule) []string {
|
||||
return groups
|
||||
}
|
||||
|
||||
func extractVersions(rule *v1beta1.Rule) []string {
|
||||
func extractVersions(rule *v1.Rule) []string {
|
||||
versions := make([]string, 0, len(rule.APIVersions))
|
||||
for _, version := range rule.APIVersions {
|
||||
if strings.ContainsAny(version, "*") {
|
||||
@@ -356,7 +356,7 @@ func extractVersions(rule *v1beta1.Rule) []string {
|
||||
return versions
|
||||
}
|
||||
|
||||
func extractResources(rule *v1beta1.Rule) []string {
|
||||
func extractResources(rule *v1.Rule) []string {
|
||||
resources := make([]string, 0, len(rule.Resources))
|
||||
for _, resource := range rule.Resources {
|
||||
// skip wildcard and subresources
|
||||
|
@@ -22,7 +22,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
"k8s.io/api/admissionregistration/v1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
@@ -36,7 +36,7 @@ import (
|
||||
var (
|
||||
scheme *runtime.Scheme = func() *runtime.Scheme {
|
||||
res := runtime.NewScheme()
|
||||
if err := v1beta1.AddToScheme(res); err != nil {
|
||||
if err := v1.AddToScheme(res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -58,21 +58,21 @@ func must3[T any, I any](val T, _ I, err error) T {
|
||||
func TestExtractTypeNames(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
policy *v1beta1.ValidatingAdmissionPolicy
|
||||
policy *v1.ValidatingAdmissionPolicy
|
||||
expected []schema.GroupVersionKind // must be sorted
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{},
|
||||
policy: &v1.ValidatingAdmissionPolicy{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "specific",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
policy: &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"deployments"},
|
||||
@@ -89,19 +89,19 @@ func TestExtractTypeNames(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "multiple",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
policy: &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"deployments"},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{""},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"pods"},
|
||||
@@ -122,11 +122,11 @@ func TestExtractTypeNames(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "all resources",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
policy: &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"*"},
|
||||
@@ -139,11 +139,11 @@ func TestExtractTypeNames(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "sub resources",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
policy: &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"pods/*"},
|
||||
@@ -156,11 +156,11 @@ func TestExtractTypeNames(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "mixtures",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
policy: &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"deployments"},
|
||||
@@ -168,8 +168,8 @@ func TestExtractTypeNames(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"*"},
|
||||
Resources: []string{"deployments"},
|
||||
@@ -196,16 +196,16 @@ func TestExtractTypeNames(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTypeCheck(t *testing.T) {
|
||||
deploymentPolicy := &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
Validations: []v1beta1.Validation{
|
||||
deploymentPolicy := &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
Validations: []v1.Validation{
|
||||
{
|
||||
Expression: "object.foo == 'bar'",
|
||||
},
|
||||
},
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"deployments"},
|
||||
@@ -218,8 +218,8 @@ func TestTypeCheck(t *testing.T) {
|
||||
deploymentPolicyWithBadMessageExpression := deploymentPolicy.DeepCopy()
|
||||
deploymentPolicyWithBadMessageExpression.Spec.Validations[0].MessageExpression = "object.foo + 114514" // confusion
|
||||
|
||||
multiExpressionPolicy := &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
Validations: []v1beta1.Validation{
|
||||
multiExpressionPolicy := &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
Validations: []v1.Validation{
|
||||
{
|
||||
Expression: "object.foo == 'bar'",
|
||||
},
|
||||
@@ -227,10 +227,10 @@ func TestTypeCheck(t *testing.T) {
|
||||
Expression: "object.bar == 'foo'",
|
||||
},
|
||||
},
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"deployments"},
|
||||
@@ -239,20 +239,20 @@ func TestTypeCheck(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
}}
|
||||
paramsRefPolicy := &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
ParamKind: &v1beta1.ParamKind{
|
||||
paramsRefPolicy := &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
ParamKind: &v1.ParamKind{
|
||||
APIVersion: "v1",
|
||||
Kind: "DoesNotMatter",
|
||||
},
|
||||
Validations: []v1beta1.Validation{
|
||||
Validations: []v1.Validation{
|
||||
{
|
||||
Expression: "object.foo == params.bar",
|
||||
},
|
||||
},
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"deployments"},
|
||||
@@ -261,16 +261,16 @@ func TestTypeCheck(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
}}
|
||||
authorizerPolicy := &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
Validations: []v1beta1.Validation{
|
||||
authorizerPolicy := &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
Validations: []v1.Validation{
|
||||
{
|
||||
Expression: "authorizer.group('').resource('endpoints').check('create').allowed()",
|
||||
},
|
||||
},
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"deployments"},
|
||||
@@ -279,16 +279,16 @@ func TestTypeCheck(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
}}
|
||||
authorizerInvalidPolicy := &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
Validations: []v1beta1.Validation{
|
||||
authorizerInvalidPolicy := &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
Validations: []v1.Validation{
|
||||
{
|
||||
Expression: "authorizer.allowed()",
|
||||
},
|
||||
},
|
||||
MatchConstraints: &v1beta1.MatchResources{ResourceRules: []v1beta1.NamedRuleWithOperations{
|
||||
MatchConstraints: &v1.MatchResources{ResourceRules: []v1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: v1beta1.RuleWithOperations{
|
||||
Rule: v1beta1.Rule{
|
||||
RuleWithOperations: v1.RuleWithOperations{
|
||||
Rule: v1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"deployments"},
|
||||
@@ -300,12 +300,12 @@ func TestTypeCheck(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
schemaToReturn *spec.Schema
|
||||
policy *v1beta1.ValidatingAdmissionPolicy
|
||||
policy *v1.ValidatingAdmissionPolicy
|
||||
assertions []assertionFunc
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{},
|
||||
policy: &v1.ValidatingAdmissionPolicy{},
|
||||
assertions: []assertionFunc{toBeEmpty},
|
||||
},
|
||||
{
|
||||
@@ -439,14 +439,14 @@ func TestTypeCheck(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "variables valid",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
Variables: []v1beta1.Variable{
|
||||
policy: &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
Variables: []v1.Variable{
|
||||
{
|
||||
Name: "works",
|
||||
Expression: "true",
|
||||
},
|
||||
},
|
||||
Validations: []v1beta1.Validation{
|
||||
Validations: []v1.Validation{
|
||||
{
|
||||
Expression: "variables.works",
|
||||
},
|
||||
@@ -466,14 +466,14 @@ func TestTypeCheck(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "variables missing field",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
Variables: []v1beta1.Variable{
|
||||
policy: &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
Variables: []v1.Variable{
|
||||
{
|
||||
Name: "works",
|
||||
Expression: "true",
|
||||
},
|
||||
},
|
||||
Validations: []v1beta1.Validation{
|
||||
Validations: []v1.Validation{
|
||||
{
|
||||
Expression: "variables.nonExisting",
|
||||
},
|
||||
@@ -497,14 +497,14 @@ func TestTypeCheck(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "variables field wrong type",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
Variables: []v1beta1.Variable{
|
||||
policy: &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
Variables: []v1.Variable{
|
||||
{
|
||||
Name: "name",
|
||||
Expression: "'something'",
|
||||
},
|
||||
},
|
||||
Validations: []v1beta1.Validation{
|
||||
Validations: []v1.Validation{
|
||||
{
|
||||
Expression: "variables.name == object.foo", // foo is int64
|
||||
},
|
||||
@@ -528,14 +528,14 @@ func TestTypeCheck(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "error in variables, not reported during type checking.",
|
||||
policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{
|
||||
Variables: []v1beta1.Variable{
|
||||
policy: &v1.ValidatingAdmissionPolicy{Spec: v1.ValidatingAdmissionPolicySpec{
|
||||
Variables: []v1.Variable{
|
||||
{
|
||||
Name: "name",
|
||||
Expression: "object.foo == 'str'",
|
||||
},
|
||||
},
|
||||
Validations: []v1beta1.Validation{
|
||||
Validations: []v1.Validation{
|
||||
{
|
||||
Expression: "variables.name == object.foo", // foo is int64
|
||||
},
|
||||
@@ -593,14 +593,14 @@ func (r *fakeSchemaResolver) ResolveSchema(gvk schema.GroupVersionKind) (*spec.S
|
||||
return r.schemaToReturn, nil
|
||||
}
|
||||
|
||||
func toBeEmpty(warnings []v1beta1.ExpressionWarning, t *testing.T) {
|
||||
func toBeEmpty(warnings []v1.ExpressionWarning, t *testing.T) {
|
||||
if len(warnings) != 0 {
|
||||
t.Fatalf("expected empty but got %v", warnings)
|
||||
}
|
||||
}
|
||||
|
||||
func toContain(substring string) func(warnings []v1beta1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []v1beta1.ExpressionWarning, t *testing.T) {
|
||||
func toContain(substring string) func(warnings []v1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []v1.ExpressionWarning, t *testing.T) {
|
||||
if len(warnings) == 0 {
|
||||
t.Errorf("expected containing %q but got empty", substring)
|
||||
}
|
||||
@@ -612,8 +612,8 @@ func toContain(substring string) func(warnings []v1beta1.ExpressionWarning, t *t
|
||||
}
|
||||
}
|
||||
|
||||
func toHaveLengthOf(expected int) func(warnings []v1beta1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []v1beta1.ExpressionWarning, t *testing.T) {
|
||||
func toHaveLengthOf(expected int) func(warnings []v1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []v1.ExpressionWarning, t *testing.T) {
|
||||
got := len(warnings)
|
||||
if expected != got {
|
||||
t.Errorf("expect warnings to have length of %d, but got %d", expected, got)
|
||||
@@ -621,8 +621,8 @@ func toHaveLengthOf(expected int) func(warnings []v1beta1.ExpressionWarning, t *
|
||||
}
|
||||
}
|
||||
|
||||
func toHaveFieldRef(paths ...string) func(warnings []v1beta1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []v1beta1.ExpressionWarning, t *testing.T) {
|
||||
func toHaveFieldRef(paths ...string) func(warnings []v1.ExpressionWarning, t *testing.T) {
|
||||
return func(warnings []v1.ExpressionWarning, t *testing.T) {
|
||||
if len(paths) != len(warnings) {
|
||||
t.Errorf("expect warnings to have length of %d, but got %d", len(paths), len(warnings))
|
||||
}
|
||||
@@ -634,4 +634,4 @@ func toHaveFieldRef(paths ...string) func(warnings []v1beta1.ExpressionWarning,
|
||||
}
|
||||
}
|
||||
|
||||
type assertionFunc func(warnings []v1beta1.ExpressionWarning, t *testing.T)
|
||||
type assertionFunc func(warnings []v1.ExpressionWarning, t *testing.T)
|
||||
|
@@ -101,7 +101,10 @@ const (
|
||||
// owner: @cici37 @jpbetz
|
||||
// kep: http://kep.k8s.io/3488
|
||||
// alpha: v1.26
|
||||
// beta: v1.28
|
||||
// stable: v1.30
|
||||
//
|
||||
// Note: the feature gate can be removed in 1.32
|
||||
// Enables expression validation in Admission Control
|
||||
ValidatingAdmissionPolicy featuregate.Feature = "ValidatingAdmissionPolicy"
|
||||
|
||||
@@ -309,7 +312,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
||||
|
||||
APIServingWithRoutine: {Default: true, PreRelease: featuregate.Beta},
|
||||
|
||||
ValidatingAdmissionPolicy: {Default: false, PreRelease: featuregate.Beta},
|
||||
ValidatingAdmissionPolicy: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.32
|
||||
|
||||
CustomResourceValidationExpressions: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.31
|
||||
|
||||
|
@@ -19,12 +19,14 @@ package options
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilwait "k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/initializer"
|
||||
admissionmetrics "k8s.io/apiserver/pkg/admission/metrics"
|
||||
@@ -36,9 +38,11 @@ import (
|
||||
apiserverapiv1 "k8s.io/apiserver/pkg/apis/apiserver/v1"
|
||||
apiserverapiv1alpha1 "k8s.io/apiserver/pkg/apis/apiserver/v1alpha1"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
cacheddiscovery "k8s.io/client-go/discovery/cached/memory"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/restmapper"
|
||||
"k8s.io/component-base/featuregate"
|
||||
)
|
||||
|
||||
@@ -143,11 +147,24 @@ func (a *AdmissionOptions) ApplyTo(
|
||||
return fmt.Errorf("failed to read plugin config: %v", err)
|
||||
}
|
||||
|
||||
discoveryClient := cacheddiscovery.NewMemCacheClient(kubeClient.Discovery())
|
||||
discoveryRESTMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient)
|
||||
genericInitializer := initializer.New(kubeClient, dynamicClient, informers, c.Authorization.Authorizer, features,
|
||||
c.DrainedNotify())
|
||||
c.DrainedNotify(), discoveryRESTMapper)
|
||||
initializersChain := admission.PluginInitializers{genericInitializer}
|
||||
initializersChain = append(initializersChain, pluginInitializers...)
|
||||
|
||||
admissionPostStartHook := func(context server.PostStartHookContext) error {
|
||||
discoveryRESTMapper.Reset()
|
||||
go utilwait.Until(discoveryRESTMapper.Reset, 30*time.Second, context.StopCh)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = c.AddPostStartHook("start-apiserver-admission-initializer", admissionPostStartHook)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to add post start hook for policy admission: %w", err)
|
||||
}
|
||||
|
||||
admissionChain, err := a.Plugins.NewFromPlugins(pluginNames, pluginsConfigProvider, initializersChain, a.Decorators)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -37,7 +37,7 @@ var (
|
||||
"[+]ping ok",
|
||||
"[+]log ok",
|
||||
"[+]etcd ok",
|
||||
"[+]poststarthook/start-kube-apiserver-admission-initializer ok",
|
||||
"[+]poststarthook/start-apiserver-admission-initializer ok",
|
||||
"[+]poststarthook/generic-apiserver-start-informers ok",
|
||||
"[+]poststarthook/start-apiextensions-informers ok",
|
||||
"[+]poststarthook/start-apiextensions-controllers ok",
|
||||
@@ -58,7 +58,7 @@ var (
|
||||
"[+]ping ok",
|
||||
"[+]log ok",
|
||||
"[+]etcd ok",
|
||||
"[+]poststarthook/start-kube-apiserver-admission-initializer ok",
|
||||
"[+]poststarthook/start-apiserver-admission-initializer ok",
|
||||
"[+]poststarthook/generic-apiserver-start-informers ok",
|
||||
"[+]poststarthook/start-apiextensions-informers ok",
|
||||
"[+]poststarthook/start-apiextensions-controllers ok",
|
||||
@@ -80,7 +80,7 @@ var (
|
||||
"[+]log ok",
|
||||
"[+]etcd ok",
|
||||
"[+]informer-sync ok",
|
||||
"[+]poststarthook/start-kube-apiserver-admission-initializer ok",
|
||||
"[+]poststarthook/start-apiserver-admission-initializer ok",
|
||||
"[+]poststarthook/generic-apiserver-start-informers ok",
|
||||
"[+]poststarthook/start-apiextensions-informers ok",
|
||||
"[+]poststarthook/start-apiextensions-controllers ok",
|
||||
|
@@ -18,14 +18,12 @@ package apimachinery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
"github.com/onsi/gomega"
|
||||
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
@@ -52,11 +50,6 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin]", frame
|
||||
var err error
|
||||
client, err = clientset.NewForConfig(f.ClientConfig())
|
||||
framework.ExpectNoError(err, "initializing client")
|
||||
_, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().List(context.Background(), metav1.ListOptions{})
|
||||
if apierrors.IsNotFound(err) {
|
||||
// TODO: feature check should fail after GA graduation
|
||||
ginkgo.Skip(fmt.Sprintf("server does not support ValidatingAdmissionPolicy v1beta1: %v, feature gate not enabled?", err))
|
||||
}
|
||||
extensionsClient, err = apiextensionsclientset.NewForConfig(f.ClientConfig())
|
||||
framework.ExpectNoError(err, "initializing api-extensions client")
|
||||
})
|
||||
@@ -76,25 +69,25 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin]", frame
|
||||
StartResourceRule().
|
||||
MatchResource([]string{"apps"}, []string{"v1"}, []string{"deployments"}).
|
||||
EndResourceRule().
|
||||
WithValidation(admissionregistrationv1beta1.Validation{
|
||||
WithValidation(admissionregistrationv1.Validation{
|
||||
Expression: "object.spec.replicas > 1",
|
||||
MessageExpression: "'wants replicas > 1, got ' + object.spec.replicas",
|
||||
}).
|
||||
WithValidation(admissionregistrationv1beta1.Validation{
|
||||
WithValidation(admissionregistrationv1.Validation{
|
||||
Expression: "namespaceObject.metadata.name == '" + f.UniqueName + "'",
|
||||
Message: "Internal error! Other namespace should not be allowed.",
|
||||
}).
|
||||
Build()
|
||||
policy, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
policy, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err, "create policy")
|
||||
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
|
||||
return client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
return client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}, policy.Name)
|
||||
binding := createBinding(f.UniqueName+".binding.example.com", f.UniqueName, policy.Name)
|
||||
binding, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{})
|
||||
binding, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err, "create policy binding")
|
||||
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
|
||||
return client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
return client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}, binding.Name)
|
||||
})
|
||||
ginkgo.By("waiting until the marker is denied", func() {
|
||||
@@ -127,27 +120,27 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin]", frame
|
||||
})
|
||||
|
||||
ginkgo.It("should type check validation expressions", func(ctx context.Context) {
|
||||
var policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy
|
||||
var policy *admissionregistrationv1.ValidatingAdmissionPolicy
|
||||
ginkgo.By("creating the policy with correct types", func() {
|
||||
policy = newValidatingAdmissionPolicyBuilder(f.UniqueName+".correct-policy.example.com").
|
||||
MatchUniqueNamespace(f.UniqueName).
|
||||
StartResourceRule().
|
||||
MatchResource([]string{"apps"}, []string{"v1"}, []string{"deployments"}).
|
||||
EndResourceRule().
|
||||
WithValidation(admissionregistrationv1beta1.Validation{
|
||||
WithValidation(admissionregistrationv1.Validation{
|
||||
Expression: "object.spec.replicas > 1",
|
||||
}).
|
||||
Build()
|
||||
var err error
|
||||
policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err, "create policy")
|
||||
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
|
||||
return client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
return client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}, policy.Name)
|
||||
})
|
||||
ginkgo.By("waiting for the type check to finish without any warnings", func() {
|
||||
err := wait.PollUntilContextCancel(ctx, 100*time.Millisecond, true, func(ctx context.Context) (done bool, err error) {
|
||||
policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
|
||||
policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -165,21 +158,21 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin]", frame
|
||||
StartResourceRule().
|
||||
MatchResource([]string{"apps"}, []string{"v1"}, []string{"deployments"}).
|
||||
EndResourceRule().
|
||||
WithValidation(admissionregistrationv1beta1.Validation{
|
||||
WithValidation(admissionregistrationv1.Validation{
|
||||
Expression: "object.spec.replicas > '1'", // confusion: int > string
|
||||
MessageExpression: "'wants replicas > 1, got ' + object.spec.replicas", // confusion: string + int
|
||||
}).
|
||||
Build()
|
||||
var err error
|
||||
policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err, "create policy")
|
||||
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
|
||||
return client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
return client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}, policy.Name)
|
||||
})
|
||||
ginkgo.By("waiting for the type check to finish with warnings", func() {
|
||||
err := wait.PollUntilContextCancel(ctx, 100*time.Millisecond, true, func(ctx context.Context) (done bool, err error) {
|
||||
policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
|
||||
policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -208,31 +201,31 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin]", frame
|
||||
StartResourceRule().
|
||||
MatchResource([]string{"apps"}, []string{"v1"}, []string{"deployments"}).
|
||||
EndResourceRule().
|
||||
WithVariable(admissionregistrationv1beta1.Variable{
|
||||
WithVariable(admissionregistrationv1.Variable{
|
||||
Name: "replicas",
|
||||
Expression: "object.spec.replicas",
|
||||
}).
|
||||
WithVariable(admissionregistrationv1beta1.Variable{
|
||||
WithVariable(admissionregistrationv1.Variable{
|
||||
Name: "oddReplicas",
|
||||
Expression: "variables.replicas % 2 == 1",
|
||||
}).
|
||||
WithValidation(admissionregistrationv1beta1.Validation{
|
||||
WithValidation(admissionregistrationv1.Validation{
|
||||
Expression: "variables.replicas > 1",
|
||||
}).
|
||||
WithValidation(admissionregistrationv1beta1.Validation{
|
||||
WithValidation(admissionregistrationv1.Validation{
|
||||
Expression: "variables.oddReplicas",
|
||||
}).
|
||||
Build()
|
||||
policy, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
policy, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err, "create policy")
|
||||
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
|
||||
return client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
return client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}, policy.Name)
|
||||
binding := createBinding(f.UniqueName+".binding.example.com", f.UniqueName, policy.Name)
|
||||
binding, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{})
|
||||
binding, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err, "create policy binding")
|
||||
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
|
||||
return client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicyBindings().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
return client.AdmissionregistrationV1().ValidatingAdmissionPolicyBindings().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}, binding.Name)
|
||||
})
|
||||
ginkgo.By("waiting until the marker is denied", func() {
|
||||
@@ -268,7 +261,7 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin]", frame
|
||||
crd := crontabExampleCRD()
|
||||
crd.Spec.Group = "stable." + f.UniqueName
|
||||
crd.Name = crd.Spec.Names.Plural + "." + crd.Spec.Group
|
||||
var policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy
|
||||
var policy *admissionregistrationv1.ValidatingAdmissionPolicy
|
||||
ginkgo.By("creating the CRD", func() {
|
||||
var err error
|
||||
crd, err = extensionsClient.ApiextensionsV1().CustomResourceDefinitions().Create(ctx, crd, metav1.CreateOptions{})
|
||||
@@ -290,19 +283,19 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin]", frame
|
||||
StartResourceRule().
|
||||
MatchResource([]string{crd.Spec.Group}, []string{"v1"}, []string{"crontabs"}).
|
||||
EndResourceRule().
|
||||
WithValidation(admissionregistrationv1beta1.Validation{
|
||||
WithValidation(admissionregistrationv1.Validation{
|
||||
Expression: "object.spec.replicas > 1",
|
||||
}).
|
||||
Build()
|
||||
policy, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
policy, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err, "create policy")
|
||||
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
|
||||
return client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
return client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}, policy.Name)
|
||||
})
|
||||
ginkgo.By("waiting for the type check to finish without warnings", func() {
|
||||
err := wait.PollUntilContextCancel(ctx, 100*time.Millisecond, true, func(ctx context.Context) (done bool, err error) {
|
||||
policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
|
||||
policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -320,22 +313,22 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin]", frame
|
||||
StartResourceRule().
|
||||
MatchResource([]string{crd.Spec.Group}, []string{"v1"}, []string{"crontabs"}).
|
||||
EndResourceRule().
|
||||
WithValidation(admissionregistrationv1beta1.Validation{
|
||||
WithValidation(admissionregistrationv1.Validation{
|
||||
Expression: "object.spec.replicas > '1'", // type confusion
|
||||
}).
|
||||
WithValidation(admissionregistrationv1beta1.Validation{
|
||||
WithValidation(admissionregistrationv1.Validation{
|
||||
Expression: "object.spec.maxRetries < 10", // not yet existing field
|
||||
}).
|
||||
Build()
|
||||
policy, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
policy, err := client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
|
||||
framework.ExpectNoError(err, "create policy")
|
||||
ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
|
||||
return client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
return client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
|
||||
}, policy.Name)
|
||||
})
|
||||
ginkgo.By("waiting for the type check to finish with warnings", func() {
|
||||
err := wait.PollUntilContextCancel(ctx, 100*time.Millisecond, true, func(ctx context.Context) (done bool, err error) {
|
||||
policy, err = client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
|
||||
policy, err = client.AdmissionregistrationV1().ValidatingAdmissionPolicies().Get(ctx, policy.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -357,17 +350,17 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin]", frame
|
||||
})
|
||||
})
|
||||
|
||||
func createBinding(bindingName string, uniqueLabel string, policyName string) *admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding {
|
||||
return &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{
|
||||
func createBinding(bindingName string, uniqueLabel string, policyName string) *admissionregistrationv1.ValidatingAdmissionPolicyBinding {
|
||||
return &admissionregistrationv1.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: bindingName},
|
||||
Spec: admissionregistrationv1beta1.ValidatingAdmissionPolicyBindingSpec{
|
||||
Spec: admissionregistrationv1.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: policyName,
|
||||
MatchResources: &admissionregistrationv1beta1.MatchResources{
|
||||
MatchResources: &admissionregistrationv1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{uniqueLabel: "true"},
|
||||
},
|
||||
},
|
||||
ValidationActions: []admissionregistrationv1beta1.ValidationAction{admissionregistrationv1beta1.Deny},
|
||||
ValidationActions: []admissionregistrationv1.ValidationAction{admissionregistrationv1.Deny},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -427,17 +420,17 @@ func basicReplicaSet(name string, replicas int32) *appsv1.ReplicaSet {
|
||||
}
|
||||
|
||||
type validatingAdmissionPolicyBuilder struct {
|
||||
policy *admissionregistrationv1beta1.ValidatingAdmissionPolicy
|
||||
policy *admissionregistrationv1.ValidatingAdmissionPolicy
|
||||
}
|
||||
|
||||
type resourceRuleBuilder struct {
|
||||
policyBuilder *validatingAdmissionPolicyBuilder
|
||||
resourceRule *admissionregistrationv1beta1.NamedRuleWithOperations
|
||||
resourceRule *admissionregistrationv1.NamedRuleWithOperations
|
||||
}
|
||||
|
||||
func newValidatingAdmissionPolicyBuilder(policyName string) *validatingAdmissionPolicyBuilder {
|
||||
return &validatingAdmissionPolicyBuilder{
|
||||
policy: &admissionregistrationv1beta1.ValidatingAdmissionPolicy{
|
||||
policy: &admissionregistrationv1.ValidatingAdmissionPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: policyName},
|
||||
},
|
||||
}
|
||||
@@ -445,7 +438,7 @@ func newValidatingAdmissionPolicyBuilder(policyName string) *validatingAdmission
|
||||
|
||||
func (b *validatingAdmissionPolicyBuilder) MatchUniqueNamespace(uniqueLabel string) *validatingAdmissionPolicyBuilder {
|
||||
if b.policy.Spec.MatchConstraints == nil {
|
||||
b.policy.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{}
|
||||
b.policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{}
|
||||
}
|
||||
b.policy.Spec.MatchConstraints.NamespaceSelector = &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
@@ -458,10 +451,10 @@ func (b *validatingAdmissionPolicyBuilder) MatchUniqueNamespace(uniqueLabel stri
|
||||
func (b *validatingAdmissionPolicyBuilder) StartResourceRule() *resourceRuleBuilder {
|
||||
return &resourceRuleBuilder{
|
||||
policyBuilder: b,
|
||||
resourceRule: &admissionregistrationv1beta1.NamedRuleWithOperations{
|
||||
RuleWithOperations: admissionregistrationv1beta1.RuleWithOperations{
|
||||
resourceRule: &admissionregistrationv1.NamedRuleWithOperations{
|
||||
RuleWithOperations: admissionregistrationv1.RuleWithOperations{
|
||||
Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create, admissionregistrationv1.Update},
|
||||
Rule: admissionregistrationv1beta1.Rule{
|
||||
Rule: admissionregistrationv1.Rule{
|
||||
APIGroups: []string{"apps"},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"deployments"},
|
||||
@@ -477,7 +470,7 @@ func (rb *resourceRuleBuilder) CreateAndUpdate() *resourceRuleBuilder {
|
||||
}
|
||||
|
||||
func (rb *resourceRuleBuilder) MatchResource(groups []string, versions []string, resources []string) *resourceRuleBuilder {
|
||||
rb.resourceRule.Rule = admissionregistrationv1beta1.Rule{
|
||||
rb.resourceRule.Rule = admissionregistrationv1.Rule{
|
||||
APIGroups: groups,
|
||||
APIVersions: versions,
|
||||
Resources: resources,
|
||||
@@ -488,23 +481,23 @@ func (rb *resourceRuleBuilder) MatchResource(groups []string, versions []string,
|
||||
func (rb *resourceRuleBuilder) EndResourceRule() *validatingAdmissionPolicyBuilder {
|
||||
b := rb.policyBuilder
|
||||
if b.policy.Spec.MatchConstraints == nil {
|
||||
b.policy.Spec.MatchConstraints = &admissionregistrationv1beta1.MatchResources{}
|
||||
b.policy.Spec.MatchConstraints = &admissionregistrationv1.MatchResources{}
|
||||
}
|
||||
b.policy.Spec.MatchConstraints.ResourceRules = append(b.policy.Spec.MatchConstraints.ResourceRules, *rb.resourceRule)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *validatingAdmissionPolicyBuilder) WithValidation(validation admissionregistrationv1beta1.Validation) *validatingAdmissionPolicyBuilder {
|
||||
func (b *validatingAdmissionPolicyBuilder) WithValidation(validation admissionregistrationv1.Validation) *validatingAdmissionPolicyBuilder {
|
||||
b.policy.Spec.Validations = append(b.policy.Spec.Validations, validation)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *validatingAdmissionPolicyBuilder) WithVariable(variable admissionregistrationv1beta1.Variable) *validatingAdmissionPolicyBuilder {
|
||||
func (b *validatingAdmissionPolicyBuilder) WithVariable(variable admissionregistrationv1.Variable) *validatingAdmissionPolicyBuilder {
|
||||
b.policy.Spec.Variables = append(b.policy.Spec.Variables, variable)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *validatingAdmissionPolicyBuilder) Build() *admissionregistrationv1beta1.ValidatingAdmissionPolicy {
|
||||
func (b *validatingAdmissionPolicyBuilder) Build() *admissionregistrationv1.ValidatingAdmissionPolicy {
|
||||
return b.policy
|
||||
}
|
||||
|
||||
|
@@ -145,6 +145,9 @@ var (
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies/status"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicybindings"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicies"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicies/status"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicybindings"): true,
|
||||
}
|
||||
|
||||
parentResources = map[schema.GroupVersionResource]schema.GroupVersionResource{
|
||||
|
@@ -65,6 +65,7 @@ var resetFieldsStatusData = map[schema.GroupVersionResource]string{
|
||||
// standard for []metav1.Condition
|
||||
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
|
||||
gvr("networking.k8s.io", "v1alpha1", "servicecidrs"): `{"status": {"conditions":[{"type":"Accepted","status":"True","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
|
||||
}
|
||||
|
||||
@@ -156,6 +157,7 @@ var resetFieldsSpecData = map[schema.GroupVersionResource]string{
|
||||
gvr("internal.apiserver.k8s.io", "v1alpha1", "storageversions"): `{}`,
|
||||
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): `{"metadata": {"labels": {"a":"c"}}, "spec": {"paramKind": {"apiVersion": "apps/v1", "kind": "Deployment"}}}`,
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies"): `{"metadata": {"labels": {"a":"c"}}, "spec": {"paramKind": {"apiVersion": "apps/v1", "kind": "Deployment"}}}`,
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicies"): `{"metadata": {"labels": {"a":"c"}}, "spec": {"paramKind": {"apiVersion": "apps/v1", "kind": "Deployment"}}}`,
|
||||
}
|
||||
|
||||
// TestResetFields makes sure that fieldManager does not own fields reset by the storage strategy.
|
||||
|
@@ -58,6 +58,7 @@ var statusData = map[schema.GroupVersionResource]string{
|
||||
// standard for []metav1.Condition
|
||||
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"False","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"False","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicies"): `{"status": {"conditions":[{"type":"Accepted","status":"False","lastTransitionTime":"2020-01-01T00:00:00Z","reason":"RuleApplied","message":"Rule was applied"}]}}`,
|
||||
}
|
||||
|
||||
const statusDefault = `{"status": {"conditions": [{"type": "MyStatus", "status":"True"}]}}`
|
||||
|
@@ -143,6 +143,9 @@ var (
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicies/status"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1beta1", "validatingadmissionpolicybindings"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicies"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicies/status"): true,
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicybindings"): true,
|
||||
// transient resource exemption
|
||||
gvr("authentication.k8s.io", "v1", "selfsubjectreviews"): true,
|
||||
gvr("authentication.k8s.io", "v1beta1", "selfsubjectreviews"): true,
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -339,6 +339,16 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
|
||||
Stub: `{"metadata":{"name":"hook2","creationTimestamp":null},"webhooks":[{"name":"externaladmissionhook.k8s.io","clientConfig":{"service":{"namespace":"ns","name":"n"},"caBundle":null},"rules":[{"operations":["CREATE"],"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore","sideEffects":"None","admissionReviewVersions":["v1beta1"]}]}`,
|
||||
ExpectedEtcdPath: "/registry/mutatingwebhookconfigurations/hook2",
|
||||
},
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicies"): {
|
||||
Stub: `{"metadata":{"name":"vap1","creationTimestamp":null},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"validations":[{"expression":"object.spec.replicas <= params.maxReplicas","message":"Too many replicas"}]}}`,
|
||||
ExpectedEtcdPath: "/registry/validatingadmissionpolicies/vap1",
|
||||
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "ValidatingAdmissionPolicy"),
|
||||
},
|
||||
gvr("admissionregistration.k8s.io", "v1", "validatingadmissionpolicybindings"): {
|
||||
Stub: `{"metadata":{"name":"pb1","creationTimestamp":null},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com","parameterNotFoundAction":"Deny"},"validationActions":["Deny"]}}`,
|
||||
ExpectedEtcdPath: "/registry/validatingadmissionpolicybindings/pb1",
|
||||
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "ValidatingAdmissionPolicyBinding"),
|
||||
},
|
||||
// --
|
||||
|
||||
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1beta1
|
||||
@@ -354,13 +364,13 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
|
||||
|
||||
// k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1
|
||||
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicies"): {
|
||||
Stub: `{"metadata":{"name":"vap1","creationTimestamp":null},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"validations":[{"expression":"object.spec.replicas <= params.maxReplicas","message":"Too many replicas"}]}}`,
|
||||
ExpectedEtcdPath: "/registry/validatingadmissionpolicies/vap1",
|
||||
Stub: `{"metadata":{"name":"vap1a1","creationTimestamp":null},"spec":{"paramKind":{"apiVersion":"test.example.com/v1","kind":"Example"},"matchConstraints":{"resourceRules": [{"resourceNames": ["fakeName"], "apiGroups":["apps"],"apiVersions":["v1"],"operations":["CREATE", "UPDATE"], "resources":["deployments"]}]},"validations":[{"expression":"object.spec.replicas <= params.maxReplicas","message":"Too many replicas"}]}}`,
|
||||
ExpectedEtcdPath: "/registry/validatingadmissionpolicies/vap1a1",
|
||||
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "ValidatingAdmissionPolicy"),
|
||||
},
|
||||
gvr("admissionregistration.k8s.io", "v1alpha1", "validatingadmissionpolicybindings"): {
|
||||
Stub: `{"metadata":{"name":"pb1","creationTimestamp":null},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com"},"validationActions":["Deny"]}}`,
|
||||
ExpectedEtcdPath: "/registry/validatingadmissionpolicybindings/pb1",
|
||||
Stub: `{"metadata":{"name":"pb1a1","creationTimestamp":null},"spec":{"policyName":"replicalimit-policy.example.com","paramRef":{"name":"replica-limit-test.example.com"},"validationActions":["Deny"]}}`,
|
||||
ExpectedEtcdPath: "/registry/validatingadmissionpolicybindings/pb1a1",
|
||||
ExpectedGVK: gvkP("admissionregistration.k8s.io", "v1beta1", "ValidatingAdmissionPolicyBinding"),
|
||||
},
|
||||
// --
|
||||
|
Reference in New Issue
Block a user