Skip to content

Commit 98f0d1b

Browse files
committed
Fix objectSelector polling
The objectSelector was always running in watch mode regardless of the evaluationInterval. ref: https://issues.redhat.com/browse/ACM-15952 Signed-off-by: Dale Haiducek <[email protected]>
1 parent facb1a8 commit 98f0d1b

File tree

4 files changed

+164
-1
lines changed

4 files changed

+164
-1
lines changed

controllers/configurationpolicy_controller.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1422,9 +1422,30 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
14221422
return nil, &scopedGVR, errEvent, err
14231423
}
14241424

1425+
usingWatch := currentlyUsingWatch(plc)
1426+
listOpts := metav1.ListOptions{
1427+
LabelSelector: objSelector.String(),
1428+
}
1429+
14251430
// Has a valid objectSelector, so list the names for each namespace using the objectSelector
14261431
for ns := range relevantNsNames {
1427-
filteredObjects, err := r.DynamicWatcher.List(plc.ObjectIdentifier(), objGVK, ns, objSelector)
1432+
var filteredObjects []unstructured.Unstructured
1433+
var err error
1434+
1435+
// If watch is enabled, use the dynamic watcher, otherwise use the controller dynamic client
1436+
if usingWatch {
1437+
filteredObjects, err = r.DynamicWatcher.List(plc.ObjectIdentifier(), objGVK, ns, objSelector)
1438+
} else {
1439+
var filteredObjectList *unstructured.UnstructuredList
1440+
filteredObjectList, err = r.TargetK8sDynamicClient.Resource(
1441+
scopedGVR.GroupVersionResource,
1442+
).Namespace(ns).List(context.TODO(), listOpts)
1443+
1444+
if err == nil {
1445+
filteredObjects = filteredObjectList.Items
1446+
}
1447+
}
1448+
14281449
if err != nil {
14291450
log.Error(err, "Failed to fetch the resources",
14301451
"objectSelector", fmt.Sprint(objectT.ObjectSelector.String()))

test/e2e/case42_resource_selection_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88

99
. "github.com/onsi/ginkgo/v2"
1010
. "github.com/onsi/gomega"
11+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1112

1213
"open-cluster-management.io/config-policy-controller/test/utils"
1314
)
@@ -238,3 +239,111 @@ var _ = Describe("Test behavior of resource selection as resources change", Orde
238239
"fakeapis [case42-3-e2e] found as specified in namespace %s", targetNs)))
239240
})
240241
})
242+
243+
var _ = Describe("Test evaluation of resource selection", Ordered, func() {
244+
const (
245+
targetNs = "case42-e2e-3"
246+
prereqYaml = "../resources/case42_resource_selector/case42_eval_prereq.yaml"
247+
policyYaml = "../resources/case42_resource_selector/case42_eval_policy.yaml"
248+
policyName = "case42-selector-eval-e2e"
249+
)
250+
251+
BeforeAll(func() {
252+
By("Applying prerequisites")
253+
utils.Kubectl("apply", "-n", targetNs, "-f", prereqYaml)
254+
DeferCleanup(func() {
255+
utils.KubectlDelete("-n", targetNs, "-f", prereqYaml)
256+
})
257+
258+
utils.Kubectl("apply", "-f", policyYaml, "-n", testNamespace)
259+
DeferCleanup(func() {
260+
utils.KubectlDelete("-f", policyYaml, "-n", testNamespace)
261+
})
262+
263+
By("Verifying initial compliance message")
264+
Eventually(func() interface{} {
265+
managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
266+
policyName, testNamespace, true, defaultTimeoutSeconds)
267+
268+
return utils.GetStatusMessage(managedPlc)
269+
}, defaultTimeoutSeconds, 1).Should(Equal(fmt.Sprintf(
270+
"fakeapis [case42-1-e2e] found but not as specified in namespace %s", targetNs)))
271+
})
272+
273+
It("does not re-evaluate when the evaluation interval is in watch", func() {
274+
managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
275+
policyName, testNamespace, true, defaultTimeoutSeconds)
276+
277+
evalTime, found, err := unstructured.NestedString(managedPlc.Object, "status", "lastEvaluated")
278+
Expect(evalTime).ToNot(BeEmpty())
279+
Expect(found).To(BeTrue())
280+
Expect(err).ToNot(HaveOccurred())
281+
282+
By("Verifying it does not evaluate again")
283+
Consistently(func() interface{} {
284+
managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
285+
policyName, testNamespace, true, defaultTimeoutSeconds)
286+
287+
newestEvalTime, found, err := unstructured.NestedString(managedPlc.Object, "status", "lastEvaluated")
288+
Expect(newestEvalTime).ToNot(BeEmpty())
289+
Expect(found).To(BeTrue())
290+
Expect(err).ToNot(HaveOccurred())
291+
292+
return newestEvalTime
293+
}, defaultConsistentlyDuration, "3s").Should(Equal(evalTime))
294+
})
295+
296+
FIt("re-evaluates when the evaluation interval is set to a duration", func() {
297+
managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
298+
policyName, testNamespace, true, defaultTimeoutSeconds)
299+
300+
By("Fetching the current evaluation timestamp")
301+
currentEvalTime, found, err := unstructured.NestedString(managedPlc.Object, "status", "lastEvaluated")
302+
Expect(currentEvalTime).ToNot(BeEmpty())
303+
Expect(found).To(BeTrue())
304+
Expect(err).ToNot(HaveOccurred())
305+
306+
By("Updating the evaluationInterval to 5s")
307+
utils.Kubectl("patch", "--namespace=managed", "configurationpolicy", policyName, "--type=json",
308+
`--patch=[{
309+
"op": "replace",
310+
"path": "/spec/evaluationInterval",
311+
"value": {
312+
"compliant": "5s",
313+
"noncompliant": "5s",
314+
}
315+
}]`,
316+
)
317+
318+
var nextEvalTime string
319+
320+
By("Waiting for the first evaluation after the spec update")
321+
Eventually(func() interface{} {
322+
managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
323+
policyName, testNamespace, true, defaultTimeoutSeconds)
324+
325+
nextEvalTime, found, err := unstructured.NestedString(managedPlc.Object, "status", "lastEvaluated")
326+
Expect(nextEvalTime).ToNot(BeEmpty())
327+
Expect(found).To(BeTrue())
328+
Expect(err).ToNot(HaveOccurred())
329+
330+
return nextEvalTime
331+
}, defaultTimeoutSeconds, 1).ShouldNot(Equal(currentEvalTime))
332+
333+
By("Verifying it evaluates each interval")
334+
Consistently(func() interface{} {
335+
currentEvalTime = nextEvalTime
336+
managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy,
337+
policyName, testNamespace, true, defaultTimeoutSeconds)
338+
339+
nextEvalTime, found, err := unstructured.NestedString(managedPlc.Object, "status", "lastEvaluated")
340+
Expect(nextEvalTime).ToNot(BeEmpty())
341+
Expect(found).To(BeTrue())
342+
Expect(err).ToNot(HaveOccurred())
343+
Expect(utils.GetStatusMessage(managedPlc)).To(Equal(fmt.Sprintf(
344+
"fakeapis [case42-1-e2e] found but not as specified in namespace %s", targetNs)))
345+
346+
return nextEvalTime
347+
}, defaultConsistentlyDuration*2, "10s").ShouldNot(Equal(currentEvalTime))
348+
})
349+
})
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
apiVersion: policy.open-cluster-management.io/v1
2+
kind: ConfigurationPolicy
3+
metadata:
4+
name: case42-selector-eval-e2e
5+
spec:
6+
evaluationInterval:
7+
compliant: watch
8+
noncompliant: watch
9+
remediationAction: inform
10+
object-templates:
11+
- complianceType: musthave
12+
objectSelector:
13+
matchExpressions:
14+
- key: case42
15+
operator: Exists
16+
objectDefinition:
17+
apiVersion: config-policy-controller.io/v1
18+
kind: FakeAPI
19+
metadata:
20+
labels:
21+
selected: "true"
22+
namespace: case42-e2e-3
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: v1
2+
kind: Namespace
3+
metadata:
4+
name: case42-e2e-3
5+
---
6+
apiVersion: config-policy-controller.io/v1
7+
kind: FakeAPI
8+
metadata:
9+
labels:
10+
case42: case42-1-e2e
11+
name: case42-1-e2e

0 commit comments

Comments
 (0)