Skip to content

Commit 3e82d9f

Browse files
dhaiducekopenshift-merge-robot
authored andcommitted
Add option to wrap Gatekeeper manifests directly
When `informGatekeeperPolicies` is set to `false`, wrap Gatekeeper manifests directly in a Policy rather than in a ConfigurationPolicy. ref: https://issues.redhat.com/browse/ACM-4438 Signed-off-by: Dale Haiducek <[email protected]>
1 parent 1761673 commit 3e82d9f

File tree

5 files changed

+208
-36
lines changed

5 files changed

+208
-36
lines changed

docs/policygenerator-reference.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ policyDefaults:
8181
# Optional. Determines whether to treat the policy as compliant when it is waiting for its dependencies to reach their
8282
# desired states. Defaults to false.
8383
ignorePending: false
84+
# Deprecated: Set informGatekeeperPolicies to false to use Gatekeeper manifests directly without wrapping in a
85+
# ConfigurationPolicy.
8486
# Optional. When the policy references a Gatekeeper policy manifest, this determines if an additional configuration
8587
# policy should be generated in order to receive policy violations in Open Cluster Management when the Gatekeeper
8688
# policy has been violated. This defaults to true.
@@ -281,6 +283,8 @@ policies:
281283
pruneObjectBehavior: ""
282284
# Optional. (See policyDefaults.ignorePending for description.)
283285
ignorePending: false
286+
# Deprecated: Set informGatekeeperPolicies to false to use Gatekeeper manifests
287+
# directly without wrapping in a ConfigurationPolicy.
284288
# Optional. (See policyDefaults.informGatekeeperPolicies for description.)
285289
informGatekeeperPolicies: true
286290
# Optional. (See policyDefaults.informKyvernoPolicies for description.)

internal/plugin_config_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,8 @@ policies:
229229

230230
expectedNsSelector := types.NamespaceSelector{Exclude: nil, Include: nil}
231231

232+
assertEqual(t, p.PolicyDefaults.InformGatekeeperPolicies, true)
233+
assertEqual(t, p.PolicyDefaults.InformKyvernoPolicies, true)
232234
assertReflectEqual(t, p.PolicyDefaults.NamespaceSelector, expectedNsSelector)
233235
assertEqual(t, p.PolicyDefaults.Placement.PlacementRulePath, "")
234236
assertEqual(t, len(p.PolicyDefaults.Placement.ClusterSelectors), 0)
@@ -257,6 +259,8 @@ policies:
257259
assertEqual(t, policy.RemediationAction, "inform")
258260
assertEqual(t, policy.Severity, "low")
259261
assertReflectEqual(t, policy.Standards, []string{"NIST SP 800-53"})
262+
assertEqual(t, policy.InformGatekeeperPolicies, true)
263+
assertEqual(t, policy.InformKyvernoPolicies, true)
260264
}
261265

262266
func TestConfigNoNamespace(t *testing.T) {

internal/plugin_test.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,124 @@ spec:
11631163
assertEqual(t, output, expected)
11641164
}
11651165

1166+
func TestCreatePolicyWithGkConstraintTemplate(t *testing.T) {
1167+
t.Parallel()
1168+
tmpDir := t.TempDir()
1169+
gatekeeperPath := path.Join(tmpDir, "gatekeeper.yaml")
1170+
yamlContent := `
1171+
apiVersion: templates.gatekeeper.sh/v1
1172+
kind: ConstraintTemplate
1173+
metadata:
1174+
name: myconstrainingtemplate
1175+
`
1176+
1177+
err := os.WriteFile(gatekeeperPath, []byte(yamlContent), 0o666)
1178+
if err != nil {
1179+
t.Fatalf("Failed to write %s", gatekeeperPath)
1180+
}
1181+
1182+
p := Plugin{}
1183+
1184+
p.PolicyDefaults.Namespace = "gatekeeper-policies"
1185+
p.PolicyDefaults.InformGatekeeperPolicies = false
1186+
policyConf := types.PolicyConfig{
1187+
Name: "policy-gatekeeper",
1188+
Manifests: []types.Manifest{
1189+
{Path: path.Join(tmpDir, "gatekeeper.yaml")},
1190+
},
1191+
}
1192+
p.Policies = append(p.Policies, policyConf)
1193+
p.applyDefaults(map[string]interface{}{})
1194+
1195+
err = p.createPolicy(&p.Policies[0])
1196+
if err != nil {
1197+
t.Fatal(err.Error())
1198+
}
1199+
1200+
output := p.outputBuffer.String()
1201+
expected := `
1202+
---
1203+
apiVersion: policy.open-cluster-management.io/v1
1204+
kind: Policy
1205+
metadata:
1206+
annotations:
1207+
policy.open-cluster-management.io/categories: CM Configuration Management
1208+
policy.open-cluster-management.io/controls: CM-2 Baseline Configuration
1209+
policy.open-cluster-management.io/standards: NIST SP 800-53
1210+
name: policy-gatekeeper
1211+
namespace: gatekeeper-policies
1212+
spec:
1213+
disabled: false
1214+
policy-templates:
1215+
- objectDefinition:
1216+
apiVersion: templates.gatekeeper.sh/v1
1217+
kind: ConstraintTemplate
1218+
metadata:
1219+
name: myconstrainingtemplate
1220+
`
1221+
expected = strings.TrimPrefix(expected, "\n")
1222+
assertEqual(t, output, expected)
1223+
}
1224+
1225+
func TestCreatePolicyWithGkConstraint(t *testing.T) {
1226+
t.Parallel()
1227+
tmpDir := t.TempDir()
1228+
gatekeeperPath := path.Join(tmpDir, "gatekeeper.yaml")
1229+
yamlContent := `
1230+
apiVersion: constraints.gatekeeper.sh/v1
1231+
kind: MyConstrainingTemplate
1232+
metadata:
1233+
name: thisthingimconstraining
1234+
`
1235+
1236+
err := os.WriteFile(gatekeeperPath, []byte(yamlContent), 0o666)
1237+
if err != nil {
1238+
t.Fatalf("Failed to write %s", gatekeeperPath)
1239+
}
1240+
1241+
p := Plugin{}
1242+
1243+
p.PolicyDefaults.Namespace = "gatekeeper-policies"
1244+
p.PolicyDefaults.InformGatekeeperPolicies = false
1245+
policyConf := types.PolicyConfig{
1246+
Name: "policy-gatekeeper",
1247+
Manifests: []types.Manifest{
1248+
{Path: path.Join(tmpDir, "gatekeeper.yaml")},
1249+
},
1250+
}
1251+
p.Policies = append(p.Policies, policyConf)
1252+
p.applyDefaults(map[string]interface{}{})
1253+
1254+
err = p.createPolicy(&p.Policies[0])
1255+
if err != nil {
1256+
t.Fatal(err.Error())
1257+
}
1258+
1259+
output := p.outputBuffer.String()
1260+
expected := `
1261+
---
1262+
apiVersion: policy.open-cluster-management.io/v1
1263+
kind: Policy
1264+
metadata:
1265+
annotations:
1266+
policy.open-cluster-management.io/categories: CM Configuration Management
1267+
policy.open-cluster-management.io/controls: CM-2 Baseline Configuration
1268+
policy.open-cluster-management.io/standards: NIST SP 800-53
1269+
name: policy-gatekeeper
1270+
namespace: gatekeeper-policies
1271+
spec:
1272+
disabled: false
1273+
policy-templates:
1274+
- objectDefinition:
1275+
apiVersion: constraints.gatekeeper.sh/v1
1276+
kind: MyConstrainingTemplate
1277+
metadata:
1278+
name: thisthingimconstraining
1279+
`
1280+
expected = strings.TrimPrefix(expected, "\n")
1281+
assertEqual(t, output, expected)
1282+
}
1283+
11661284
func TestCreatePolicyWithDifferentRemediationAction(t *testing.T) {
11671285
t.Parallel()
11681286
tmpDir := t.TempDir()

internal/utils.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ func getPolicyTemplates(policyConf *types.PolicyConfig) ([]map[string]interface{
174174
extraDeps := policyConf.Manifests[i].ExtraDependencies
175175

176176
for _, manifest := range manifestGroup {
177-
isPolicyTypeManifest, err := isPolicyTypeManifest(manifest)
177+
isPolicyTypeManifest, isOcmPolicy, err := isPolicyTypeManifest(manifest)
178178
if err != nil {
179179
return nil, fmt.Errorf(
180180
"%w in manifest path: %s",
@@ -186,7 +186,10 @@ func getPolicyTemplates(policyConf *types.PolicyConfig) ([]map[string]interface{
186186
if isPolicyTypeManifest {
187187
policyTemplate := map[string]interface{}{"objectDefinition": manifest}
188188

189-
setTemplateOptions(manifest, ignorePending, extraDeps)
189+
// Only set dependency options if it's an OCM policy
190+
if isOcmPolicy {
191+
setTemplateOptions(manifest, ignorePending, extraDeps)
192+
}
190193

191194
policyTemplates = append(policyTemplates, policyTemplate)
192195

@@ -280,41 +283,49 @@ func setTemplateOptions(tmpl map[string]interface{}, ignorePending bool, extraDe
280283
}
281284
}
282285

283-
// isPolicyTypeManifest determines if the manifest is a non-root policy manifest
284-
// by checking apiVersion and kind fields.
285-
// Return error when apiVersion and kind fields aren't string, or if the manifest
286-
// is a non-root policy manifest, but it is invalid because it is missing a name.
287-
func isPolicyTypeManifest(manifest map[string]interface{}) (bool, error) {
286+
// isPolicyTypeManifest determines whether the manifest is a kind handled by the generator and
287+
// whether the manifest is a non-root OCM policy manifest by checking apiVersion and kind fields.
288+
// Return error when:
289+
// - apiVersion and kind fields can't be determined
290+
// - the manifest is a root policy manifest
291+
// - the manifest is invalid because it is missing a name
292+
func isPolicyTypeManifest(manifest map[string]interface{}) (bool, bool, error) {
288293
apiVersion, found, err := unstructured.NestedString(manifest, "apiVersion")
289294
if !found || err != nil {
290-
return false, errors.New("invalid or not found apiVersion")
295+
return false, false, errors.New("invalid or not found apiVersion")
291296
}
292297

293298
kind, found, err := unstructured.NestedString(manifest, "kind")
294299
if !found || err != nil {
295-
return false, errors.New("invalid or not found kind")
300+
return false, false, errors.New("invalid or not found kind")
296301
}
297302

298303
// Don't allow generation for root Policies
299304
isOcmAPI := strings.HasPrefix(apiVersion, "policy.open-cluster-management.io")
300305
if isOcmAPI && kind == "Policy" {
301-
return false, errors.New("providing a root Policy kind is not supported by the generator; " +
306+
return false, false, errors.New("providing a root Policy kind is not supported by the generator; " +
302307
"the manifest should be applied to the hub cluster directly")
303308
}
304309

305310
// Identify OCM Policies
306-
isPolicy := isOcmAPI && kind != "Policy" && strings.HasSuffix(kind, "Policy")
311+
isOcmPolicy := isOcmAPI && kind != "Policy" && strings.HasSuffix(kind, "Policy")
312+
313+
// Identify Gatekeeper kinds
314+
isGkConstraintTemplate := strings.HasPrefix(apiVersion, "templates.gatekeeper.sh") && kind == "ConstraintTemplate"
315+
isGkConstraint := strings.HasPrefix(apiVersion, "constraints.gatekeeper.sh")
316+
isGkObj := isGkConstraintTemplate || isGkConstraint
307317

318+
isPolicy := isOcmPolicy || isGkObj
308319

309320
if isPolicy {
310321
// metadata.name is required on policy manifests
311322
_, found, err = unstructured.NestedString(manifest, "metadata", "name")
312323
if !found || err != nil {
313-
return true, errors.New("invalid or not found metadata.name")
324+
return isPolicy, isOcmPolicy, errors.New("invalid or not found metadata.name")
314325
}
315326
}
316327

317-
return isPolicy, nil
328+
return isPolicy, isOcmPolicy, nil
318329
}
319330

320331
// setNamespaceSelector sets the namespace selector, if set, on the input policy template.

0 commit comments

Comments
 (0)