diff --git a/controllers/configurationpolicy_controller.go b/controllers/configurationpolicy_controller.go index 89824896..c4d3ab23 100644 --- a/controllers/configurationpolicy_controller.go +++ b/controllers/configurationpolicy_controller.go @@ -1703,7 +1703,18 @@ func (r *ConfigurationPolicyReconciler) handleSingleObj( var uid string completed, reason, msg, uid, err := r.enforceByCreatingOrDeleting(obj) - result.events = append(result.events, objectTmplEvalEvent{completed, reason, msg}) + hasStatus := false + if tmplObj, err := unmarshalFromJSON(objectT.ObjectDefinition.Raw); err == nil { + _, hasStatus = tmplObj.Object["status"] + } + + if completed && hasStatus { + msg += ", the status of the object will be verified in the next evaluation" + reason += ", status unchecked" + result.events = append(result.events, objectTmplEvalEvent{false, reason, msg}) + } else { + result.events = append(result.events, objectTmplEvalEvent{completed, reason, msg}) + } if err != nil { // violation created for handling error diff --git a/test/e2e/case34_enforce_w_status_test.go b/test/e2e/case34_enforce_w_status_test.go new file mode 100644 index 00000000..c1c556b2 --- /dev/null +++ b/test/e2e/case34_enforce_w_status_test.go @@ -0,0 +1,78 @@ +// Copyright (c) 2023 Red Hat, Inc. +// Copyright Contributors to the Open Cluster Management project + +package e2e + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "open-cluster-management.io/config-policy-controller/test/utils" +) + +var _ = Describe("Test compliance events of enforced policies that define a status", Ordered, func() { + const ( + rsrcPath = "../resources/case34_enforce_w_status/" + policyYAML = rsrcPath + "policy.yaml" + policyName = "case34-parent" + cfgPlcYAML = rsrcPath + "config-policy.yaml" + updatedCfgPlc = rsrcPath + "config-policy-updated.yaml" + cfgPlcName = "case34-cfgpol" + nestedPlcYAML = rsrcPath + "nested-cfgpol-updated.yaml" + nestedPlcName = "case34-cfgpol-nested" + ) + + It("Should have the expected events", func() { + By("Setting up the policy") + createConfigPolicyWithParent(policyYAML, policyName, cfgPlcYAML) + + By("Checking there is a NonCompliant event on the policy") + Eventually(func() interface{} { + return utils.GetMatchingEvents(clientManaged, testNamespace, + policyName, cfgPlcName, "^NonCompliant;", defaultTimeoutSeconds) + }, defaultTimeoutSeconds, 5).ShouldNot(BeEmpty()) + + By("Checking there are no Compliant events on the policy") + Consistently(func() interface{} { + return utils.GetMatchingEvents(clientManaged, testNamespace, + policyName, cfgPlcName, "^Compliant;", defaultTimeoutSeconds) + }, defaultTimeoutSeconds, 5).Should(BeEmpty()) + + By("Updating the policy") + utils.Kubectl("apply", "-f", updatedCfgPlc, "-n", testNamespace) + + By("Checking there are no Compliant events created during the update flow") + Consistently(func() interface{} { + return utils.GetMatchingEvents(clientManaged, testNamespace, + policyName, cfgPlcName, "^Compliant;", defaultTimeoutSeconds) + }, defaultTimeoutSeconds, 5).Should(BeEmpty()) + + By("Updating the nested policy to increment its generation") + utils.Kubectl("apply", "-f", nestedPlcYAML, "-n", testNamespace) + nestedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, + nestedPlcName, testNamespace, true, defaultTimeoutSeconds) + Expect(nestedPlc.GetGeneration()).To(BeNumerically("==", 3)) + + By("Checking there is now a Compliant event on the policy") + Eventually(func() interface{} { + return utils.GetMatchingEvents(clientManaged, testNamespace, + policyName, cfgPlcName, "^Compliant;", defaultTimeoutSeconds) + }, defaultTimeoutSeconds, 5).ShouldNot(BeEmpty()) + }) + + AfterAll(func() { + utils.Kubectl("delete", "policy", policyName, "-n", "managed", "--ignore-not-found") + configPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, + cfgPlcName, "managed", false, defaultTimeoutSeconds, + ) + Expect(configPlc).To(BeNil()) + utils.Kubectl("delete", "configurationpolicy", nestedPlcName, "-n", "managed", "--ignore-not-found") + nestedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, + nestedPlcName, "managed", false, defaultTimeoutSeconds, + ) + Expect(nestedPlc).To(BeNil()) + utils.Kubectl("delete", "event", "--field-selector=involvedObject.name="+policyName, "-n", "managed") + utils.Kubectl("delete", "event", "--field-selector=involvedObject.name="+cfgPlcName, "-n", "managed") + utils.Kubectl("delete", "event", "--field-selector=involvedObject.name="+nestedPlcName, "-n", "managed") + }) +}) diff --git a/test/resources/case34_enforce_w_status/config-policy-updated.yaml b/test/resources/case34_enforce_w_status/config-policy-updated.yaml new file mode 100644 index 00000000..3b2002de --- /dev/null +++ b/test/resources/case34_enforce_w_status/config-policy-updated.yaml @@ -0,0 +1,27 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: ConfigurationPolicy +metadata: + name: case34-cfgpol +spec: + remediationAction: enforce + severity: low + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: case34-cfgpol-nested + namespace: managed + spec: + remediationAction: inform + severity: low + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Namespace + metadata: + name: default + status: + lastEvaluatedGeneration: 3 diff --git a/test/resources/case34_enforce_w_status/config-policy.yaml b/test/resources/case34_enforce_w_status/config-policy.yaml new file mode 100644 index 00000000..28f21874 --- /dev/null +++ b/test/resources/case34_enforce_w_status/config-policy.yaml @@ -0,0 +1,33 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: ConfigurationPolicy +metadata: + name: case34-cfgpol + ownerReferences: + - apiVersion: policy.open-cluster-management.io/v1 + blockOwnerDeletion: false + controller: true + kind: Policy + name: case34-parent + uid: 08bae967-4262-498a-84e9-d1f0e321b41e # to be replaced! +spec: + remediationAction: enforce + severity: low + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: case34-cfgpol-nested + namespace: managed + spec: + remediationAction: inform + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Namespace + metadata: + name: default + status: + lastEvaluatedGeneration: 3 diff --git a/test/resources/case34_enforce_w_status/nested-cfgpol-updated.yaml b/test/resources/case34_enforce_w_status/nested-cfgpol-updated.yaml new file mode 100644 index 00000000..4a979031 --- /dev/null +++ b/test/resources/case34_enforce_w_status/nested-cfgpol-updated.yaml @@ -0,0 +1,14 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: ConfigurationPolicy +metadata: + name: case34-cfgpol-nested +spec: + remediationAction: inform + severity: high # updated field! + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Namespace + metadata: + name: default diff --git a/test/resources/case34_enforce_w_status/policy.yaml b/test/resources/case34_enforce_w_status/policy.yaml new file mode 100644 index 00000000..ec8c283e --- /dev/null +++ b/test/resources/case34_enforce_w_status/policy.yaml @@ -0,0 +1,34 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: case34-parent +spec: + disabled: false + remediationAction: enforce + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: case34-cfgpol + spec: + remediationAction: enforce + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: case34-cfgpol-nested + namespace: managed + spec: + remediationAction: inform + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Namespace + metadata: + name: default + status: + lastEvaluatedGeneration: 3