Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 104 additions & 70 deletions controllers/configurationpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -1037,9 +1037,15 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc *policyv1.Conf
resolverToUse = tmplResolver
}

desiredObjects, scopedGVR, errEvent, err := r.determineDesiredObjects(
desiredObjects, scopedGVR, determinedRelatedObjects, errEvent, err := r.determineDesiredObjects(
plc, index, objectT, resolverToUse, resolveOptions,
)

// Merge the related objects returned from determineDesiredObjects into the outer relatedObjects
for _, object := range determinedRelatedObjects {
relatedObjects = addOrUpdateRelatedObject(relatedObjects, object)
}

if err != nil {
// Return all mapping and templating errors encountered and let the caller decide if the errors should be
// retried
Expand Down Expand Up @@ -1319,6 +1325,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
) (
[]*unstructured.Unstructured,
*depclient.ScopedGVR,
[]policyv1.RelatedObject,
*objectTmplEvalEvent,
error,
) {
Expand All @@ -1339,7 +1346,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
message: "Error parsing the namespace from the object definition",
}

return nil, nil, errEvent, err
return nil, nil, nil, errEvent, err
}

objGVK := parsedMinMetadata.GroupVersionKind()
Expand All @@ -1354,18 +1361,47 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
),
}

return nil, nil, errEvent, nil
return nil, nil, nil, errEvent, nil
}

skippedObjMsg := "All objects of kind %s were skipped by the `skipObject` template function"

scopedGVR, err := r.getMapping(objGVK, plc, index)
if err != nil {
// Parse templates in a generic way to check whether skipObject was called
if tmplResolver != nil && templates.HasTemplate(objectT.ObjectDefinition.Raw, "", true) {
// Provide a full but empty context to prevent template errors
templateContext := struct {
Object map[string]any
ObjectNamespace string
ObjectName string
}{Object: map[string]any{}, ObjectNamespace: "", ObjectName: ""}

_, skipObject, _ := resolveGoTemplates(
objectT.ObjectDefinition.Raw,
templateContext,
tmplResolver,
resolveOptions,
)

if skipObject {
event := &objectTmplEvalEvent{
compliant: true,
reason: "",
message: fmt.Sprintf(skippedObjMsg, objGVK.Kind),
}

return []*unstructured.Unstructured{}, nil, nil, event, nil
}
}

errEvent := &objectTmplEvalEvent{
compliant: false,
reason: "K8s error",
message: err.Error(),
}

return nil, nil, errEvent, err
return nil, nil, nil, errEvent, err
}

// Set up relevant object namespace-name-objects map to populate with the
Expand Down Expand Up @@ -1422,7 +1458,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
message: msg,
}

return nil, &scopedGVR, errEvent, err
return nil, &scopedGVR, nil, errEvent, err
}

// Fetch object when:
Expand Down Expand Up @@ -1487,7 +1523,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(

errEvent := &objectTmplEvalEvent{false, "K8s missing namespace", msg}

return nil, &scopedGVR, errEvent, nil
return nil, &scopedGVR, nil, errEvent, nil
}

// Fetch related objects from the cluster
Expand All @@ -1511,7 +1547,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
message: msg,
}

return nil, &scopedGVR, errEvent, err
return nil, &scopedGVR, nil, errEvent, err
}
} else {
existingObj, _ = getObject(desiredNs, desiredName, scopedGVR, r.TargetK8sDynamicClient)
Expand Down Expand Up @@ -1540,7 +1576,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
message: msg,
}

return nil, &scopedGVR, errEvent, err
return nil, &scopedGVR, nil, errEvent, err
}

listOpts := metav1.ListOptions{
Expand Down Expand Up @@ -1583,7 +1619,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
message: msg,
}

return nil, &scopedGVR, errEvent, err
return nil, &scopedGVR, nil, errEvent, err
}

// Populate objects from objectSelector results
Expand Down Expand Up @@ -1650,63 +1686,16 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
if tmplResolver != nil && hasTemplate && desiredObj == nil { //nolint:gocritic
r.processedPolicyCache.Delete(plc.GetUID())

var templateContext any

// Remove managedFields because it has a key that's just a dot,
// which is problematic in the template library
unstructured.RemoveNestedField(obj.Object, "metadata", "managedFields")

// Only populate context variables as they are available:
switch {
case name != "" && ns != "":
// - Namespaced object with metadata.name or objectSelector
templateContext = struct {
Object map[string]any
ObjectNamespace string
ObjectName string
}{Object: obj.Object, ObjectNamespace: ns, ObjectName: name}

case name != "":
// - Cluster-scoped object with metadata.name or objectSelector
templateContext = struct {
Object map[string]any
ObjectName string
}{Object: obj.Object, ObjectName: name}

case ns != "":
// - Unnamed namespaced object
templateContext = struct {
ObjectNamespace string
}{ObjectNamespace: ns}
}

skipObject := false

resolveOptions.CustomFunctions = map[string]any{
"skipObject": func(skips ...any) (empty string, err error) {
switch len(skips) {
case 0:
skipObject = true
case 1:
if !skipObject {
if skip, ok := skips[0].(bool); ok {
skipObject = skip
} else {
err = fmt.Errorf(
"expected boolean but received '%v'", skips[0])
}
}
default:
err = fmt.Errorf(
"expected one optional boolean argument but received %d arguments", len(skips))
}

return empty, err
},
}
// Get the template context for resolving Go templates
templateContext := getTemplateContext(obj.Object, name, ns)

resolvedTemplate, err := tmplResolver.ResolveTemplate(
objectT.ObjectDefinition.Raw, templateContext, resolveOptions,
// Resolve the Go templates
resolvedTemplate, skipObject, err := resolveGoTemplates(
objectT.ObjectDefinition.Raw, templateContext, tmplResolver, resolveOptions,
)

if skipObject {
Expand All @@ -1731,7 +1720,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
message: complianceMsg,
}

return nil, &scopedGVR, errEvent, err
return nil, &scopedGVR, nil, errEvent, err
}

if objectT.RecordDiff == "" && resolvedTemplate.HasSensitiveData {
Expand Down Expand Up @@ -1771,7 +1760,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
index, plc.Name, err),
}

return nil, &scopedGVR, errEvent, err
return nil, &scopedGVR, nil, errEvent, err
}

// Populate the namespace and name if applicable
Expand All @@ -1798,7 +1787,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
"from the namespace selector after template resolution",
}

return nil, &scopedGVR, errEvent, nil
return nil, &scopedGVR, nil, errEvent, nil
}

// Error if the namespace is templated and returns empty.
Expand All @@ -1817,7 +1806,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
),
}

return nil, &scopedGVR, errEvent, nil
return nil, &scopedGVR, nil, errEvent, nil
}

// Error if the name doesn't match the parsed name from the objectSelector
Expand All @@ -1829,7 +1818,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
"from the object selector after template resolution",
}

return nil, &scopedGVR, errEvent, nil
return nil, &scopedGVR, nil, errEvent, nil
}

desiredObjects = append(desiredObjects, desiredObj)
Expand All @@ -1844,7 +1833,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(

switch {
case skipObjectCalled:
msg = "All objects of kind %s were skipped by the `skipObject` template function"
msg = skippedObjMsg
case objectSelector != nil:
msg = "No objects of kind %s were matched from the policy objectSelector"
default:
Expand All @@ -1857,7 +1846,7 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
message: fmt.Sprintf(msg, objGVK.Kind),
}

return nil, &scopedGVR, event, nil
return nil, &scopedGVR, nil, event, nil
}

// For mustnothave with no name and with an object selector, filter the "desired" objects
Expand All @@ -1880,10 +1869,55 @@ func (r *ConfigurationPolicyReconciler) determineDesiredObjects(
}
}

return targetedObjects, &scopedGVR, nil, nil
// If no objects were matched, return a compliant event since this is
// mustnothave and no objects were found.
if len(targetedObjects) == 0 {
var namespaces []string
var relatedObjects []policyv1.RelatedObject

// Populate the related objects list with the objects
// that were matched by the object selector.
for ns := range relevantNsNames {
namespaces = append(namespaces, ns)

for name := range relevantNsNames[ns] {
relatedObjects = addRelatedObjects(
true,
scopedGVR,
objGVK.Kind,
ns,
[]string{name},
"",
nil,
)
}
}

sort.Strings(namespaces)

nsMsg := "namespace"
if len(namespaces) > 1 {
nsMsg = "namespaces:"
}

event := &objectTmplEvalEvent{
compliant: true,
reason: "",
message: fmt.Sprintf(
"%s missing as expected in %s %s",
scopedGVR.Resource,
nsMsg,
strings.Join(namespaces, ", "),
),
}

return nil, &scopedGVR, relatedObjects, event, nil
}

return targetedObjects, &scopedGVR, nil, nil, nil
}

return desiredObjects, &scopedGVR, nil, nil
return desiredObjects, &scopedGVR, nil, nil, nil
}

// batchedEvents combines compliance events into batches that should be emitted in order. For example,
Expand Down
Loading