Skip to content

Commit d252b39

Browse files
jan-lawopenshift-merge-bot[bot]
authored andcommitted
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 bd2dd07 commit d252b39

File tree

6 files changed

+290
-32
lines changed

6 files changed

+290
-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: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,117 @@ 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+
policyName = "policy-service"
179+
serviceName = "grc-policy-propagator-metrics"
180+
policyYAML = "../resources/case8_status_check/case8_service_inform.yaml"
181+
serviceYAML = "../resources/case8_status_check/case8_service.yaml"
182+
)
183+
184+
It("Should be compliant when the inform policy omits a field added by the apiserver", func() {
185+
By("Creating a Service with explicit type: ClusterIP")
186+
utils.Kubectl("apply", "-f", serviceYAML)
187+
188+
By("Creating the " + policyName + " policy that omits the type field)")
189+
utils.Kubectl("apply", "-f", policyYAML, "-n", testNamespace)
190+
191+
By("Verifying that the " + policyName + " policy is compliant")
192+
Eventually(func(g Gomega) {
193+
managedPlc := utils.GetWithTimeout(
194+
clientManagedDynamic, gvrConfigPolicy, policyName, testNamespace, true, defaultTimeoutSeconds,
195+
)
196+
197+
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
198+
199+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
200+
g.Expect(err).ToNot(HaveOccurred())
201+
g.Expect(relatedObjects).To(HaveLen(1))
202+
203+
relatedObj := relatedObjects[0].(map[string]interface{})
204+
matchesAfterDryRun, _, _ := unstructured.NestedBool(relatedObj, "properties", "matchesAfterDryRun")
205+
206+
g.Expect(matchesAfterDryRun).To(BeFalse())
207+
}, defaultTimeoutSeconds, 1).Should(Succeed())
208+
})
209+
210+
It("Should be compliant when the enforce policy omits a field added by the apiserver", func() {
211+
By("Changing the policy to enforce mode")
212+
utils.EnforceConfigurationPolicy(policyName, testNamespace)
213+
214+
By("Verifying that the " + policyName + " policy is compliant")
215+
Eventually(func(g Gomega) {
216+
managedPlc := utils.GetWithTimeout(
217+
clientManagedDynamic, gvrConfigPolicy, policyName, testNamespace, true, defaultTimeoutSeconds,
218+
)
219+
220+
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
221+
222+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
223+
g.Expect(err).ToNot(HaveOccurred())
224+
g.Expect(relatedObjects).To(HaveLen(1))
225+
226+
relatedObj := relatedObjects[0].(map[string]interface{})
227+
matchesAfterDryRun, _, _ := unstructured.NestedBool(relatedObj, "properties", "matchesAfterDryRun")
228+
229+
g.Expect(matchesAfterDryRun).To(BeFalse())
230+
}, defaultTimeoutSeconds, 1).Should(Succeed())
231+
})
232+
233+
It("Should be compliant when the enforce policy includes all fields", func() {
234+
By("Patching the " + policyName + " policy with the Service type field)")
235+
utils.Kubectl("patch", "configurationpolicy", policyName, "-n", testNamespace, "--type=json", "-p",
236+
`[{"op": "add", "path": "/spec/object-templates/0/objectDefinition/spec/type", "value": "ClusterIP"}]`)
237+
238+
By("Verifying that the " + policyName + " policy is compliant")
239+
Eventually(func(g Gomega) {
240+
managedPlc := utils.GetWithTimeout(
241+
clientManagedDynamic, gvrConfigPolicy, policyName, testNamespace, true, defaultTimeoutSeconds,
242+
)
243+
244+
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
245+
246+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
247+
g.Expect(err).ToNot(HaveOccurred())
248+
g.Expect(relatedObjects).To(HaveLen(1))
249+
250+
relatedObj := relatedObjects[0].(map[string]interface{})
251+
matchesAfterDryRun, _, _ := unstructured.NestedBool(relatedObj, "properties", "matchesAfterDryRun")
252+
253+
g.Expect(matchesAfterDryRun).To(BeTrue())
254+
}, defaultTimeoutSeconds, 1).Should(Succeed())
255+
})
256+
257+
It("Should be compliant when the inform policy includes all fields", func() {
258+
By("Changing the policy to inform mode")
259+
utils.Kubectl("patch", "configurationpolicy", policyName, `--type=json`,
260+
`-p=[{"op":"replace","path":"/spec/remediationAction","value":"inform"}]`, "-n", testNamespace)
261+
262+
By("Verifying that the " + policyName + " policy is compliant")
263+
Eventually(func(g Gomega) {
264+
managedPlc := utils.GetWithTimeout(
265+
clientManagedDynamic, gvrConfigPolicy, policyName, testNamespace, true, defaultTimeoutSeconds,
266+
)
267+
268+
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
269+
270+
relatedObjects, _, err := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
271+
g.Expect(err).ToNot(HaveOccurred())
272+
g.Expect(relatedObjects).To(HaveLen(1))
273+
274+
relatedObj := relatedObjects[0].(map[string]interface{})
275+
matchesAfterDryRun, _, _ := unstructured.NestedBool(relatedObj, "properties", "matchesAfterDryRun")
276+
277+
g.Expect(matchesAfterDryRun).To(BeTrue())
278+
}, defaultTimeoutSeconds, 1).Should(Succeed())
279+
})
280+
281+
AfterAll(func() {
282+
deleteConfigPolicies([]string{policyName, policyName})
283+
284+
utils.KubectlDelete("service", serviceName, "-n", "managed")
285+
})
286+
})
287+
})
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
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
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+

0 commit comments

Comments
 (0)