Skip to content

Commit 1ef1686

Browse files
committed
Test related object properties present for any remediation action
ref: https://issues.redhat.com/browse/ACM-14577 Tests scenarios where the policy reports a mismatched object, but the dry run validation reports a compliant object due to the apiserver adding or modifying fields in the request body. This commit also prevents timeouts in case40 by wrapping assertions with Eventually. Signed-off-by: Janelle Law <[email protected]>
1 parent db17f5c commit 1ef1686

9 files changed

+574
-32
lines changed

controllers/configurationpolicy_controller_test.go

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,75 @@ func TestUpdatedRelatedObjects(t *testing.T) {
705705
assert.Equal(t, "bar", relatedList[0].Object.Metadata.Name)
706706
}
707707

708+
func TestAddRelatedObjectProperties(t *testing.T) {
709+
compliant := true
710+
scopedGVR := depclient.ScopedGVR{
711+
GroupVersionResource: policyv1.SchemeBuilder.GroupVersion.WithResource("ConfigurationPolicy"),
712+
Namespaced: true,
713+
}
714+
namespace := "default"
715+
name := "foo"
716+
reason := "reason"
717+
718+
// Create test data for ObjectProperties
719+
createdByPolicy := true
720+
testUID := "test-uid-12345"
721+
testDiff := "test diff content"
722+
dryRunMatches := true
723+
724+
creationInfo := &policyv1.ObjectProperties{
725+
CreatedByPolicy: &createdByPolicy,
726+
UID: testUID,
727+
Diff: testDiff,
728+
MatchesAfterDryRun: dryRunMatches,
729+
}
730+
731+
relatedList := addRelatedObjects(
732+
compliant, scopedGVR, "ConfigurationPolicy", namespace, []string{name}, reason, creationInfo,
733+
)
734+
related := relatedList[0]
735+
736+
// Validate that the properties are correctly set
737+
assert.NotNil(t, related.Properties, "Properties should not be nil when creationInfo is provided")
738+
assert.NotNil(t, related.Properties.CreatedByPolicy, "CreatedByPolicy should not be nil")
739+
assert.Equal(t, createdByPolicy, *related.Properties.CreatedByPolicy)
740+
assert.Equal(t, testUID, related.Properties.UID)
741+
assert.Equal(t, testDiff, related.Properties.Diff)
742+
assert.Equal(t, dryRunMatches, related.Properties.MatchesAfterDryRun)
743+
744+
// add the same object and make sure the existing one is overwritten
745+
reason = "new"
746+
compliant = false
747+
748+
// Create new test data for ObjectProperties with different values
749+
newCreatedByPolicy := false
750+
newTestDiff := "updated diff content"
751+
newDryRunMatches := false
752+
753+
newCreationInfo := &policyv1.ObjectProperties{
754+
CreatedByPolicy: &newCreatedByPolicy,
755+
UID: testUID,
756+
Diff: newTestDiff,
757+
MatchesAfterDryRun: newDryRunMatches,
758+
}
759+
760+
relatedList = addRelatedObjects(
761+
compliant, scopedGVR, "ConfigurationPolicy", namespace, []string{name}, reason, newCreationInfo)
762+
related = relatedList[0]
763+
764+
assert.Len(t, relatedList, 1)
765+
assert.Equal(t, string(policyv1.NonCompliant), related.Compliant)
766+
assert.Equal(t, "new", related.Reason)
767+
768+
// Validate that the properties were updated with new values
769+
assert.NotNil(t, related.Properties, "Properties should not be nil when newCreationInfo is provided")
770+
assert.NotNil(t, related.Properties.CreatedByPolicy, "CreatedByPolicy should not be nil")
771+
assert.Equal(t, newCreatedByPolicy, *related.Properties.CreatedByPolicy)
772+
assert.Equal(t, testUID, related.Properties.UID)
773+
assert.Equal(t, newTestDiff, related.Properties.Diff)
774+
assert.Equal(t, newDryRunMatches, related.Properties.MatchesAfterDryRun)
775+
}
776+
708777
func TestCreateStatus(t *testing.T) {
709778
testcases := []struct {
710779
testName string
@@ -1544,3 +1613,155 @@ func TestShouldHandleSingleKeyFalse(t *testing.T) {
15441613
assert.False(t, skip)
15451614
}
15461615
}
1616+
1617+
// TestAlreadyEvaluated tests the core caching functionality for evaluated objects
1618+
func TestAlreadyEvaluated(t *testing.T) {
1619+
// Create a test ConfigurationPolicy
1620+
policy := &policyv1.ConfigurationPolicy{
1621+
ObjectMeta: metav1.ObjectMeta{
1622+
Name: "test-policy",
1623+
Namespace: "default",
1624+
UID: types.UID("test-policy-uid"),
1625+
},
1626+
}
1627+
1628+
// Create a test unstructured object
1629+
testObj := &unstructured.Unstructured{
1630+
Object: map[string]interface{}{
1631+
"apiVersion": "v1",
1632+
"kind": "ConfigMap",
1633+
"metadata": map[string]interface{}{
1634+
"name": "test-configmap",
1635+
"namespace": "default",
1636+
"uid": "test-object-uid",
1637+
"resourceVersion": "123",
1638+
},
1639+
},
1640+
}
1641+
1642+
// Create a reconciler with an empty cache
1643+
r := &ConfigurationPolicyReconciler{
1644+
processedPolicyCache: sync.Map{},
1645+
}
1646+
1647+
t.Run("Test caching compliant=true", func(t *testing.T) {
1648+
// Call setEvaluatedObject with compliant=true
1649+
r.setEvaluatedObject(policy, testObj, true, "test message")
1650+
1651+
// Verify the object was cached correctly
1652+
evaluated, compliant, msg := r.alreadyEvaluated(policy, testObj)
1653+
1654+
assert.True(t, evaluated, "Object should be marked as evaluated")
1655+
assert.True(t, compliant, "Object should be marked as compliant")
1656+
assert.Equal(t, "test message", msg, "Message should match")
1657+
})
1658+
1659+
t.Run("Test caching compliant=false", func(t *testing.T) {
1660+
// Create a new policy and object to avoid cache interference
1661+
policy2 := &policyv1.ConfigurationPolicy{
1662+
ObjectMeta: metav1.ObjectMeta{
1663+
Name: "test-policy-2",
1664+
Namespace: "default",
1665+
UID: types.UID("test-policy-uid-2"),
1666+
},
1667+
}
1668+
1669+
testObj2 := &unstructured.Unstructured{
1670+
Object: map[string]interface{}{
1671+
"apiVersion": "v1",
1672+
"kind": "ConfigMap",
1673+
"metadata": map[string]interface{}{
1674+
"name": "test-configmap-2",
1675+
"namespace": "default",
1676+
"uid": "test-object-uid-2",
1677+
"resourceVersion": "124",
1678+
},
1679+
},
1680+
}
1681+
1682+
// Call setEvaluatedObject with compliant=false
1683+
r.setEvaluatedObject(policy2, testObj2, false, "error message")
1684+
1685+
// Verify the object was cached correctly
1686+
evaluated, compliant, msg := r.alreadyEvaluated(policy2, testObj2)
1687+
1688+
assert.True(t, evaluated, "Object should be marked as evaluated")
1689+
assert.False(t, compliant, "Object should be marked as non-compliant")
1690+
assert.Equal(t, "error message", msg, "Message should match")
1691+
})
1692+
1693+
t.Run("Test not cached object", func(t *testing.T) {
1694+
// Create a completely new policy and object
1695+
policy3 := &policyv1.ConfigurationPolicy{
1696+
ObjectMeta: metav1.ObjectMeta{
1697+
Name: "test-policy-3",
1698+
Namespace: "default",
1699+
UID: types.UID("test-policy-uid-3"),
1700+
},
1701+
}
1702+
1703+
testObj3 := &unstructured.Unstructured{
1704+
Object: map[string]interface{}{
1705+
"apiVersion": "v1",
1706+
"kind": "ConfigMap",
1707+
"metadata": map[string]interface{}{
1708+
"name": "test-configmap-3",
1709+
"namespace": "default",
1710+
"uid": "test-object-uid-3",
1711+
"resourceVersion": "125",
1712+
},
1713+
},
1714+
}
1715+
1716+
// Call alreadyEvaluated without caching first
1717+
evaluated, compliant, msg := r.alreadyEvaluated(policy3, testObj3)
1718+
1719+
assert.False(t, evaluated, "Object should not be marked as evaluated")
1720+
assert.False(t, compliant, "Object should be marked as non-compliant")
1721+
assert.Empty(t, msg, "Message should be empty")
1722+
})
1723+
1724+
t.Run("Test resource version change invalidates cache", func(t *testing.T) {
1725+
// Use the same policy but change the resource version
1726+
testObjNewVersion := &unstructured.Unstructured{
1727+
Object: map[string]interface{}{
1728+
"apiVersion": "v1",
1729+
"kind": "ConfigMap",
1730+
"metadata": map[string]interface{}{
1731+
"name": "test-configmap",
1732+
"namespace": "default",
1733+
"uid": "test-object-uid",
1734+
"resourceVersion": "456", // Different resource version
1735+
},
1736+
},
1737+
}
1738+
1739+
// Call alreadyEvaluated - should return false due to different resource version
1740+
// but the cached values from the previous evaluation should still be returned
1741+
evaluated, compliant, msg := r.alreadyEvaluated(policy, testObjNewVersion)
1742+
1743+
assert.False(t, evaluated, "Object should not be marked as evaluated due to different resource version")
1744+
assert.True(t, compliant, "Should return the cached compliance state from previous evaluation")
1745+
assert.Equal(t, "test message", msg, "Should return the cached message from previous evaluation")
1746+
})
1747+
1748+
t.Run("Test nil inputs", func(t *testing.T) {
1749+
// Test with nil policy
1750+
evaluated, compliant, msg := r.alreadyEvaluated(nil, testObj)
1751+
assert.False(t, evaluated)
1752+
assert.False(t, compliant)
1753+
assert.Empty(t, msg)
1754+
1755+
// Test with nil object
1756+
evaluated, compliant, msg = r.alreadyEvaluated(policy, nil)
1757+
assert.False(t, evaluated)
1758+
assert.False(t, compliant)
1759+
assert.Empty(t, msg)
1760+
1761+
// Test with both nil
1762+
evaluated, compliant, msg = r.alreadyEvaluated(nil, nil)
1763+
assert.False(t, evaluated)
1764+
assert.False(t, compliant)
1765+
assert.Empty(t, msg)
1766+
})
1767+
}

test/e2e/case20_delete_objects_test.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -130,26 +130,40 @@ var _ = Describe("Test Object deletion", Ordered, func() {
130130
return properties["uid"]
131131
}, defaultTimeoutSeconds, 1).ShouldNot(BeEmpty())
132132
})
133-
It("should not update status field for inform policies", func() {
133+
It("should update status field for inform policies", func() {
134134
By("Creating " + case20ConfigPolicyNameInform + " on managed")
135135
utils.Kubectl("apply", "-f", case20PolicyYamlInform, "-n", testNamespace)
136136
plc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
137137
case20ConfigPolicyNameInform, testNamespace, true, defaultTimeoutSeconds)
138138
Expect(plc).NotTo(BeNil())
139+
140+
var managedPlc *unstructured.Unstructured
141+
139142
Eventually(func(g Gomega) {
140-
managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
143+
managedPlc = utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
141144
case20ConfigPolicyNameInform, testNamespace, true, defaultTimeoutSeconds)
142145

143146
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
144147
}, defaultTimeoutSeconds, 1).Should(Succeed())
145-
Eventually(func() interface{} {
146-
managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
147-
case20ConfigPolicyNameInform, testNamespace, true, defaultTimeoutSeconds)
148-
relatedObj := managedPlc.Object["status"].(map[string]interface{})["relatedObjects"].([]interface{})[0]
149-
properties := relatedObj.(map[string]interface{})["properties"]
150148

151-
return properties
152-
}, defaultTimeoutSeconds, 1).Should(BeNil())
149+
By("Verifying the related object properties")
150+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
151+
Expect(err).ToNot(HaveOccurred())
152+
Expect(relatedObjects).To(HaveLen(1))
153+
154+
relatedObj := relatedObjects[0].(map[string]interface{})
155+
156+
createdByPolicy, found, _ := unstructured.NestedBool(relatedObj, "properties", "createdByPolicy")
157+
Expect(found).To(BeTrue())
158+
Expect(createdByPolicy).To(BeFalse())
159+
160+
uid, found, _ := unstructured.NestedString(relatedObj, "properties", "uid")
161+
Expect(found).To(BeTrue())
162+
Expect(uid).ToNot(BeEmpty())
163+
164+
matchesAfterDryRun, found, _ := unstructured.NestedBool(relatedObj, "properties", "matchesAfterDryRun")
165+
Expect(found).To(BeTrue())
166+
Expect(matchesAfterDryRun).To(BeTrue())
153167
})
154168
AfterAll(func() {
155169
policies := []string{

test/e2e/case40_recreate_option_test.go

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ var _ = Describe("Recreate options", Ordered, func() {
117117

118118
By("Verifying the ConfigurationPolicy is Compliant")
119119
var managedPlc *unstructured.Unstructured
120-
120+
var propsUID string
121121
Eventually(func(g Gomega) {
122122
managedPlc = utils.GetWithTimeout(
123123
clientManagedDynamic,
@@ -129,37 +129,39 @@ var _ = Describe("Recreate options", Ordered, func() {
129129
)
130130

131131
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
132-
}, defaultTimeoutSeconds, 1).Should(Succeed())
133132

134-
By("Verifying the diff is not set")
135-
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
136-
Expect(err).ToNot(HaveOccurred())
137-
Expect(relatedObjects).To(HaveLen(1))
133+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
134+
g.Expect(err).ToNot(HaveOccurred())
135+
g.Expect(relatedObjects).To(HaveLen(1))
138136

139-
relatedObject := relatedObjects[0].(map[string]interface{})
137+
relatedObject := relatedObjects[0].(map[string]interface{})
140138

141-
diff, _, _ := unstructured.NestedString(relatedObject, "properties", "diff")
142-
Expect(diff).To(BeEmpty())
139+
diff, _, _ := unstructured.NestedString(relatedObject, "properties", "diff")
140+
g.Expect(diff).To(BeEmpty())
143141

144-
propsUID, _, _ := unstructured.NestedString(relatedObject, "properties", "uid")
145-
Expect(propsUID).ToNot(BeEmpty())
142+
propsUID, _, _ = unstructured.NestedString(relatedObject, "properties", "uid")
143+
g.Expect(propsUID).ToNot(BeEmpty())
146144

147-
createdByPolicy, _, _ := unstructured.NestedBool(relatedObject, "properties", "createdByPolicy")
148-
Expect(createdByPolicy).To(BeTrue())
145+
createdByPolicy, _, _ := unstructured.NestedBool(relatedObject, "properties", "createdByPolicy")
146+
g.Expect(createdByPolicy).To(BeTrue())
149147

150-
deployment, err = clientManagedDynamic.Resource(gvrDeployment).Namespace("default").Get(
151-
ctx, "case40", metav1.GetOptions{},
152-
)
153-
Expect(err).ToNot(HaveOccurred())
148+
deployment, err = clientManagedDynamic.Resource(gvrDeployment).Namespace("default").Get(
149+
ctx, "case40", metav1.GetOptions{},
150+
)
151+
g.Expect(err).ToNot(HaveOccurred())
152+
}, defaultTimeoutSeconds, 1).Should(Succeed())
154153

155154
By("Verifying the Deployment was recreated")
156-
Expect(deployment.GetUID()).ToNot(Equal(uid), "Expected a new UID on the Deployment after it got recreated")
157-
Expect(propsUID).To(
158-
BeEquivalentTo(deployment.GetUID()), "Expect the object properties UID to match the new Deployment",
159-
)
155+
Eventually(func(g Gomega) {
156+
g.Expect(deployment.GetUID()).ToNot(
157+
Equal(uid), "Expected a new UID on the Deployment after it got recreated")
158+
g.Expect(propsUID).To(
159+
BeEquivalentTo(deployment.GetUID()), "Expect the object properties UID to match the new Deployment",
160+
)
160161

161-
selector, _, _ := unstructured.NestedString(deployment.Object, "spec", "selector", "matchLabels", "app")
162-
Expect(selector).To(Equal("case40-2"))
162+
selector, _, _ := unstructured.NestedString(deployment.Object, "spec", "selector", "matchLabels", "app")
163+
g.Expect(selector).To(Equal("case40-2"))
164+
}, defaultTimeoutSeconds, 1).Should(Succeed())
163165

164166
deleteConfigPolicies([]string{"case40"})
165167
})

0 commit comments

Comments
 (0)