Skip to content

Commit 74d815f

Browse files
dhaiducekopenshift-merge-bot[bot]
authored andcommitted
fix: Allow templated namespace without nsSelector
When a `namespaceSelector` was not provided and a namespace was templated, the controller considered this invalid and didn't attempt to resolve the template, returning errors about a missing namespace. ref: https://issues.redhat.com/browse/ACM-21804 Signed-off-by: Dale Haiducek <[email protected]>
1 parent 536d691 commit 74d815f

File tree

8 files changed

+163
-22
lines changed

8 files changed

+163
-22
lines changed

controllers/configurationpolicy_controller.go

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,10 +1347,12 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
13471347
relevantNsNames := map[string]map[string]unstructured.Unstructured{}
13481348

13491349
desiredNs := parsedMinMetadata.Metadata.Namespace
1350+
hasTemplatedNs := false
13501351

13511352
// If the namespace is templated, consider it not explicitly set
13521353
if templates.HasTemplate([]byte(desiredNs), "", true) {
13531354
desiredNs = ""
1355+
hasTemplatedNs = true
13541356
}
13551357

13561358
desiredName := parsedMinMetadata.Metadata.Name
@@ -1401,27 +1403,31 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
14011403
// (Fetching for the object selector is handled later on)
14021404
fetchObject := needsObject && desiredName != "" && parsedMinMetadata.Metadata.Namespace == ""
14031405

1404-
if len(selectedNamespaces) != 0 {
1405-
for _, ns := range selectedNamespaces {
1406-
if fetchObject {
1407-
var existingObj *unstructured.Unstructured
1408-
if usingWatch {
1409-
existingObj, _ = r.getObjectFromCache(plc, ns, desiredName, objGVK)
1410-
} else {
1411-
existingObj, _ = getObject(ns, desiredName, scopedGVR, r.TargetK8sDynamicClient)
1412-
}
1406+
for _, ns := range selectedNamespaces {
1407+
if fetchObject {
1408+
var existingObj *unstructured.Unstructured
1409+
if usingWatch {
1410+
existingObj, _ = r.getObjectFromCache(plc, ns, desiredName, objGVK)
1411+
} else {
1412+
existingObj, _ = getObject(ns, desiredName, scopedGVR, r.TargetK8sDynamicClient)
1413+
}
14131414

1414-
if existingObj != nil {
1415-
relevantNsNames[ns] = map[string]unstructured.Unstructured{desiredName: *existingObj}
1416-
} else {
1417-
relevantNsNames[ns] = defaultNamesPerNs
1418-
}
1415+
if existingObj != nil {
1416+
relevantNsNames[ns] = map[string]unstructured.Unstructured{desiredName: *existingObj}
14191417
} else {
14201418
relevantNsNames[ns] = defaultNamesPerNs
14211419
}
1420+
} else {
1421+
relevantNsNames[ns] = defaultNamesPerNs
14221422
}
14231423
}
14241424

1425+
// If no namespaces were selected and the namespace is templated, set a default.
1426+
// Having an empty namespace after template resolution is handled later on.
1427+
if len(relevantNsNames) == 0 && hasTemplatedNs {
1428+
relevantNsNames[""] = defaultNamesPerNs
1429+
}
1430+
14251431
case scopedGVR.Namespaced:
14261432
// Namespaced, but a namespace was provided
14271433
relevantNsNames[desiredNs] = defaultNamesPerNs
@@ -1765,6 +1771,25 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
17651771
return nil, &scopedGVR, errEvent, nil
17661772
}
17671773

1774+
// Error if the namespace is templated and returns empty.
1775+
if scopedGVR.Namespaced && hasTemplatedNs && desiredObj.GetNamespace() == "" {
1776+
var space string
1777+
if desiredName != "" {
1778+
space = " "
1779+
}
1780+
1781+
errEvent := &objectTmplEvalEvent{
1782+
compliant: false,
1783+
reason: reasonTemplateError,
1784+
message: fmt.Sprintf("namespaced object%s%s of kind %s has no namespace specified "+
1785+
"after template resolution",
1786+
space, desiredName, objGVK.Kind,
1787+
),
1788+
}
1789+
1790+
return nil, &scopedGVR, errEvent, nil
1791+
}
1792+
17681793
// Error if the name doesn't match the parsed name from the objectSelector
17691794
if objectSelector != nil && desiredObj.GetName() != name {
17701795
errEvent := &objectTmplEvalEvent{

test/dryrun/context_vars/context_vars_test.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,22 @@ var (
2727
objUnnamedDef embed.FS
2828
//go:embed objectns_cluster_scoped
2929
objNsClusterScoped embed.FS
30+
//go:embed objectns_templated_empty
31+
objNsTemplatedEmpty embed.FS
32+
//go:embed objectns_templated_no_nsselector
33+
objNsTemplatedNoNsSelector embed.FS
3034

3135
testCases = map[string]embed.FS{
32-
"Test Object: available for namespaced objects": objNamespaced,
33-
"Test Object: available for complex objects": objPod,
34-
"Test Object: nil for objects that don't exist": objPodNsSelector,
35-
"Test Object: nil but succeeds with default function": objPodDefaultFunc,
36-
"Test Object: available for cluster-scoped objects": objClusterScoped,
37-
"Test Object: nil when namespace is templated": objTmplNs,
38-
"Test Object: unavailable for unnamed objects": objUnnamedDef,
39-
"Test ObjectNamespace: unavailable for cluster-scoped objects": objNsClusterScoped,
36+
"Test Object: available for namespaced objects": objNamespaced,
37+
"Test Object: available for complex objects": objPod,
38+
"Test Object: nil for objects that don't exist": objPodNsSelector,
39+
"Test Object: nil but succeeds with default function": objPodDefaultFunc,
40+
"Test Object: available for cluster-scoped objects": objClusterScoped,
41+
"Test Object: nil when namespace is templated": objTmplNs,
42+
"Test Object: unavailable for unnamed objects": objUnnamedDef,
43+
"Test ObjectNamespace: unavailable for cluster-scoped objects": objNsClusterScoped,
44+
"Test ObjectNamespace: noncompliant for empty templated namespace": objNsTemplatedEmpty,
45+
"Test ObjectNamespace: available for templated namespace": objNsTemplatedNoNsSelector,
4046
}
4147
)
4248

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
apiVersion: v1
3+
kind: Namespace
4+
metadata:
5+
name: my-namespace
6+
---
7+
apiVersion: v1
8+
kind: Namespace
9+
metadata:
10+
name: my-other-namespace
11+
---
12+
apiVersion: v1
13+
kind: ConfigMap
14+
metadata:
15+
name: templated-ns-configmap
16+
namespace: my-namespace
17+
data:
18+
this: thing
19+
---
20+
apiVersion: v1
21+
kind: ConfigMap
22+
metadata:
23+
name: configmap-extra
24+
namespace: my-namespace
25+
data:
26+
this: thing
27+
---
28+
apiVersion: v1
29+
kind: ConfigMap
30+
metadata:
31+
name: configmap-other-ns
32+
namespace: my-other-namespace
33+
data:
34+
this: thing
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Diffs:
2+
# Compliance messages:
3+
NonCompliant; violation - namespaced object templated-ns-configmap of kind ConfigMap has no namespace specified after template resolution
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: policy.open-cluster-management.io/v1
2+
kind: ConfigurationPolicy
3+
metadata:
4+
name: policy-object-var-templated-name
5+
spec:
6+
remediationAction: inform
7+
object-templates:
8+
- complianceType: musthave
9+
recordDiff: InStatus
10+
objectDefinition:
11+
apiVersion: v1
12+
kind: ConfigMap
13+
metadata:
14+
name: templated-ns-configmap
15+
namespace: '{{ "" }}'
16+
data:
17+
this: thing
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
apiVersion: v1
3+
kind: Namespace
4+
metadata:
5+
name: my-namespace
6+
---
7+
apiVersion: v1
8+
kind: Namespace
9+
metadata:
10+
name: my-other-namespace
11+
---
12+
apiVersion: v1
13+
kind: ConfigMap
14+
metadata:
15+
name: templated-ns-configmap
16+
namespace: my-namespace
17+
data:
18+
this: thing
19+
---
20+
apiVersion: v1
21+
kind: ConfigMap
22+
metadata:
23+
name: configmap-extra
24+
namespace: my-namespace
25+
data:
26+
this: thing
27+
---
28+
apiVersion: v1
29+
kind: ConfigMap
30+
metadata:
31+
name: configmap-other-ns
32+
namespace: my-other-namespace
33+
data:
34+
this: thing
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Diffs:
2+
v1 ConfigMap my-namespace/templated-ns-configmap:
3+
4+
# Compliance messages:
5+
Compliant; notification - configmaps [templated-ns-configmap] found as specified in namespace my-namespace
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: policy.open-cluster-management.io/v1
2+
kind: ConfigurationPolicy
3+
metadata:
4+
name: policy-object-var-templated-name
5+
spec:
6+
remediationAction: inform
7+
object-templates:
8+
- complianceType: musthave
9+
recordDiff: InStatus
10+
objectDefinition:
11+
apiVersion: v1
12+
kind: ConfigMap
13+
metadata:
14+
name: templated-ns-configmap
15+
namespace: '{{ "my-namespace" }}'
16+
data:
17+
this: thing

0 commit comments

Comments
 (0)