Skip to content

Commit 412f96b

Browse files
committed
Support PruneObjectBehavior
ref: stolostron/backlog#25353 Signed-off-by: Dale Haiducek <[email protected]>
1 parent d7c58a0 commit 412f96b

File tree

7 files changed

+114
-3
lines changed

7 files changed

+114
-3
lines changed

docs/policygenerator-reference.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ policyDefaults:
4343
# avoid evaluating the policy after it has become a particular compliance state.
4444
compliant: 30m
4545
noncompliant: 45s
46+
# Optional. Determines whether objects created or monitored by the policy should be deleted when the policy is
47+
# deleted. Pruning only takes place if the remediation action of the policy has been set to "enforce". Example values
48+
# are "DeleteIfCreated", "DeleteAll", or "None". This defaults to unset, which is equivalent to "None".
49+
pruneObjectBehavior: "None"
4650
# Optional. When the policy references a Kyverno policy manifest, this determines if an additional
4751
# configuration policy should be generated in order to receive policy violations in Open Cluster
4852
# Management when the Kyverno policy has been violated. This defaults to true.
@@ -138,6 +142,8 @@ policies:
138142
metadataComplianceType: ""
139143
# Optional. (See policyDefaults.evaluationInterval for description.)
140144
evaluationInterval: {}
145+
# Optional. (See policyDefaults.pruneObjectBehavior for description.)
146+
pruneObjectBehavior: ""
141147
# (Note: a path to a directory containing a Kustomize manifest is a supported alternative.) Optional. A
142148
# Kustomize patch to apply to the manifest(s) at the path. If there are multiple manifests, the patch requires
143149
# the apiVersion, kind, metadata.name, and metadata.namespace (if applicable) fields to be set so Kustomize can
@@ -174,6 +180,8 @@ policies:
174180
disabled: false
175181
# Optional. (See policyDefaults.evaluationInterval for description.)
176182
evaluationInterval: {}
183+
# Optional. (See policyDefaults.pruneObjectBehavior for description.)
184+
pruneObjectBehavior: ""
177185
# Optional. (See policyDefaults.informKyvernoPolicies for description.)
178186
informKyvernoPolicies: true
179187
# Optional. (See policyDefaults.consolidateManifests for description.)

internal/plugin.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,10 @@ func (p *Plugin) applyDefaults(unmarshaledConfig map[string]interface{}) {
458458
}
459459
}
460460

461+
if policy.PruneObjectBehavior == "" {
462+
policy.PruneObjectBehavior = p.PolicyDefaults.PruneObjectBehavior
463+
}
464+
461465
if policy.PolicySets == nil {
462466
policy.PolicySets = p.PolicyDefaults.PolicySets
463467
}
@@ -584,6 +588,10 @@ func (p *Plugin) applyDefaults(unmarshaledConfig map[string]interface{}) {
584588
manifest.EvaluationInterval.NonCompliant = policy.EvaluationInterval.NonCompliant
585589
}
586590
}
591+
592+
if manifest.PruneObjectBehavior == "" && policy.PruneObjectBehavior != "" {
593+
manifest.PruneObjectBehavior = policy.PruneObjectBehavior
594+
}
587595
}
588596

589597
for _, plcsetInPlc := range policy.PolicySets {
@@ -788,6 +796,17 @@ func (p *Plugin) assertValidConfig() error {
788796
return err
789797
}
790798

799+
// Verify that consolidated manifests don't specify fields
800+
// that can't be overridden at the objectTemplate level
801+
if policy.ConsolidateManifests && manifest.PruneObjectBehavior != "" {
802+
return fmt.Errorf(
803+
"the policy %s has the pruneObjectBehavior value set on manifest[%d] but "+
804+
"consolidateManifests is true",
805+
policy.Name,
806+
j,
807+
)
808+
}
809+
791810
evalInterval := &manifest.EvaluationInterval
792811
if policy.ConsolidateManifests && (evalInterval.Compliant != "" || evalInterval.NonCompliant != "") {
793812
return fmt.Errorf(

internal/plugin_config_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,70 @@ policies:
10081008
}
10091009
}
10101010

1011+
func TestConfigInvalidManifestKey(t *testing.T) {
1012+
t.Parallel()
1013+
tmpDir := t.TempDir()
1014+
createConfigMap(t, tmpDir, "configmap.yaml")
1015+
1016+
tests := map[string]struct {
1017+
// Individual values can't be used for compliant/noncompliant since an empty string means
1018+
// to not inherit from the policy defaults.
1019+
keyName string
1020+
defaultKey string
1021+
policyKey string
1022+
manifestKey string
1023+
expectedMsg string
1024+
}{
1025+
"pruneObjectBehavior specified in manifest": {
1026+
"pruneObjectBehavior",
1027+
"",
1028+
"",
1029+
"None",
1030+
`the policy policy-app has the pruneObjectBehavior value set` +
1031+
` on manifest[0] but consolidateManifests is true`,
1032+
},
1033+
}
1034+
1035+
for testName, test := range tests {
1036+
test := test
1037+
1038+
t.Run(
1039+
testName,
1040+
func(t *testing.T) {
1041+
t.Parallel()
1042+
config := fmt.Sprintf(`
1043+
apiVersion: policy.open-cluster-management.io/v1
1044+
kind: PolicyGenerator
1045+
metadata:
1046+
name: policy-generator-name
1047+
policyDefaults:
1048+
namespace: my-policies
1049+
%s: %s
1050+
policies:
1051+
- name: policy-app
1052+
%s: %s
1053+
manifests:
1054+
- path: %s
1055+
%s: %s
1056+
`,
1057+
test.keyName, test.defaultKey,
1058+
test.keyName, test.policyKey,
1059+
path.Join(tmpDir, "configmap.yaml"),
1060+
test.keyName, test.manifestKey,
1061+
)
1062+
1063+
p := Plugin{}
1064+
err := p.Config([]byte(config), tmpDir)
1065+
if err == nil {
1066+
t.Fatal("Expected an error but did not get one")
1067+
}
1068+
1069+
assertEqual(t, err.Error(), test.expectedMsg)
1070+
},
1071+
)
1072+
}
1073+
}
1074+
10111075
func TestConfigNoManifests(t *testing.T) {
10121076
t.Parallel()
10131077
const config = `

internal/plugin_test.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ func TestGenerate(t *testing.T) {
3939
p.PolicyDefaults.Placement.Name = "my-placement-rule"
4040
p.PolicyDefaults.Namespace = "my-policies"
4141
p.PolicyDefaults.MetadataComplianceType = "musthave"
42+
p.PolicyDefaults.PruneObjectBehavior = "DeleteAll"
4243
patch := map[string]interface{}{
4344
"metadata": map[string]interface{}{
4445
"labels": map[string]string{
@@ -47,7 +48,8 @@ func TestGenerate(t *testing.T) {
4748
},
4849
}
4950
policyConf := types.PolicyConfig{
50-
Name: "policy-app-config",
51+
Name: "policy-app-config",
52+
PruneObjectBehavior: "None",
5153
Manifests: []types.Manifest{
5254
{
5355
Path: path.Join(tmpDir, "configmap.yaml"),
@@ -107,6 +109,7 @@ spec:
107109
labels:
108110
chandler: bing
109111
name: my-configmap
112+
pruneObjectBehavior: None
110113
remediationAction: inform
111114
severity: low
112115
---
@@ -138,6 +141,7 @@ spec:
138141
kind: ConfigMap
139142
metadata:
140143
name: my-configmap
144+
pruneObjectBehavior: DeleteAll
141145
remediationAction: inform
142146
severity: low
143147
---

internal/types/types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Manifest struct {
1111
ComplianceType string `json:"complianceType,omitempty" yaml:"complianceType,omitempty"`
1212
MetadataComplianceType string `json:"metadataComplianceType,omitempty" yaml:"metadataComplianceType,omitempty"`
1313
EvaluationInterval EvaluationInterval `json:"evaluationInterval,omitempty" yaml:"evaluationInterval,omitempty"`
14+
PruneObjectBehavior string `json:"pruneObjectBehavior,omitempty" yaml:"pruneObjectBehavior,omitempty"`
1415
Patches []map[string]interface{} `json:"patches,omitempty" yaml:"patches,omitempty"`
1516
Path string `json:"path,omitempty" yaml:"path,omitempty"`
1617
}
@@ -80,6 +81,7 @@ type PolicyConfig struct {
8081
EvaluationInterval EvaluationInterval `json:"evaluationInterval,omitempty" yaml:"evaluationInterval,omitempty"`
8182
PolicyAnnotations map[string]string `json:"policyAnnotations,omitempty" yaml:"policyAnnotations,omitempty"`
8283
ConfigurationPolicyAnnotations map[string]string `json:"configurationPolicyAnnotations,omitempty" yaml:"configurationPolicyAnnotations,omitempty"`
84+
PruneObjectBehavior string `json:"pruneObjectBehavior,omitempty" yaml:"pruneObjectBehavior,omitempty"`
8385
}
8486

8587
type PolicyDefaults struct {
@@ -103,6 +105,7 @@ type PolicyDefaults struct {
103105
EvaluationInterval EvaluationInterval `json:"evaluationInterval,omitempty" yaml:"evaluationInterval,omitempty"`
104106
PolicyAnnotations map[string]string `json:"policyAnnotations,omitempty" yaml:"policyAnnotations,omitempty"`
105107
ConfigurationPolicyAnnotations map[string]string `json:"configurationPolicyAnnotations,omitempty" yaml:"configurationPolicyAnnotations,omitempty"`
108+
PruneObjectBehavior string `json:"pruneObjectBehavior,omitempty" yaml:"pruneObjectBehavior,omitempty"`
106109
}
107110

108111
type PolicySetConfig struct {

internal/utils.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ func getPolicyTemplates(policyConf *types.PolicyConfig) ([]map[string]map[string
209209
len(policyTemplates)+1,
210210
&[]map[string]interface{}{objTemplate},
211211
&policyConf.Manifests[i].EvaluationInterval,
212+
policyConf.Manifests[i].PruneObjectBehavior,
212213
)
213214
setNamespaceSelector(policyConf, policyTemplate)
214215
policyTemplates = append(policyTemplates, *policyTemplate)
@@ -225,7 +226,13 @@ func getPolicyTemplates(policyConf *types.PolicyConfig) ([]map[string]map[string
225226
// just build one policyTemplate by using the above non-empty consolidated objectTemplates
226227
// ConsolidateManifests = true or there is non-policy-type manifest
227228
if policyConf.ConsolidateManifests && len(objectTemplates) > 0 {
228-
policyTemplate := buildPolicyTemplate(policyConf, 1, &objectTemplates, &policyConf.EvaluationInterval)
229+
policyTemplate := buildPolicyTemplate(
230+
policyConf,
231+
1,
232+
&objectTemplates,
233+
&policyConf.EvaluationInterval,
234+
policyConf.PruneObjectBehavior,
235+
)
229236
setNamespaceSelector(policyConf, policyTemplate)
230237
policyTemplates = append(policyTemplates, *policyTemplate)
231238
}
@@ -300,6 +307,7 @@ func buildPolicyTemplate(
300307
policyNum int,
301308
objectTemplates *[]map[string]interface{},
302309
evaluationInterval *types.EvaluationInterval,
310+
pruneObjectBehavior string,
303311
) *map[string]map[string]interface{} {
304312
var name string
305313
if policyNum > 1 {
@@ -328,6 +336,11 @@ func buildPolicyTemplate(
328336
metadata["annotations"] = policyConf.ConfigurationPolicyAnnotations
329337
}
330338

339+
if pruneObjectBehavior != "" {
340+
configSpec := policyTemplate["objectDefinition"]["spec"].(map[string]interface{})
341+
configSpec["pruneObjectBehavior"] = policyConf.PruneObjectBehavior
342+
}
343+
331344
if evaluationInterval.Compliant != "" || evaluationInterval.NonCompliant != "" {
332345
evalInterval := map[string]interface{}{}
333346

internal/utils_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1338,7 +1338,7 @@ func TestProcessKustomizeDir(t *testing.T) {
13381338
"kustomization.yaml": `
13391339
resources:
13401340
- configmap.yaml
1341-
- https://github.com/dhaiducek/policy-generator-plugin/examples/input-kustomize/?ref=support-kustomize
1341+
- https://github.com/dhaiducek/policy-generator-plugin/examples/input-kustomize/?ref=kustomize-dir
13421342
13431343
namespace: kustomize-test
13441344
`,

0 commit comments

Comments
 (0)