diff --git a/pkg/resources/validatingadmissionpolicy.go b/pkg/resources/validatingadmissionpolicy.go new file mode 100644 index 0000000..b3bc218 --- /dev/null +++ b/pkg/resources/validatingadmissionpolicy.go @@ -0,0 +1,49 @@ +package resources + +import ( + "fmt" + + admissionv1 "k8s.io/api/admissionregistration/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ValidatingAdmissionPolicyMutator struct { + Name string + Spec admissionv1.ValidatingAdmissionPolicySpec + meta MetadataMutator +} + +var _ Mutator[*admissionv1.ValidatingAdmissionPolicy] = &ValidatingAdmissionPolicyMutator{} + +func NewValidatingAdmissionPolicyMutator(name string, sourceSpec admissionv1.ValidatingAdmissionPolicySpec) Mutator[*admissionv1.ValidatingAdmissionPolicy] { + return &ValidatingAdmissionPolicyMutator{ + Name: name, + Spec: sourceSpec, + meta: NewMetadataMutator(), + } +} + +func (m *ValidatingAdmissionPolicyMutator) String() string { + return fmt.Sprintf("validatingadmissionpolicy %s", m.Name) +} + +func (m *ValidatingAdmissionPolicyMutator) Empty() *admissionv1.ValidatingAdmissionPolicy { + return &admissionv1.ValidatingAdmissionPolicy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "admissionregistration.k8s.io/v1", + Kind: "ValidatingAdmissionPolicy", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: m.Name, + }, + } +} + +func (m *ValidatingAdmissionPolicyMutator) Mutate(r *admissionv1.ValidatingAdmissionPolicy) error { + r.Spec = *m.Spec.DeepCopy() + return m.meta.Mutate(r) +} + +func (m *ValidatingAdmissionPolicyMutator) MetadataMutator() MetadataMutator { + return m.meta +} diff --git a/pkg/resources/validatingadmissionpolicy_test.go b/pkg/resources/validatingadmissionpolicy_test.go new file mode 100644 index 0000000..eb12dc5 --- /dev/null +++ b/pkg/resources/validatingadmissionpolicy_test.go @@ -0,0 +1,117 @@ +package resources_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + admissionv1 "k8s.io/api/admissionregistration/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openmcp-project/controller-utils/pkg/resources" + "github.com/openmcp-project/controller-utils/pkg/testing" +) + +var _ = Describe("ValidatingAdmissionPolicyMutator", func() { + var ( + ctx context.Context + fakeClient client.WithWatch + scheme *runtime.Scheme + labels map[string]string + annotations map[string]string + mutator resources.Mutator[*admissionv1.ValidatingAdmissionPolicy] + ) + + BeforeEach(func() { + ctx = context.TODO() + + // Create a scheme and register the admissionregistration/v1 API + scheme = runtime.NewScheme() + Expect(admissionv1.AddToScheme(scheme)).To(Succeed()) + + // Initialize the fake client + var err error + fakeClient, err = testing.GetFakeClient(scheme) + Expect(err).ToNot(HaveOccurred()) + + // Define labels and annotations + labels = map[string]string{"label1": "value1"} + annotations = map[string]string{"annotation1": "value1"} + + // Create a ValidatingAdmissionPolicy mutator + mutator = resources.NewValidatingAdmissionPolicyMutator("test-vap", admissionv1.ValidatingAdmissionPolicySpec{ + ParamKind: &admissionv1.ParamKind{ + APIVersion: "v1", + Kind: "TestParam", + }, + MatchConstraints: &admissionv1.MatchResources{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"foo": "bar"}, + }, + }, + Validations: []admissionv1.Validation{ + { + Expression: "asdf", + Message: "doesnotmatter", + MessageExpression: "qwer", + }, + }, + FailurePolicy: ptr.To(admissionv1.Fail), + AuditAnnotations: []admissionv1.AuditAnnotation{ + { + Key: "example.com/audit", + ValueExpression: "example.com/audit-value", + }, + }, + MatchConditions: []admissionv1.MatchCondition{ + { + Name: "example-condition", + Expression: "example.com/condition-expression", + }, + }, + Variables: []admissionv1.Variable{ + { + Name: "example-variable", + Expression: "example.com/variable-expression", + }, + }, + }) + mutator.MetadataMutator().WithLabels(labels).WithAnnotations(annotations) + }) + + It("should create an empty ValidatingAdmissionPolicy with correct metadata", func() { + vap := mutator.Empty() + + Expect(vap.Name).To(Equal("test-vap")) + Expect(vap.APIVersion).To(Equal("admissionregistration.k8s.io/v1")) + Expect(vap.Kind).To(Equal("ValidatingAdmissionPolicy")) + }) + + It("should apply labels and annotations using Mutate", func() { + vap := mutator.Empty() + + // Apply the mutator's Mutate method + Expect(mutator.Mutate(vap)).To(Succeed()) + + // Verify that the labels and annotations are applied + Expect(vap.Labels).To(Equal(labels)) + Expect(vap.Annotations).To(Equal(annotations)) + }) + + It("should create and retrieve the ValidatingAdmissionPolicy using the fake client", func() { + vap := mutator.Empty() + Expect(mutator.Mutate(vap)).To(Succeed()) + + // Create the ValidatingAdmissionPolicy in the fake client + Expect(fakeClient.Create(ctx, vap)).To(Succeed()) + + // Retrieve the ValidatingAdmissionPolicy from the fake client and verify it + retrievedValidatingAdmissionPolicy := &admissionv1.ValidatingAdmissionPolicy{} + Expect(fakeClient.Get(ctx, client.ObjectKey{Name: "test-vap"}, retrievedValidatingAdmissionPolicy)).To(Succeed()) + Expect(retrievedValidatingAdmissionPolicy).To(Equal(vap)) + }) +}) diff --git a/pkg/resources/validatingadmissionpolicybinding.go b/pkg/resources/validatingadmissionpolicybinding.go new file mode 100644 index 0000000..aab2c1b --- /dev/null +++ b/pkg/resources/validatingadmissionpolicybinding.go @@ -0,0 +1,49 @@ +package resources + +import ( + "fmt" + + admissionv1 "k8s.io/api/admissionregistration/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ValidatingAdmissionPolicyBindingMutator struct { + Name string + Spec admissionv1.ValidatingAdmissionPolicyBindingSpec + meta MetadataMutator +} + +var _ Mutator[*admissionv1.ValidatingAdmissionPolicyBinding] = &ValidatingAdmissionPolicyBindingMutator{} + +func NewValidatingAdmissionPolicyBindingMutator(name string, sourceSpec admissionv1.ValidatingAdmissionPolicyBindingSpec) Mutator[*admissionv1.ValidatingAdmissionPolicyBinding] { + return &ValidatingAdmissionPolicyBindingMutator{ + Name: name, + Spec: sourceSpec, + meta: NewMetadataMutator(), + } +} + +func (m *ValidatingAdmissionPolicyBindingMutator) String() string { + return fmt.Sprintf("validatingadmissionpolicy %s", m.Name) +} + +func (m *ValidatingAdmissionPolicyBindingMutator) Empty() *admissionv1.ValidatingAdmissionPolicyBinding { + return &admissionv1.ValidatingAdmissionPolicyBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "admissionregistration.k8s.io/v1", + Kind: "ValidatingAdmissionPolicyBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: m.Name, + }, + } +} + +func (m *ValidatingAdmissionPolicyBindingMutator) Mutate(r *admissionv1.ValidatingAdmissionPolicyBinding) error { + r.Spec = *m.Spec.DeepCopy() + return m.meta.Mutate(r) +} + +func (m *ValidatingAdmissionPolicyBindingMutator) MetadataMutator() MetadataMutator { + return m.meta +} diff --git a/pkg/resources/validatingadmissionpolicybinding_test.go b/pkg/resources/validatingadmissionpolicybinding_test.go new file mode 100644 index 0000000..3b305d6 --- /dev/null +++ b/pkg/resources/validatingadmissionpolicybinding_test.go @@ -0,0 +1,99 @@ +package resources_test + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + admissionv1 "k8s.io/api/admissionregistration/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/openmcp-project/controller-utils/pkg/resources" + "github.com/openmcp-project/controller-utils/pkg/testing" +) + +var _ = Describe("ValidatingAdmissionPolicyBindingMutator", func() { + var ( + ctx context.Context + fakeClient client.WithWatch + scheme *runtime.Scheme + labels map[string]string + annotations map[string]string + mutator resources.Mutator[*admissionv1.ValidatingAdmissionPolicyBinding] + ) + + BeforeEach(func() { + ctx = context.TODO() + + // Create a scheme and register the admissionregistration/v1 API + scheme = runtime.NewScheme() + Expect(admissionv1.AddToScheme(scheme)).To(Succeed()) + + // Initialize the fake client + var err error + fakeClient, err = testing.GetFakeClient(scheme) + Expect(err).ToNot(HaveOccurred()) + + // Define labels and annotations + labels = map[string]string{"label1": "value1"} + annotations = map[string]string{"annotation1": "value1"} + + // Create a ValidatingAdmissionPolicyBinding mutator + mutator = resources.NewValidatingAdmissionPolicyBindingMutator("test-vapb", admissionv1.ValidatingAdmissionPolicyBindingSpec{ + PolicyName: "test-policy", + ParamRef: &admissionv1.ParamRef{ + Name: "test-param", + Namespace: "foo", + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"foo": "bar"}, + }, + ParameterNotFoundAction: ptr.To(admissionv1.DenyAction), + }, + MatchResources: &admissionv1.MatchResources{ + NamespaceSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"foo": "bar"}, + }, + }, + ValidationActions: []admissionv1.ValidationAction{ + "asdf", + }, + }) + mutator.MetadataMutator().WithLabels(labels).WithAnnotations(annotations) + }) + + It("should create an empty ValidatingAdmissionPolicyBinding with correct metadata", func() { + vapb := mutator.Empty() + + Expect(vapb.Name).To(Equal("test-vapb")) + Expect(vapb.APIVersion).To(Equal("admissionregistration.k8s.io/v1")) + Expect(vapb.Kind).To(Equal("ValidatingAdmissionPolicyBinding")) + }) + + It("should apply labels and annotations using Mutate", func() { + vapb := mutator.Empty() + + // Apply the mutator's Mutate method + Expect(mutator.Mutate(vapb)).To(Succeed()) + + // Verify that the labels and annotations are applied + Expect(vapb.Labels).To(Equal(labels)) + Expect(vapb.Annotations).To(Equal(annotations)) + }) + + It("should create and retrieve the ValidatingAdmissionPolicyBinding using the fake client", func() { + vapb := mutator.Empty() + Expect(mutator.Mutate(vapb)).To(Succeed()) + + // Create the ValidatingAdmissionPolicyBinding in the fake client + Expect(fakeClient.Create(ctx, vapb)).To(Succeed()) + + // Retrieve the ValidatingAdmissionPolicyBinding from the fake client and verify it + retrievedValidatingAdmissionPolicyBinding := &admissionv1.ValidatingAdmissionPolicyBinding{} + Expect(fakeClient.Get(ctx, client.ObjectKey{Name: "test-vapb"}, retrievedValidatingAdmissionPolicyBinding)).To(Succeed()) + Expect(retrievedValidatingAdmissionPolicyBinding).To(Equal(vapb)) + }) +})