Skip to content

Commit ffd8e2c

Browse files
committed
Fix when context variable isn't used for metadata
When the `.ObjectName` template variable was used outside `.metadata.name`, the controller complained that the name didn't match. This also removes an errant E2E test. Signed-off-by: Dale Haiducek <[email protected]>
1 parent e931ef2 commit ffd8e2c

File tree

5 files changed

+57
-34
lines changed

5 files changed

+57
-34
lines changed

api/v1/configurationpolicy_types.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ type Target struct {
7979
Exclude []NonEmptyString `json:"exclude,omitempty"`
8080
}
8181

82+
// IsEmpty returns whether the defined Target would always return no objects.
83+
func (t Target) IsEmpty() bool {
84+
return t.LabelSelector == nil && len(t.Include) == 0
85+
}
86+
8287
// Define String() so that the LabelSelector is dereferenced in the logs
8388
func (t Target) String() string {
8489
fmtSelectorStr := "{include:%s,exclude:%s,matchLabels:%+v,matchExpressions:%+v}"

controllers/configurationpolicy_controller.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,45 +1584,45 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
15841584
return nil, &scopedGVR, errEvent, err
15851585
}
15861586

1587+
// Populate the namespace and name if applicable
1588+
if desiredObj.GetName() == "" && name != "" {
1589+
desiredObj.SetName(strings.TrimSpace(name))
1590+
}
1591+
1592+
if desiredObj.GetNamespace() == "" && ns != "" {
1593+
desiredObj.SetNamespace(strings.TrimSpace(ns))
1594+
}
1595+
15871596
// strings.TrimSpace() is needed here because a multi-line value will have
15881597
// '\n' in it. This is kept for backwards compatibility.
15891598
desiredObj.SetName(strings.TrimSpace(desiredObj.GetName()))
15901599
desiredObj.SetKind(strings.TrimSpace(desiredObj.GetKind()))
15911600
desiredObj.SetNamespace(strings.TrimSpace(desiredObj.GetNamespace()))
15921601

1593-
// If the namespace doesn't match the original, return an error
1594-
if needsPerNamespaceTemplating && desiredObj.GetNamespace() != ns {
1602+
// Error if the namespace doesn't match the parsed namespace from the namespaceSelector
1603+
if !plc.Spec.NamespaceSelector.IsEmpty() && desiredObj.GetNamespace() != ns {
15951604
errEvent := &objectTmplEvalEvent{
15961605
compliant: false,
15971606
reason: reasonTemplateError,
1598-
message: "The object definition's namespace must match the selected namespace after template " +
1599-
"resolution",
1607+
message: "The object definition's namespace must match the result " +
1608+
"from the namespace selector after template resolution",
16001609
}
16011610

16021611
return nil, &scopedGVR, errEvent, nil
16031612
}
16041613

1605-
// If the name doesn't match the original, return an error
1606-
if needsPerNameTemplating && desiredObj.GetName() != name {
1614+
// Error if the name doesn't match the parsed name from the objectSelector
1615+
if objectSelector != nil && desiredObj.GetName() != name {
16071616
errEvent := &objectTmplEvalEvent{
16081617
compliant: false,
16091618
reason: reasonTemplateError,
1610-
message: "The object definition's name must match the selected name after template " +
1611-
"resolution",
1619+
message: "The object definition's name must match the result " +
1620+
"from the object selector after template resolution",
16121621
}
16131622

16141623
return nil, &scopedGVR, errEvent, nil
16151624
}
16161625

1617-
// Populate the namespace and name if they're not empty strings
1618-
if name != "" {
1619-
desiredObj.SetName(strings.TrimSpace(name))
1620-
}
1621-
1622-
if ns != "" {
1623-
desiredObj.SetNamespace(strings.TrimSpace(ns))
1624-
}
1625-
16261626
desiredObjects = append(desiredObjects, desiredObj)
16271627
}
16281628
}

pkg/common/namespace_selection.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ func (r *NamespaceSelectorReconciler) Get(objNS string, objName string, t policy
179179
r.lock.Unlock()
180180

181181
// Return no namespaces when both include and label selector are empty
182-
if t.LabelSelector == nil && len(t.Include) == 0 {
182+
if t.IsEmpty() {
183183
log.V(2).Info("Updating selection from Reconcile for empty selector",
184184
"namespace", objNS, "policy", objName)
185185

@@ -306,7 +306,7 @@ func (r *NamespaceSelectorReconciler) update(namespace string, name string, sel
306306
func filter(allNSList corev1.NamespaceList, t policyv1.Target) ([]string, error) {
307307
// If MatchLabels and MatchExpressions are nil, the resulting label selector
308308
// matches all namespaces. This is to guard against that.
309-
if t.LabelSelector == nil && len(t.Include) == 0 {
309+
if t.IsEmpty() {
310310
return []string{}, nil
311311
}
312312

test/e2e/case13_templatization_test.go

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -577,8 +577,8 @@ var _ = Describe("Test templatization", Ordered, func() {
577577
e2eBaseName = "case13-e2e-objectname-var"
578578
invalidPolicyYAML = case13RsrcPath + "/case13_objectname_var_invalid_name.yaml"
579579
invalidPolicyName = "case13-invalid-name"
580-
emptyPolicyYAML = case13RsrcPath + "/case13_objectname_var_empty_name.yaml"
581-
emptyPolicyName = "case13-empty-name"
580+
outsidePolicyYAML = case13RsrcPath + "/case13_objectname_var_outside_name.yaml"
581+
outsidePolicyName = "case13-outside-name"
582582
allSkippedPolicyYAML = case13RsrcPath + "/case13_objectname_var_all_skipped.yaml"
583583
allSkippedPolicyName = "case13-objectname-var-all-skipped"
584584
)
@@ -636,26 +636,42 @@ var _ = Describe("Test templatization", Ordered, func() {
636636
}
637637
})
638638

639-
It("Should fail when the name is empty after template resolution", func(ctx SpecContext) {
640-
By("Applying the " + emptyPolicyName + " ConfigurationPolicy")
641-
utils.Kubectl("apply", "-n", testNamespace, "-f", emptyPolicyYAML)
639+
It("Should succeed when context vars are in use but name/namespace are left empty", func(ctx SpecContext) {
640+
By("Applying the " + outsidePolicyName + " ConfigurationPolicy")
641+
utils.Kubectl("apply", "-n", testNamespace, "-f", outsidePolicyYAML)
642642

643-
By("By verifying that the ConfigurationPolicy is noncompliant")
643+
By("By verifying that the ConfigurationPolicy is compliant and has the correct related objects")
644644
Eventually(func(g Gomega) {
645645
managedPlc := utils.GetWithTimeout(
646646
clientManagedDynamic,
647647
gvrConfigPolicy,
648-
emptyPolicyName,
648+
outsidePolicyName,
649649
testNamespace,
650650
true,
651651
defaultTimeoutSeconds,
652652
)
653653

654-
utils.CheckComplianceStatus(g, managedPlc, "NonCompliant")
655-
g.Expect(utils.GetStatusMessage(managedPlc)).To(Equal(
656-
"The object definition's name must match the selected name after template resolution",
657-
))
654+
utils.CheckComplianceStatus(g, managedPlc, "Compliant")
655+
g.Expect(utils.GetStatusMessage(managedPlc)).Should(Equal(fmt.Sprintf(
656+
"configmaps [case13-e2e-objectname-var3] found as specified in namespace %s", e2eBaseName)))
657+
658+
relatedObjects, _, _ := unstructured.NestedSlice(managedPlc.Object, "status", "relatedObjects")
659+
g.Expect(relatedObjects).To(HaveLen(1))
660+
relatedObject, ok := relatedObjects[0].(map[string]interface{})
661+
g.Expect(ok).To(BeTrue(), "Related object is not a map")
662+
relatedObject1NS, _, _ := unstructured.NestedString(relatedObject, "object", "metadata", "name")
663+
g.Expect(relatedObject1NS).To(
664+
Equal(fmt.Sprintf("%s%d", e2eBaseName, 3)),
665+
"Related object name should match")
658666
}, defaultTimeoutSeconds, 1).Should(Succeed())
667+
668+
By("By verifying the ConfigMaps")
669+
cm, err := clientManaged.CoreV1().ConfigMaps(e2eBaseName).Get(
670+
ctx, "case13-e2e-objectname-var3", metav1.GetOptions{})
671+
Expect(err).ToNot(HaveOccurred())
672+
Expect(cm.ObjectMeta.Labels).To(HaveKeyWithValue("case13", "passed"))
673+
Expect(cm.ObjectMeta.Labels).To(HaveKeyWithValue("object-name", cm.GetName()))
674+
Expect(cm.ObjectMeta.Labels).To(HaveKeyWithValue("object-namespace", cm.GetNamespace()))
659675
})
660676

661677
It("Should fail when the name doesn't match after template resolution", func(ctx SpecContext) {
@@ -705,7 +721,7 @@ var _ = Describe("Test templatization", Ordered, func() {
705721
AfterEach(func() {
706722
utils.KubectlDelete("configurationpolicy", policyName, "-n", testNamespace)
707723
utils.KubectlDelete("configurationpolicy", invalidPolicyName, "-n", testNamespace)
708-
utils.KubectlDelete("configurationpolicy", emptyPolicyName, "-n", testNamespace)
724+
utils.KubectlDelete("configurationpolicy", outsidePolicyName, "-n", testNamespace)
709725
utils.KubectlDelete("configurationpolicy", allSkippedPolicyName, "-n", testNamespace)
710726
utils.KubectlDelete("configmaps", "-n", e2eBaseName, "--all")
711727
})

test/resources/case13_templatization/case13_objectname_var_empty_name.yaml renamed to test/resources/case13_templatization/case13_objectname_var_outside_name.yaml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
apiVersion: policy.open-cluster-management.io/v1
22
kind: ConfigurationPolicy
33
metadata:
4-
name: case13-empty-name
4+
name: case13-outside-name
55
spec:
66
remediationAction: enforce
77
namespaceSelector:
@@ -17,5 +17,7 @@ spec:
1717
apiVersion: v1
1818
kind: ConfigMap
1919
metadata:
20-
name: "{{ if false }}{{ .ObjectName }}{{ end }}"
21-
namespace: "{{ .ObjectNamespace }}"
20+
labels:
21+
case13: passed
22+
object-name: '{{ if (hasSuffix "3" .ObjectName) }}{{ .ObjectName }}{{ else }}{{ skipObject }}{{ end }}'
23+
object-namespace: "{{ .ObjectNamespace }}"

0 commit comments

Comments
 (0)