Skip to content

Commit b5900f8

Browse files
authored
Allow replace metadata.name and metadata.namespace for single (#23)
yaml structure manifest Signed-off-by: melserngawy <[email protected]>
1 parent 978aab2 commit b5900f8

File tree

4 files changed

+176
-3
lines changed

4 files changed

+176
-3
lines changed

docs/policygenerator-reference.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,11 @@ policies:
7676
kind: ""
7777
metadata:
7878
# Optional: Only required when there are multiple manifests in the path.
79+
# Optional: Replace metadata.name is only allowed for a single YAML structure manifest.
7980
name: ""
8081
# Optional: Only required when there are multiple manifests in the path and it's a
8182
# manifest to a namespaced resource.
83+
# Optional: Replace metadata.namespace is only allowed for a single YAML structure manifest.
8284
namespace: ""
8385
# An example modification to the manifest
8486
annotations:

internal/patches.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type manifestPatcher struct {
2020
patches []map[string]interface{}
2121
}
2222

23-
// validateManifestInfo verifes that the apiVersion, kind, metadata.name fields from a manifest
23+
// validateManifestInfo verifies that the apiVersion, kind, metadata.name fields from a manifest
2424
// are set. If at least one is not present, an error is returned based on the input error template
2525
// which accepts the field name.
2626
func validateManifestInfo(manifest map[string]interface{}, errTemplate string) error {

internal/utils.go

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ func getManifests(policyConf *types.PolicyConfig) ([]map[string]interface{}, err
2121
manifests := []map[string]interface{}{}
2222
for _, manifest := range policyConf.Manifests {
2323
manifestPaths := []string{}
24+
manifestFiles := []map[string]interface{}{}
2425
readErr := fmt.Errorf("failed to read the manifest path %s", manifest.Path)
2526
manifestPathInfo, err := os.Stat(manifest.Path)
2627
if err != nil {
@@ -47,10 +48,36 @@ func getManifests(policyConf *types.PolicyConfig) ([]map[string]interface{}, err
4748
manifestPaths = append(manifestPaths, yamlPath)
4849
}
4950
} else {
50-
manifestPaths = append(manifestPaths, manifest.Path)
51+
// Unmarshal the manifest in order to check for metadata patch replacement
52+
manifestFile, err := unmarshalManifestFile(manifest.Path)
53+
if err != nil {
54+
return nil, err
55+
}
56+
57+
if len(*manifestFile) == 0 {
58+
continue
59+
}
60+
// Allowing replace the original manifest metadata.name and/or metadata.namespace if it is a single
61+
// yaml structure in the manifest path
62+
if len(*manifestFile) == 1 && len(manifest.Patches) == 1 {
63+
if patchMetadata, ok := manifest.Patches[0]["metadata"].(map[string]interface{}); ok {
64+
if metadata, ok := (*manifestFile)[0]["metadata"].(map[string]interface{}); ok {
65+
name, ok := patchMetadata["name"].(string)
66+
if ok && name != "" {
67+
metadata["name"] = name
68+
}
69+
namespace, ok := patchMetadata["namespace"].(string)
70+
if ok && namespace != "" {
71+
metadata["namespace"] = namespace
72+
}
73+
(*manifestFile)[0]["metadata"] = metadata
74+
}
75+
}
76+
}
77+
78+
manifestFiles = append(manifestFiles, *manifestFile...)
5179
}
5280

53-
manifestFiles := []map[string]interface{}{}
5481
for _, manifestPath := range manifestPaths {
5582
manifestFile, err := unmarshalManifestFile(manifestPath)
5683
if err != nil {

internal/utils_test.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,150 @@ data:
253253
assertReflectEqual(t, annotations, map[string]interface{}{"monica": "geller"})
254254
}
255255

256+
func TestGetPolicyTemplateMetadataPatches(t *testing.T) {
257+
t.Parallel()
258+
tmpDir := t.TempDir()
259+
manifestPath := path.Join(tmpDir, "patch-configmap.yaml")
260+
manifestYAML := `
261+
---
262+
apiVersion: v1
263+
kind: configmap
264+
metadata:
265+
name: test-configmap
266+
namespace: test-namespace
267+
data:
268+
image: "quay.io/potatos1"
269+
`
270+
err := ioutil.WriteFile(manifestPath, []byte(manifestYAML), 0o666)
271+
if err != nil {
272+
t.Fatalf("Failed to write %s", manifestPath)
273+
}
274+
275+
patches := []map[string]interface{}{
276+
{
277+
"metadata": map[string]interface{}{
278+
"name": "patch-configmap",
279+
"namespace": "patch-namespace",
280+
},
281+
"data": map[string]interface{}{
282+
"image": "quay.io/potatos2",
283+
},
284+
},
285+
}
286+
287+
manifests := []types.Manifest{
288+
{Path: manifestPath, Patches: patches},
289+
}
290+
policyConf := types.PolicyConfig{
291+
Manifests: manifests,
292+
Name: "policy-app-config",
293+
}
294+
295+
policyTemplates, err := getPolicyTemplates(&policyConf)
296+
if err != nil {
297+
t.Fatalf("Failed to get the policy templates: %v ", err)
298+
}
299+
assertEqual(t, len(policyTemplates), 1)
300+
301+
policyTemplate := policyTemplates[0]
302+
objdef := policyTemplate["objectDefinition"]
303+
assertEqual(t, objdef["metadata"].(map[string]string)["name"], "policy-app-config")
304+
spec, ok := objdef["spec"].(map[string]interface{})
305+
if !ok {
306+
t.Fatal("The spec field is an invalid format")
307+
}
308+
309+
objTemplates, ok := spec["object-templates"].([]map[string]interface{})
310+
if !ok {
311+
t.Fatal("The object-templates field is an invalid format")
312+
}
313+
assertEqual(t, len(objTemplates), 1)
314+
315+
objDef, ok := objTemplates[0]["objectDefinition"].(map[string]interface{})
316+
if !ok {
317+
t.Fatal("The objectDefinition field is an invalid format")
318+
}
319+
320+
metadata, ok := objDef["metadata"].(map[string]interface{})
321+
if !ok {
322+
t.Fatal("The metadata field is an invalid format")
323+
}
324+
325+
name, ok := metadata["name"].(string)
326+
if !ok {
327+
t.Fatal("The metadata.name field is an invalid format")
328+
}
329+
assertEqual(t, name, "patch-configmap")
330+
331+
namespace, ok := metadata["namespace"].(string)
332+
if !ok {
333+
t.Fatal("The metadata.namespace field is an invalid format")
334+
}
335+
assertEqual(t, namespace, "patch-namespace")
336+
337+
data, ok := objDef["data"].(map[string]interface{})
338+
if !ok {
339+
t.Fatal("The data field is an invalid format")
340+
}
341+
342+
image, ok := data["image"].(string)
343+
if !ok {
344+
t.Fatal("The data.image field is an invalid format")
345+
}
346+
assertEqual(t, image, "quay.io/potatos2")
347+
}
348+
349+
func TestGetPolicyTemplateMetadataPatchesFail(t *testing.T) {
350+
t.Parallel()
351+
tmpDir := t.TempDir()
352+
manifestPath := path.Join(tmpDir, "multi-configmaps.yaml")
353+
manifestYAML := `
354+
---
355+
apiVersion: v1
356+
kind: configmap
357+
metadata:
358+
name: test-configmap
359+
namespace: test-namespace
360+
data:
361+
image: "quay.io/potatos1"
362+
---
363+
apiVersion: v1
364+
kind: configmap
365+
metadata:
366+
name: test2-configmap
367+
namespace: test2-namespace
368+
data:
369+
image: "quay.io/potatos1"
370+
`
371+
err := ioutil.WriteFile(manifestPath, []byte(manifestYAML), 0o666)
372+
if err != nil {
373+
t.Fatalf("Failed to write %s", manifestPath)
374+
}
375+
376+
patches := []map[string]interface{}{
377+
{
378+
"metadata": map[string]interface{}{
379+
"name": "patch-configmap",
380+
"namespace": "patch-namespace",
381+
},
382+
"data": map[string]interface{}{
383+
"image": "quay.io/potatos2",
384+
},
385+
},
386+
}
387+
388+
manifests := []types.Manifest{
389+
{Path: manifestPath, Patches: patches},
390+
}
391+
policyConf := types.PolicyConfig{
392+
Manifests: manifests,
393+
Name: "policy-app-config",
394+
}
395+
396+
_, err = getPolicyTemplates(&policyConf)
397+
assertEqual(t, err != nil, true)
398+
}
399+
256400
func TestGetPolicyTemplateKyverno(t *testing.T) {
257401
t.Parallel()
258402
tmpDir := t.TempDir()

0 commit comments

Comments
 (0)