Skip to content

Commit 65d7d38

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 65d7d38

File tree

7 files changed

+343
-32
lines changed

7 files changed

+343
-32
lines changed

controllers/configurationpolicy_controller_test.go

Lines changed: 69 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

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
})

test/e2e/case8_status_check_test.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,133 @@ var _ = Describe("Test pod obj template handling", func() {
171171
})
172172
})
173173
})
174+
175+
var _ = Describe("Test related object property status", Ordered, func() {
176+
Describe("Create a policy missing a field added by kubernetes", Ordered, func() {
177+
const (
178+
policyNoType = "policy-service-no-type"
179+
policyWithType = "policy-service-with-type"
180+
serviceName = "grc-policy-propagator-metrics"
181+
policyNoTypeYAML = "../resources/case8_status_check/case8_service_no_type_inform.yaml"
182+
policyWithTypeYAML = "../resources/case8_status_check/case8_service_with_type_inform.yaml"
183+
serviceYAML = "../resources/case8_status_check/case8_service_with_type.yaml"
184+
)
185+
186+
It("Should be compliant when the inform policy omits a field added by the apiserver", func() {
187+
By("Creating a Service with explicit type: ClusterIP")
188+
utils.Kubectl("apply", "-f", serviceYAML)
189+
190+
By("Creating the " + policyNoType + " policy that omits the type field)")
191+
utils.Kubectl("apply", "-f", policyNoTypeYAML, "-n", testNamespace)
192+
193+
By("Verifying that the " + policyNoType + " policy is compliant")
194+
var managedPlc *unstructured.Unstructured
195+
196+
Eventually(func(g Gomega) {
197+
managedPlc = utils.GetWithTimeout(
198+
clientManagedDynamic, gvrConfigPolicy, policyNoType, testNamespace, true, defaultTimeoutSeconds,
199+
)
200+
201+
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
202+
}, defaultTimeoutSeconds, 1).Should(Succeed())
203+
204+
By("Verifying that matchesAfterDryRun is false")
205+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
206+
Expect(err).ToNot(HaveOccurred())
207+
Expect(relatedObjects).To(HaveLen(1))
208+
209+
relatedObj := relatedObjects[0].(map[string]interface{})
210+
211+
matchesAfterDryRun, _, _ := unstructured.NestedBool(relatedObj, "properties", "matchesAfterDryRun")
212+
213+
Expect(matchesAfterDryRun).To(BeFalse())
214+
})
215+
216+
It("Should be compliant when the inform policy includes all fields", func() {
217+
By("Creating the " + policyWithType + " policy that includes the type field)")
218+
utils.Kubectl("apply", "-f", policyWithTypeYAML, "-n", testNamespace)
219+
220+
By("Verifying that the " + policyWithType + " policy is compliant")
221+
var managedPlc *unstructured.Unstructured
222+
223+
Eventually(func(g Gomega) {
224+
managedPlc = utils.GetWithTimeout(
225+
clientManagedDynamic, gvrConfigPolicy, policyWithType, testNamespace, true, defaultTimeoutSeconds,
226+
)
227+
228+
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
229+
}, defaultTimeoutSeconds, 1).Should(Succeed())
230+
231+
By("Verifying that matchesAfterDryRun is true")
232+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
233+
Expect(err).ToNot(HaveOccurred())
234+
Expect(relatedObjects).To(HaveLen(1))
235+
236+
relatedObj := relatedObjects[0].(map[string]interface{})
237+
238+
matchesAfterDryRun, _, _ := unstructured.NestedBool(relatedObj, "properties", "matchesAfterDryRun")
239+
240+
Expect(matchesAfterDryRun).To(BeTrue())
241+
})
242+
243+
It("Should be compliant when the enforce policy omits a field added by the apiserver", func() {
244+
By("Changing the policy to enforce mode")
245+
utils.EnforceConfigurationPolicy(policyNoType, testNamespace)
246+
247+
By("Verifying that the " + policyNoType + " policy is compliant")
248+
var managedPlc *unstructured.Unstructured
249+
250+
Eventually(func(g Gomega) {
251+
managedPlc = utils.GetWithTimeout(
252+
clientManagedDynamic, gvrConfigPolicy, policyNoType, testNamespace, true, defaultTimeoutSeconds,
253+
)
254+
255+
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
256+
}, defaultTimeoutSeconds, 1).Should(Succeed())
257+
258+
By("Verifying that matchesAfterDryRun is false")
259+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
260+
Expect(err).ToNot(HaveOccurred())
261+
Expect(relatedObjects).To(HaveLen(1))
262+
263+
relatedObj := relatedObjects[0].(map[string]interface{})
264+
265+
matchesAfterDryRun, _, _ := unstructured.NestedBool(relatedObj, "properties", "matchesAfterDryRun")
266+
267+
Expect(matchesAfterDryRun).To(BeFalse())
268+
})
269+
270+
It("Should be compliant when the enforce policy includes all fields", func() {
271+
By("Changing the policy to enforce mode")
272+
utils.EnforceConfigurationPolicy(policyWithType, testNamespace)
273+
274+
By("Verifying that the " + policyWithType + " policy is compliant")
275+
var managedPlc *unstructured.Unstructured
276+
277+
Eventually(func(g Gomega) {
278+
managedPlc = utils.GetWithTimeout(
279+
clientManagedDynamic, gvrConfigPolicy, policyWithType, testNamespace, true, defaultTimeoutSeconds,
280+
)
281+
282+
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
283+
}, defaultTimeoutSeconds, 1).Should(Succeed())
284+
285+
By("Verifying that matchesAfterDryRun is true")
286+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
287+
Expect(err).ToNot(HaveOccurred())
288+
Expect(relatedObjects).To(HaveLen(1))
289+
290+
relatedObj := relatedObjects[0].(map[string]interface{})
291+
292+
matchesAfterDryRun, _, _ := unstructured.NestedBool(relatedObj, "properties", "matchesAfterDryRun")
293+
294+
Expect(matchesAfterDryRun).To(BeTrue())
295+
})
296+
297+
AfterAll(func() {
298+
deleteConfigPolicies([]string{policyNoType, policyWithType})
299+
300+
utils.KubectlDelete("service", serviceName, "-n", "managed")
301+
})
302+
})
303+
})
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
apiVersion: policy.open-cluster-management.io/v1
2+
kind: ConfigurationPolicy
3+
metadata:
4+
name: policy-service-no-type
5+
namespace: managed
6+
spec:
7+
remediationAction: inform
8+
severity: low
9+
object-templates:
10+
- complianceType: mustonlyhave
11+
metadataComplianceType: musthave
12+
objectDefinition:
13+
apiVersion: v1
14+
kind: Service
15+
metadata:
16+
name: grc-policy-propagator-metrics
17+
namespace: managed
18+
spec: # Intentionally omitting 'type' field - K8s will default to ClusterIP
19+
clusterIP: '{{ (lookup "v1" "Service" "managed" "grc-policy-propagator-metrics").spec.clusterIP }}'
20+
clusterIPs:
21+
- '{{ (lookup "v1" "Service" "managed" "grc-policy-propagator-metrics").spec.clusterIP }}'
22+
internalTrafficPolicy: Cluster
23+
ipFamilies:
24+
- IPv4
25+
ipFamilyPolicy: SingleStack
26+
ports:
27+
- name: metrics-https
28+
port: 8443
29+
protocol: TCP
30+
targetPort: 8443
31+
selector:
32+
app: grc
33+
component: ocm-policy-propagator
34+
release: grc
35+
sessionAffinity: None
36+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: grc-policy-propagator-metrics
5+
namespace: managed
6+
annotations:
7+
test: test
8+
spec:
9+
internalTrafficPolicy: Cluster
10+
ipFamilies:
11+
- IPv4
12+
ipFamilyPolicy: SingleStack
13+
ports:
14+
- name: metrics-https
15+
port: 8443
16+
protocol: TCP
17+
targetPort: 8443
18+
selector:
19+
app: grc
20+
component: ocm-policy-propagator
21+
release: grc
22+
sessionAffinity: None
23+
type: ClusterIP

0 commit comments

Comments
 (0)