mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 19:01:49 +00:00
Add integration test.
Co-authored-by: Kermit Alexander II <kermitalexandr@google.com>
This commit is contained in:
parent
46f97d4662
commit
d86cfa9854
27
test/integration/apiserver/cel/main_test.go
Normal file
27
test/integration/apiserver/cel/main_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cel
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
framework.EtcdMain(m.Run)
|
||||
}
|
645
test/integration/apiserver/cel/validatingadmissionpolicy_test.go
Normal file
645
test/integration/apiserver/cel/validatingadmissionpolicy_test.go
Normal file
@ -0,0 +1,645 @@
|
||||
/*
|
||||
Copyright 2022 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
apiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||
"k8s.io/kubernetes/test/integration/framework"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
|
||||
admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
// Test_ValidateNamespace_NoParams tests a ValidatingAdmissionPolicy that validates creation of a Namespace with no params.
|
||||
func Test_ValidateNamespace_NoParams(t *testing.T) {
|
||||
forbiddenReason := metav1.StatusReasonForbidden
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy
|
||||
policyBinding *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding
|
||||
namespace *v1.Namespace
|
||||
err string
|
||||
failureReason metav1.StatusReason
|
||||
}{
|
||||
{
|
||||
name: "namespace name contains suffix enforced by validating admission policy, using object metadata fields",
|
||||
policy: withValidations([]admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "object.metadata.name.endsWith('k8s')",
|
||||
},
|
||||
}, withFailurePolicy(admissionregistrationv1alpha1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
|
||||
policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
|
||||
namespace: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-k8s",
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "namespace name does NOT contain suffix enforced by validating admission policyusing, object metadata fields",
|
||||
policy: withValidations([]admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "object.metadata.name.endsWith('k8s')",
|
||||
},
|
||||
}, withFailurePolicy(admissionregistrationv1alpha1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
|
||||
policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
|
||||
namespace: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-foobar",
|
||||
},
|
||||
},
|
||||
err: "namespaces \"test-foobar\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: object.metadata.name.endsWith('k8s')",
|
||||
failureReason: metav1.StatusReasonInvalid,
|
||||
},
|
||||
{
|
||||
name: "namespace name does NOT contain suffix enforced by validating admission policy using object metadata fields, AND validating expression returns StatusReasonForbidden",
|
||||
policy: withValidations([]admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "object.metadata.name.endsWith('k8s')",
|
||||
Reason: &forbiddenReason,
|
||||
},
|
||||
}, withFailurePolicy(admissionregistrationv1alpha1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
|
||||
policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
|
||||
namespace: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "forbidden-test-foobar",
|
||||
},
|
||||
},
|
||||
err: "namespaces \"forbidden-test-foobar\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: object.metadata.name.endsWith('k8s')",
|
||||
failureReason: metav1.StatusReasonForbidden,
|
||||
},
|
||||
{
|
||||
name: "namespace name contains suffix enforced by validating admission policy, using request field",
|
||||
policy: withValidations([]admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "request.name.endsWith('k8s')",
|
||||
},
|
||||
}, withFailurePolicy(admissionregistrationv1alpha1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
|
||||
policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
|
||||
namespace: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-k8s",
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "namespace name does NOT contains suffix enforced by validating admission policy, using request field",
|
||||
policy: withValidations([]admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "request.name.endsWith('k8s')",
|
||||
},
|
||||
}, withFailurePolicy(admissionregistrationv1alpha1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
|
||||
policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
|
||||
namespace: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-k8s",
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "runtime error when validating namespace, but failurePolicy=Ignore",
|
||||
policy: withValidations([]admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "object.nonExistentProperty == 'someval'",
|
||||
},
|
||||
}, withFailurePolicy(admissionregistrationv1alpha1.Ignore, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
|
||||
policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
|
||||
namespace: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-k8s",
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "runtime error when validating namespace, but failurePolicy=Fail",
|
||||
policy: withValidations([]admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "object.nonExistentProperty == 'someval'",
|
||||
},
|
||||
}, withFailurePolicy(admissionregistrationv1alpha1.Fail, withNamespaceMatch(makePolicy("validate-namespace-suffix")))),
|
||||
policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", ""),
|
||||
namespace: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-k8s",
|
||||
},
|
||||
},
|
||||
err: "namespaces \"test-k8s\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: expression 'object.nonExistentProperty == 'someval'' resulted in error: no such key: nonExistentProperty",
|
||||
failureReason: metav1.StatusReasonInvalid,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
t.Run(testcase.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.CELValidatingAdmission, true)()
|
||||
server, err := apiservertesting.StartTestServer(t, nil, []string{
|
||||
"--enable-admission-plugins", "ValidatingAdmissionPolicy",
|
||||
}, framework.SharedEtcd())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer server.TearDownFn()
|
||||
|
||||
config := server.ClientConfig
|
||||
|
||||
client, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
policy := withWaitReadyConstraintAndExpression(testcase.policy)
|
||||
if _, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
|
||||
if err == nil && testcase.err == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if err == nil && testcase.err != "" {
|
||||
t.Logf("actual error: %v", err)
|
||||
t.Logf("expected error: %v", testcase.err)
|
||||
t.Fatal("got nil error but expected an error")
|
||||
}
|
||||
|
||||
if err != nil && testcase.err == "" {
|
||||
t.Logf("actual error: %v", err)
|
||||
t.Logf("expected error: %v", testcase.err)
|
||||
t.Fatal("got error but expected none")
|
||||
}
|
||||
|
||||
if err.Error() != testcase.err {
|
||||
t.Logf("actual validation error: %v", err)
|
||||
t.Logf("expected validation error: %v", testcase.err)
|
||||
t.Error("unexpected validation error")
|
||||
}
|
||||
|
||||
checkFailureReason(t, err, testcase.failureReason)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test_ValidateNamespace_WithConfigMapParams tests a ValidatingAdmissionPolicy that validates creation of a Namespace,
|
||||
// using ConfigMap as a param reference.
|
||||
func Test_ValidateNamespace_WithConfigMapParams(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy
|
||||
policyBinding *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding
|
||||
configMap *v1.ConfigMap
|
||||
namespace *v1.Namespace
|
||||
err string
|
||||
failureReason metav1.StatusReason
|
||||
}{
|
||||
{
|
||||
name: "namespace name contains suffix enforced by validating admission policy",
|
||||
policy: withValidations([]admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "object.metadata.name.endsWith(params.data.namespaceSuffix)",
|
||||
},
|
||||
}, withFailurePolicy(admissionregistrationv1alpha1.Fail, withParams(configParamKind(), withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
|
||||
policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", "validate-namespace-suffix-param"),
|
||||
configMap: makeConfigParams("validate-namespace-suffix-param", map[string]string{
|
||||
"namespaceSuffix": "k8s",
|
||||
}),
|
||||
namespace: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-k8s",
|
||||
},
|
||||
},
|
||||
err: "",
|
||||
},
|
||||
{
|
||||
name: "namespace name does NOT contain suffix enforced by validating admission policy",
|
||||
policy: withValidations([]admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "object.metadata.name.endsWith(params.data.namespaceSuffix)",
|
||||
},
|
||||
}, withFailurePolicy(admissionregistrationv1alpha1.Fail, withParams(configParamKind(), withNamespaceMatch(makePolicy("validate-namespace-suffix"))))),
|
||||
policyBinding: makeBinding("validate-namespace-suffix-binding", "validate-namespace-suffix", "validate-namespace-suffix-param"),
|
||||
configMap: makeConfigParams("validate-namespace-suffix-param", map[string]string{
|
||||
"namespaceSuffix": "k8s",
|
||||
}),
|
||||
namespace: &v1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-foo",
|
||||
},
|
||||
},
|
||||
err: "namespaces \"test-foo\" is forbidden: ValidatingAdmissionPolicy 'validate-namespace-suffix' with binding 'validate-namespace-suffix-binding' denied request: failed expression: object.metadata.name.endsWith(params.data.namespaceSuffix)",
|
||||
failureReason: metav1.StatusReasonInvalid,
|
||||
},
|
||||
}
|
||||
|
||||
for _, testcase := range testcases {
|
||||
t.Run(testcase.name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.CELValidatingAdmission, true)()
|
||||
server, err := apiservertesting.StartTestServer(t, nil, []string{
|
||||
"--enable-admission-plugins", "ValidatingAdmissionPolicy",
|
||||
}, framework.SharedEtcd())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer server.TearDownFn()
|
||||
|
||||
config := server.ClientConfig
|
||||
|
||||
client, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), testcase.configMap, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
policy := withWaitReadyConstraintAndExpression(testcase.policy)
|
||||
if _, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := createAndWaitReady(t, client, testcase.policyBinding, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = client.CoreV1().Namespaces().Create(context.TODO(), testcase.namespace, metav1.CreateOptions{})
|
||||
if err == nil && testcase.err == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if err == nil && testcase.err != "" {
|
||||
t.Logf("actual error: %v", err)
|
||||
t.Logf("expected error: %v", testcase.err)
|
||||
t.Fatal("got nil error but expected an error")
|
||||
}
|
||||
|
||||
if err != nil && testcase.err == "" {
|
||||
t.Logf("actual error: %v", err)
|
||||
t.Logf("expected error: %v", testcase.err)
|
||||
t.Fatal("got error but expected none")
|
||||
}
|
||||
|
||||
if err.Error() != testcase.err {
|
||||
t.Logf("actual validation error: %v", err)
|
||||
t.Logf("expected validation error: %v", testcase.err)
|
||||
t.Error("unexpected validation error")
|
||||
}
|
||||
|
||||
checkFailureReason(t, err, testcase.failureReason)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiplePolicyBindings(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.CELValidatingAdmission, true)()
|
||||
server, err := apiservertesting.StartTestServer(t, nil, nil, framework.SharedEtcd())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer server.TearDownFn()
|
||||
|
||||
config := server.ClientConfig
|
||||
|
||||
client, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
paramKind := &admissionregistrationv1alpha1.ParamKind{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
}
|
||||
policy := withPolicyExistsLabels([]string{"paramIdent"}, withParams(paramKind, withPolicyMatch("secrets", withFailurePolicy(admissionregistrationv1alpha1.Fail, makePolicy("test-policy")))))
|
||||
policy.Spec.Validations = []admissionregistrationv1alpha1.Validation{
|
||||
{
|
||||
Expression: "params.data.autofail != 'true' && (params.data.conditional == 'false' || object.metadata.name.startsWith(params.data.check))",
|
||||
},
|
||||
}
|
||||
policy = withWaitReadyConstraintAndExpression(policy)
|
||||
if _, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Create(context.TODO(), policy, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
autoFailParams := makeConfigParams("autofail-params", map[string]string{
|
||||
"autofail": "true",
|
||||
})
|
||||
if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), autoFailParams, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
autofailBinding := withBindingExistsLabels([]string{"autofail-binding-label"}, policy, makeBinding("autofail-binding", "test-policy", "autofail-params"))
|
||||
if err := createAndWaitReady(t, client, autofailBinding, map[string]string{"paramIdent": "true", "autofail-binding-label": "true"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
autoPassParams := makeConfigParams("autopass-params", map[string]string{
|
||||
"autofail": "false",
|
||||
"conditional": "false",
|
||||
})
|
||||
if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), autoPassParams, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
autopassBinding := withBindingExistsLabels([]string{"autopass-binding-label"}, policy, makeBinding("autopass-binding", "test-policy", "autopass-params"))
|
||||
if err := createAndWaitReady(t, client, autopassBinding, map[string]string{"paramIdent": "true", "autopass-binding-label": "true"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
condpassParams := makeConfigParams("condpass-params", map[string]string{
|
||||
"autofail": "false",
|
||||
"conditional": "true",
|
||||
"check": "prefix-",
|
||||
})
|
||||
if _, err := client.CoreV1().ConfigMaps("default").Create(context.TODO(), condpassParams, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
condpassBinding := withBindingExistsLabels([]string{"condpass-binding-label"}, policy, makeBinding("condpass-binding", "test-policy", "condpass-params"))
|
||||
if err := createAndWaitReady(t, client, condpassBinding, map[string]string{"paramIdent": "true", "condpass-binding-label": "true"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
autofailingSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "autofailing-secret",
|
||||
Labels: map[string]string{
|
||||
"paramIdent": "someVal",
|
||||
"autofail-binding-label": "true",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = client.CoreV1().Secrets("default").Create(context.TODO(), autofailingSecret, metav1.CreateOptions{})
|
||||
if err == nil {
|
||||
t.Fatal("expected secret creation to fail due to autofail-binding")
|
||||
}
|
||||
checkForFailedRule(t, err)
|
||||
checkFailureReason(t, err, metav1.StatusReasonInvalid)
|
||||
|
||||
autopassingSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "autopassing-secret",
|
||||
Labels: map[string]string{
|
||||
"paramIdent": "someVal",
|
||||
"autopass-binding-label": "true",
|
||||
},
|
||||
},
|
||||
}
|
||||
if _, err := client.CoreV1().Secrets("default").Create(context.TODO(), autopassingSecret, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatalf("expected secret creation to succeed, got: %s", err)
|
||||
}
|
||||
|
||||
condpassingSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "prefix-condpassing-secret",
|
||||
Labels: map[string]string{
|
||||
"paramIdent": "someVal",
|
||||
"condpass-binding-label": "true",
|
||||
},
|
||||
},
|
||||
}
|
||||
if _, err := client.CoreV1().Secrets("default").Create(context.TODO(), condpassingSecret, metav1.CreateOptions{}); err != nil {
|
||||
t.Fatalf("expected secret creation to succeed, got: %s", err)
|
||||
}
|
||||
|
||||
condfailingSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "condfailing-secret",
|
||||
Labels: map[string]string{
|
||||
"paramIdent": "someVal",
|
||||
"condpass-binding-label": "true",
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = client.CoreV1().Secrets("default").Create(context.TODO(), condfailingSecret, metav1.CreateOptions{})
|
||||
if err == nil {
|
||||
t.Fatal("expected secret creation to fail due to autofail-binding")
|
||||
}
|
||||
checkForFailedRule(t, err)
|
||||
checkFailureReason(t, err, metav1.StatusReasonInvalid)
|
||||
}
|
||||
|
||||
func withWaitReadyConstraintAndExpression(policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
|
||||
policy = policy.DeepCopy()
|
||||
policy.Spec.MatchConstraints.ResourceRules = append(policy.Spec.MatchConstraints.ResourceRules, admissionregistrationv1alpha1.NamedRuleWithOperations{
|
||||
ResourceNames: []string{"test-marker"},
|
||||
RuleWithOperations: admissionregistrationv1alpha1.RuleWithOperations{
|
||||
Operations: []admissionregistrationv1.OperationType{
|
||||
"UPDATE",
|
||||
},
|
||||
Rule: admissionregistrationv1.Rule{
|
||||
APIGroups: []string{
|
||||
"",
|
||||
},
|
||||
APIVersions: []string{
|
||||
"v1",
|
||||
},
|
||||
Resources: []string{
|
||||
"endpoints",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
policy.Spec.Validations = append([]admissionregistrationv1alpha1.Validation{{
|
||||
Expression: "object.metadata.name != 'test-marker'",
|
||||
Message: "marker denied; policy is ready",
|
||||
}}, policy.Spec.Validations...)
|
||||
return policy
|
||||
}
|
||||
|
||||
func createAndWaitReady(t *testing.T, client *clientset.Clientset, binding *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding, matchLabels map[string]string) error {
|
||||
marker := &v1.Endpoints{ObjectMeta: metav1.ObjectMeta{Name: "test-marker", Namespace: "default", Labels: matchLabels}}
|
||||
defer func() {
|
||||
err := client.CoreV1().Endpoints("default").Delete(context.TODO(), marker.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
t.Logf("error deleting marker: %v", err)
|
||||
}
|
||||
}()
|
||||
marker, err := client.CoreV1().Endpoints("default").Create(context.TODO(), marker, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicyBindings().Create(context.TODO(), binding, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if waitErr := wait.PollImmediate(time.Millisecond*5, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
_, err := client.CoreV1().Endpoints("default").Patch(context.TODO(), marker.Name, types.JSONPatchType, []byte("[]"), metav1.PatchOptions{})
|
||||
if err != nil && strings.Contains(err.Error(), "marker denied; policy is ready") {
|
||||
return true, nil
|
||||
} else {
|
||||
t.Logf("waiting for policy to be ready. Marker: %v, Last marker patch response: %v", marker, err)
|
||||
return false, err
|
||||
}
|
||||
}); waitErr != nil {
|
||||
return waitErr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func makePolicy(name string) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
|
||||
return &admissionregistrationv1alpha1.ValidatingAdmissionPolicy{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name},
|
||||
}
|
||||
}
|
||||
|
||||
func withParams(params *admissionregistrationv1alpha1.ParamKind, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
|
||||
policy.Spec.ParamKind = params
|
||||
return policy
|
||||
}
|
||||
|
||||
func configParamKind() *admissionregistrationv1alpha1.ParamKind {
|
||||
return &admissionregistrationv1alpha1.ParamKind{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
}
|
||||
}
|
||||
|
||||
func withFailurePolicy(failure admissionregistrationv1alpha1.FailurePolicyType, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
|
||||
policy.Spec.FailurePolicy = &failure
|
||||
return policy
|
||||
}
|
||||
|
||||
func withNamespaceMatch(policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
|
||||
return withPolicyMatch("namespaces", policy)
|
||||
}
|
||||
|
||||
func withPolicyMatch(resource string, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
|
||||
policy.Spec.MatchConstraints = &admissionregistrationv1alpha1.MatchResources{
|
||||
ResourceRules: []admissionregistrationv1alpha1.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: admissionregistrationv1alpha1.RuleWithOperations{
|
||||
Operations: []admissionregistrationv1.OperationType{
|
||||
"CREATE",
|
||||
},
|
||||
Rule: admissionregistrationv1.Rule{
|
||||
APIGroups: []string{
|
||||
"",
|
||||
},
|
||||
APIVersions: []string{
|
||||
"*",
|
||||
},
|
||||
Resources: []string{
|
||||
resource,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return policy
|
||||
}
|
||||
|
||||
func withPolicyExistsLabels(labels []string, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
|
||||
if policy.Spec.MatchConstraints == nil {
|
||||
policy.Spec.MatchConstraints = &admissionregistrationv1alpha1.MatchResources{}
|
||||
}
|
||||
matchExprs := buildExistsSelector(labels)
|
||||
policy.Spec.MatchConstraints.ObjectSelector = &metav1.LabelSelector{
|
||||
MatchExpressions: matchExprs,
|
||||
}
|
||||
return policy
|
||||
}
|
||||
|
||||
func withValidations(validations []admissionregistrationv1alpha1.Validation, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy) *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
|
||||
policy.Spec.Validations = validations
|
||||
return policy
|
||||
}
|
||||
|
||||
func makeBinding(name, policyName, paramName string) *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding {
|
||||
var paramRef *admissionregistrationv1alpha1.ParamRef
|
||||
if paramName != "" {
|
||||
paramRef = &admissionregistrationv1alpha1.ParamRef{
|
||||
Name: paramName,
|
||||
Namespace: "default",
|
||||
}
|
||||
}
|
||||
return &admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name},
|
||||
Spec: admissionregistrationv1alpha1.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: policyName,
|
||||
ParamRef: paramRef,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func withBindingExistsLabels(labels []string, policy *admissionregistrationv1alpha1.ValidatingAdmissionPolicy, binding *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding) *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding {
|
||||
if policy != nil {
|
||||
// shallow copy
|
||||
constraintsCopy := *policy.Spec.MatchConstraints
|
||||
binding.Spec.MatchResources = &constraintsCopy
|
||||
}
|
||||
matchExprs := buildExistsSelector(labels)
|
||||
binding.Spec.MatchResources.ObjectSelector = &metav1.LabelSelector{
|
||||
MatchExpressions: matchExprs,
|
||||
}
|
||||
return binding
|
||||
}
|
||||
|
||||
func buildExistsSelector(labels []string) []metav1.LabelSelectorRequirement {
|
||||
matchExprs := make([]metav1.LabelSelectorRequirement, len(labels))
|
||||
for i := 0; i < len(labels); i++ {
|
||||
matchExprs[i].Key = labels[i]
|
||||
matchExprs[i].Operator = metav1.LabelSelectorOpExists
|
||||
}
|
||||
return matchExprs
|
||||
}
|
||||
|
||||
func makeConfigParams(name string, data map[string]string) *v1.ConfigMap {
|
||||
return &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: name},
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func checkForFailedRule(t *testing.T, err error) {
|
||||
if !strings.Contains(err.Error(), "failed expression") {
|
||||
t.Fatalf("unexpected error (expected to find \"failed expression\"): %s", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), "evaluation error") {
|
||||
t.Fatalf("CEL rule evaluation failed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func checkFailureReason(t *testing.T, err error, expectedReason metav1.StatusReason) {
|
||||
reason := err.(apierrors.APIStatus).Status().Reason
|
||||
if reason != expectedReason {
|
||||
t.Logf("actual error reason: %v", reason)
|
||||
t.Logf("expected failure reason: %v", expectedReason)
|
||||
t.Error("unexpected error reason")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user