From ad77733a364ec1da5ce2e8589efb21088b35863f Mon Sep 17 00:00:00 2001 From: Dale Haiducek <19750917+dhaiducek@users.noreply.github.com> Date: Wed, 26 Feb 2025 12:27:33 -0500 Subject: [PATCH] Add `objectSelector` ref: https://issues.redhat.com/browse/ACM-18011 Signed-off-by: Dale Haiducek <19750917+dhaiducek@users.noreply.github.com> --- docs/policygenerator-reference.yaml | 9 +++++ internal/plugin.go | 10 +++++- internal/plugin_test.go | 56 ++++++++++++++++++++--------- internal/types/types.go | 17 ++++++--- internal/utils.go | 5 +++ 5 files changed, 75 insertions(+), 22 deletions(-) diff --git a/docs/policygenerator-reference.yaml b/docs/policygenerator-reference.yaml index ac832d2..e77117a 100644 --- a/docs/policygenerator-reference.yaml +++ b/docs/policygenerator-reference.yaml @@ -119,6 +119,11 @@ policyDefaults: exclude: [] matchLabels: {} matchExpressions: [] + # Optional. Defines the label selector for objects defined in the `objectDefinition`. If there is an object name + # defined in the `objectDefinition`, the `objectSelector` is ignored. + objectSelector: + matchLabels: {} + matchExpressions: [] # Optional. Determines whether to define extraDependencies on policy templates so that they are applied in the order # they are defined in the manifests list for that policy. Cannot be specified when consolidateManifests is set to # true. Cannot be specified at the same time as extraDependencies. @@ -260,6 +265,8 @@ policies: # Optional. (See policyDefaults.namespaceSelector for description.) # Cannot be specified when policyDefaults.consolidateManifests is set to true. namespaceSelector: {} + # Optional. (See policyDefaults.objectSelector for description.) + objectSelector: {} # Optional. (See policyDefaults.customMessage for description.) # Cannot be specified when policyDefaults.consolidateManifests is set to true. customMessage: @@ -359,6 +366,8 @@ policies: consolidateManifests: true # Optional. (See policyDefaults.namespaceSelector for description.) namespaceSelector: {} + # Optional. (See policyDefaults.objectSelector for description.) + objectSelector: {} # Optional. (See policyDefaults.orderManifests for description.) # Cannot be specified when consolidateManifests is set to true. # If set true here, the default extraDependencies will be overwritten. diff --git a/internal/plugin.go b/internal/plugin.go index ca9879a..332f8c3 100644 --- a/internal/plugin.go +++ b/internal/plugin.go @@ -587,6 +587,10 @@ func (p *Plugin) applyDefaults(unmarshaledConfig map[string]interface{}) { policy.Description = p.PolicyDefaults.Description } + if policy.ObjectSelector.IsUnset() && !p.PolicyDefaults.ObjectSelector.IsUnset() { + policy.ObjectSelector = p.PolicyDefaults.ObjectSelector + } + if policy.RecreateOption == "" { policy.RecreateOption = p.PolicyDefaults.RecreateOption } @@ -723,7 +727,7 @@ func (p *Plugin) applyDefaults(unmarshaledConfig map[string]interface{}) { defNsSelector := p.PolicyDefaults.NamespaceSelector if nsSelector.Exclude == nil && nsSelector.Include == nil && - nsSelector.MatchLabels == nil && nsSelector.MatchExpressions == nil { + nsSelector.LabelSelector.IsUnset() { policy.NamespaceSelector = defNsSelector } @@ -798,6 +802,10 @@ func (p *Plugin) applyDefaults(unmarshaledConfig map[string]interface{}) { manifest.Severity = policy.Severity } + if manifest.ObjectSelector.IsUnset() && !policy.ObjectSelector.IsUnset() { + manifest.ObjectSelector = policy.ObjectSelector + } + if manifest.RecreateOption == "" { manifest.RecreateOption = policy.RecreateOption } diff --git a/internal/plugin_test.go b/internal/plugin_test.go index 51b753a..1d37d87 100644 --- a/internal/plugin_test.go +++ b/internal/plugin_test.go @@ -44,6 +44,9 @@ func TestGenerate(t *testing.T) { p.PolicyDefaults.MetadataComplianceType = "musthave" p.PolicyDefaults.RecordDiff = "Log" p.PolicyDefaults.RecreateOption = "IfRequired" + p.PolicyDefaults.ObjectSelector = types.LabelSelector{ + MatchLabels: &map[string]string{}, + } p.PolicyDefaults.PruneObjectBehavior = "DeleteAll" patch := map[string]interface{}{ "metadata": map[string]interface{}{ @@ -56,6 +59,9 @@ func TestGenerate(t *testing.T) { Name: "policy-app-config", ConfigurationPolicyOptions: types.ConfigurationPolicyOptions{ PruneObjectBehavior: "None", + ObjectSelector: types.LabelSelector{ + MatchLabels: &map[string]string{"phoebe": "buffay"}, + }, }, Manifests: []types.Manifest{ { @@ -72,6 +78,9 @@ func TestGenerate(t *testing.T) { MetadataComplianceType: "mustonlyhave", RecordDiff: "None", RecreateOption: "None", + ObjectSelector: types.LabelSelector{ + MatchExpressions: &[]metav1.LabelSelectorRequirement{}, + }, }, Path: path.Join(tmpDir, "configmap.yaml"), }, @@ -121,6 +130,9 @@ spec: labels: chandler: bing name: my-configmap + objectSelector: + matchLabels: + phoebe: buffay recordDiff: Log recreateOption: IfRequired pruneObjectBehavior: None @@ -157,6 +169,8 @@ spec: kind: ConfigMap metadata: name: my-configmap + objectSelector: + matchExpressions: [] recordDiff: None recreateOption: None pruneObjectBehavior: DeleteAll @@ -3999,23 +4013,27 @@ func TestCreatePolicyWithNamespaceSelector(t *testing.T) { "nil-selector": {namespaceSelector: types.NamespaceSelector{}}, "empty-selector-values": { namespaceSelector: types.NamespaceSelector{ - Include: []string{}, - Exclude: []string{}, - MatchLabels: &map[string]string{}, - MatchExpressions: &[]metav1.LabelSelectorRequirement{}, + Include: []string{}, + Exclude: []string{}, + LabelSelector: types.LabelSelector{ + MatchLabels: &map[string]string{}, + MatchExpressions: &[]metav1.LabelSelectorRequirement{}, + }, }, }, "completely-filled-values": { namespaceSelector: types.NamespaceSelector{ Include: []string{"test-ns-1", "test-ns-2"}, Exclude: []string{"*-ns-[1]"}, - MatchLabels: &map[string]string{ - "testing": "is awesome", + LabelSelector: types.LabelSelector{ + MatchLabels: &map[string]string{ + "testing": "is awesome", + }, + MatchExpressions: &[]metav1.LabelSelectorRequirement{{ + Key: "door", + Operator: "Exists", + }}, }, - MatchExpressions: &[]metav1.LabelSelectorRequirement{{ - Key: "door", - Operator: "Exists", - }}, }, }, "include-exclude-only": { @@ -4026,13 +4044,15 @@ func TestCreatePolicyWithNamespaceSelector(t *testing.T) { }, "label-selectors-only": { namespaceSelector: types.NamespaceSelector{ - MatchLabels: &map[string]string{ - "testing": "is awesome", + LabelSelector: types.LabelSelector{ + MatchLabels: &map[string]string{ + "testing": "is awesome", + }, + MatchExpressions: &[]metav1.LabelSelectorRequirement{{ + Key: "door", + Operator: "Exists", + }}, }, - MatchExpressions: &[]metav1.LabelSelectorRequirement{{ - Key: "door", - Operator: "Exists", - }}, }, }, } @@ -4046,7 +4066,9 @@ func TestCreatePolicyWithNamespaceSelector(t *testing.T) { p := Plugin{} p.PolicyDefaults.Namespace = "my-policies" p.PolicyDefaults.NamespaceSelector = types.NamespaceSelector{ - MatchLabels: &map[string]string{}, + LabelSelector: types.LabelSelector{ + MatchLabels: &map[string]string{}, + }, } policyConf := types.PolicyConfig{ Name: "policy-app-config", Manifests: []types.Manifest{ diff --git a/internal/types/types.go b/internal/types/types.go index dbcfb74..5532d11 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -56,6 +56,7 @@ type ConfigurationPolicyOptions struct { RecordDiff string `json:"recordDiff,omitempty" yaml:"recordDiff,omitempty"` RecreateOption string `json:"recreateOption,omitempty" yaml:"recreateOption,omitempty"` CustomMessage CustomMessage `json:"customMessage,omitempty" yaml:"customMessage,omitempty"` + ObjectSelector LabelSelector `json:"objectSelector,omitempty" yaml:"objectSelector,omitempty"` } type GatekeeperOptions struct { @@ -77,17 +78,25 @@ type Filepath struct { Path string `json:"path,omitempty" yaml:"path,omitempty"` } -type NamespaceSelector struct { - Exclude []string `json:"exclude,omitempty" yaml:"exclude,omitempty"` - Include []string `json:"include,omitempty" yaml:"include,omitempty"` +type LabelSelector struct { MatchLabels *map[string]string `json:"matchLabels,omitempty" yaml:"matchLabels,omitempty"` MatchExpressions *[]metav1.LabelSelectorRequirement `json:"matchExpressions,omitempty" yaml:"matchExpressions,omitempty"` } +func (s LabelSelector) IsUnset() bool { + return s.MatchExpressions == nil && s.MatchLabels == nil +} + +type NamespaceSelector struct { + LabelSelector `json:",inline" yaml:",inline"` + Exclude []string `json:"exclude,omitempty" yaml:"exclude,omitempty"` + Include []string `json:"include,omitempty" yaml:"include,omitempty"` +} + // Define String() so that the LabelSelector is dereferenced in the logs func (t NamespaceSelector) String() string { fmtSelectorStr := "{include:%s,exclude:%s,matchLabels:%+v,matchExpressions:%+v}" - if t.MatchLabels == nil && t.MatchExpressions == nil { + if t.LabelSelector.IsUnset() { return fmt.Sprintf(fmtSelectorStr, t.Include, t.Exclude, nil, nil) } diff --git a/internal/utils.go b/internal/utils.go index 18f8307..d0f7907 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -180,6 +180,7 @@ func getPolicyTemplates(policyConf *types.PolicyConfig) ([]map[string]interface{ recordDiff := policyConf.Manifests[i].RecordDiff ignorePending := policyConf.Manifests[i].IgnorePending extraDeps := policyConf.Manifests[i].ExtraDependencies + objSelector := policyConf.Manifests[i].ObjectSelector policyName := policyConf.Manifests[i].Name if policyName == "" { @@ -253,6 +254,10 @@ func getPolicyTemplates(policyConf *types.PolicyConfig) ([]map[string]interface{ objTemplate["metadataComplianceType"] = metadataComplianceType } + if !objSelector.IsUnset() { + objTemplate["objectSelector"] = objSelector + } + if recreateOption != "" { objTemplate["recreateOption"] = recreateOption }