Skip to content

Commit

Permalink
Allow multiline templatization
Browse files Browse the repository at this point in the history
Adds a new field, object-templates-raw, that allows users to paste in an object template. This makes multiline templates, like those using the range keyword, possible. It can be set instead of object-templates, but both cannot be set.

ref: https://issues.redhat.com/browse/ACM-2739?filter=-1

Signed-off-by: Will Kutler <wkutler@redhat.com>
  • Loading branch information
willkutler committed Jan 19, 2023
1 parent aaf185e commit 22908a5
Show file tree
Hide file tree
Showing 14 changed files with 582 additions and 18 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
.PHONY: manifests
manifests: controller-gen
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=config-policy-controller paths="./..." output:crd:artifacts:config=deploy/crds output:rbac:artifacts:config=deploy/rbac
mv deploy/crds/policy.open-cluster-management.io_configurationpolicies.yaml deploy/crds/kustomize/policy.open-cluster-management.io_configurationpolicies.yaml
# Add a newline so that the format matches what kubebuilder generates
@printf "\n---\n" > deploy/crds/policy.open-cluster-management.io_configurationpolicies.yaml
$(KUSTOMIZE) build deploy/crds/kustomize >> deploy/crds/policy.open-cluster-management.io_configurationpolicies.yaml

.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
Expand Down
1 change: 1 addition & 0 deletions api/v1/configurationpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ type ConfigurationPolicySpec struct {
// retrieve namespaces.
NamespaceSelector Target `json:"namespaceSelector,omitempty"`
ObjectTemplates []*ObjectTemplate `json:"object-templates,omitempty"`
ObjectTemplatesRaw string `json:"object-templates-raw,omitempty"`
EvaluationInterval EvaluationInterval `json:"evaluationInterval,omitempty"`
// +kubebuilder:default:=None
PruneObjectBehavior PruneObjectBehavior `json:"pruneObjectBehavior,omitempty"`
Expand Down
59 changes: 45 additions & 14 deletions controllers/configurationpolicy_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,6 +773,22 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi
}
}

// set up raw data for template processing
var rawDataList [][]byte
var isRawObjTemplate bool

if plc.Spec.ObjectTemplatesRaw != "" {
rawDataList = [][]byte{[]byte(plc.Spec.ObjectTemplatesRaw)}
isRawObjTemplate = true
} else {
for _, objectT := range plc.Spec.ObjectTemplates {
rawDataList = append(rawDataList, objectT.ObjectDefinition.Raw)
}
isRawObjTemplate = false
}

tmplResolverCfg.InputIsYAML = isRawObjTemplate

tmplResolver, err := templates.NewResolver(&r.TargetK8sClient, r.TargetK8sConfig, tmplResolverCfg)
if err != nil {
// If the encryption key is invalid, clear the cache.
Expand All @@ -791,10 +807,13 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi
if !disableTemplates {
startTime := time.Now().UTC()

for _, objectT := range plc.Spec.ObjectTemplates {
var objTemps []*policyv1.ObjectTemplate

// process object templates for go template usage
for i, rawData := range rawDataList {
// first check to make sure there are no hub-templates with delimiter - {{hub
// if one exists, it means the template resolution on the hub did not succeed.
if templates.HasTemplate(objectT.ObjectDefinition.Raw, "{{hub", false) {
if templates.HasTemplate(rawData, "{{hub", false) {
// check to see there is an annotation set to the hub error msg,
// if not ,set a generic msg
hubTemplatesErrMsg, ok := annotations["policy.open-cluster-management.io/hub-templates-error"]
Expand All @@ -814,10 +833,10 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi
return
}

if templates.HasTemplate(objectT.ObjectDefinition.Raw, "", true) {
if templates.HasTemplate(rawData, "", true) {
log.V(1).Info("Processing policy templates")

resolvedTemplate, tplErr := tmplResolver.ResolveTemplate(objectT.ObjectDefinition.Raw, nil)
resolvedTemplate, tplErr := tmplResolver.ResolveTemplate(rawData, nil)

if errors.Is(tplErr, templates.ErrMissingAPIResource) ||
errors.Is(tplErr, templates.ErrMissingAPIResourceInvalidTemplate) {
Expand All @@ -829,7 +848,7 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi
discoveryErr := r.refreshDiscoveryInfo()
if discoveryErr == nil {
tmplResolver.SetKubeAPIResourceList(r.apiResourceList)
resolvedTemplate, tplErr = tmplResolver.ResolveTemplate(objectT.ObjectDefinition.Raw, nil)
resolvedTemplate, tplErr = tmplResolver.ResolveTemplate(rawData, nil)
} else {
log.V(2).Info(
"Failed to refresh the API discovery information after a template encountered an unknown " +
Expand Down Expand Up @@ -871,7 +890,7 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi
return
}

resolvedTemplate, tplErr = tmplResolver.ResolveTemplate(objectT.ObjectDefinition.Raw, nil)
resolvedTemplate, tplErr = tmplResolver.ResolveTemplate(rawData, nil)
}

if tplErr != nil {
Expand All @@ -880,8 +899,22 @@ func (r *ConfigurationPolicyReconciler) handleObjectTemplates(plc policyv1.Confi
return
}

// Set the resolved data for use in further processing
objectT.ObjectDefinition.Raw = resolvedTemplate.ResolvedJSON
// If raw data, only one passthrough is needed, since all the object templates are in it
if isRawObjTemplate {
err := json.Unmarshal(resolvedTemplate.ResolvedJSON, &objTemps)
if err != nil {
addTemplateErrorViolation("Error unmarshalling raw template", err.Error())

return
}

plc.Spec.ObjectTemplates = objTemps

break
}

// Otherwise, set the resolved data for use in further processing
plc.Spec.ObjectTemplates[i].ObjectDefinition.Raw = resolvedTemplate.ResolvedJSON
}
}

Expand Down Expand Up @@ -2503,13 +2536,11 @@ func (r *ConfigurationPolicyReconciler) addForUpdate(policy *policyv1.Configurat
if policy.Spec == nil {
compliant = false
} else {
for index := range policy.Spec.ObjectTemplates {
if index < len(policy.Status.CompliancyDetails) {
if policy.Status.CompliancyDetails[index].ComplianceState == policyv1.NonCompliant {
compliant = false
for index := range policy.Status.CompliancyDetails {
if policy.Status.CompliancyDetails[index].ComplianceState == policyv1.NonCompliant {
compliant = false

break
}
break
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions deploy/crds/kustomize/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
resources:
- policy.open-cluster-management.io_configurationpolicies.yaml

# Add validation more complicated than Kubebuilder markers can provide
patches:
- path: obj-template-validation.json
target:
group: apiextensions.k8s.io
version: v1
kind: CustomResourceDefinition
name: configurationpolicies.policy.open-cluster-management.io
11 changes: 11 additions & 0 deletions deploy/crds/kustomize/obj-template-validation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"op":"add",
"path":"/spec/versions/0/schema/openAPIV3Schema/properties/spec/oneOf",
"value": [{
"required": ["object-templates"]
},{
"required": ["object-templates-raw"]
}]
}
]
Loading

0 comments on commit 22908a5

Please sign in to comment.