From 77b5a4a21594bbe7598ea223987d4e6650e11896 Mon Sep 17 00:00:00 2001 From: Phillip Wittrock Date: Fri, 15 May 2020 12:30:59 -0700 Subject: [PATCH] kustomize transformers to create fields if they are null --- api/filters/annotations/annotations.go | 1 + api/filters/annotations/annotations_test.go | 24 +++++++++++++ api/filters/fsslice/fieldspec_filter.go | 15 ++++++++ api/filters/fsslice/fsslice.go | 4 +++ api/filters/labels/labels.go | 1 + api/filters/labels/labels_test.go | 29 +++++++++++++++ api/filters/namespace/namespace.go | 1 + api/filters/namespace/namespace_test.go | 31 ++++++++++++++++ api/filters/prefixsuffix/prefixsuffix.go | 1 + api/filters/replicacount/replicacount.go | 1 + api/filters/replicacount/replicacount_test.go | 35 +++++++++++++++++++ 11 files changed, 143 insertions(+) diff --git a/api/filters/annotations/annotations.go b/api/filters/annotations/annotations.go index 8e8b42f1b..7e4600103 100644 --- a/api/filters/annotations/annotations.go +++ b/api/filters/annotations/annotations.go @@ -32,6 +32,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { FsSlice: f.FsSlice, SetValue: fsslice.SetEntry(k, f.Annotations[k], yaml.StringTag), CreateKind: yaml.MappingNode, // Annotations are MappingNodes. + CreateTag: "!!map", }); err != nil { return nil, err } diff --git a/api/filters/annotations/annotations_test.go b/api/filters/annotations/annotations_test.go index 3f4fb9562..b69e0ddc6 100644 --- a/api/filters/annotations/annotations_test.go +++ b/api/filters/annotations/annotations_test.go @@ -186,6 +186,30 @@ metadata: "f": "true1", }}, }, + + // test quoting of values which are not considered strings in yaml 1.1 + "null_annotations": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: null +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + annotations: + a: a1 + b: b1 +`, + filter: Filter{Annotations: annoMap{ + "a": "a1", + "b": "b1", + }}, + }, } for tn, tc := range testCases { diff --git a/api/filters/fsslice/fieldspec_filter.go b/api/filters/fsslice/fieldspec_filter.go index 4d2e3c4c1..ca5b0a13a 100644 --- a/api/filters/fsslice/fieldspec_filter.go +++ b/api/filters/fsslice/fieldspec_filter.go @@ -23,6 +23,8 @@ type fieldSpecFilter struct { // CreateKind defines the type of node to create if the field is not found CreateKind yaml.Kind + CreateTag string + // path keeps internal state about the current path path []string } @@ -63,6 +65,8 @@ func (fltr fieldSpecFilter) field(obj *yaml.RNode) error { // lookup the field matching the next path element var lookupField yaml.Filter + var kind yaml.Kind + var tag string switch { case !fltr.FieldSpec.CreateIfNotPresent || fltr.CreateKind == 0 || isSeq: // dont' create the field if we don't find it @@ -70,9 +74,13 @@ func (fltr fieldSpecFilter) field(obj *yaml.RNode) error { case len(fltr.path) <= 1: // create the field if it is missing: use the provided node kind lookupField = yaml.LookupCreate(fltr.CreateKind, fieldName) + kind = fltr.CreateKind + tag = fltr.CreateTag default: // create the field if it is missing: must be a mapping node lookupField = yaml.LookupCreate(yaml.MappingNode, fieldName) + kind = yaml.MappingNode + tag = "!!map" } // locate (or maybe create) the field @@ -81,6 +89,13 @@ func (fltr fieldSpecFilter) field(obj *yaml.RNode) error { return errors.WrapPrefixf(err, "fieldName: %s", fieldName) } + // if the value exists, but is null, then change it to the creation type + // TODO: update yaml.LookupCreate to support this + if field.YNode().Tag == "!!null" { + field.YNode().Kind = kind + field.YNode().Tag = tag + } + // copy the current fltr and change the path on the copy var next = fltr // call filter for the next path element on the matching field diff --git a/api/filters/fsslice/fsslice.go b/api/filters/fsslice/fsslice.go index e783c2860..2a1044cc5 100644 --- a/api/filters/fsslice/fsslice.go +++ b/api/filters/fsslice/fsslice.go @@ -49,6 +49,9 @@ type Filter struct { // CreateKind is used to create fields that do not exist CreateKind yaml.Kind + + // CreateTag is used to set the tag if encountering a null field + CreateTag string } func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) { @@ -60,6 +63,7 @@ func (fltr Filter) Filter(obj *yaml.RNode) (*yaml.RNode, error) { FieldSpec: fltr.FsSlice[i], SetValue: fltr.SetValue, CreateKind: fltr.CreateKind, + CreateTag: fltr.CreateTag, }).Filter(obj) if err != nil { return nil, err diff --git a/api/filters/labels/labels.go b/api/filters/labels/labels.go index 3e3e148ac..c3f2bf725 100644 --- a/api/filters/labels/labels.go +++ b/api/filters/labels/labels.go @@ -33,6 +33,7 @@ func (f Filter) Filter(nodes []*yaml.RNode) ([]*yaml.RNode, error) { FsSlice: f.FsSlice, SetValue: fsslice.SetEntry(k, f.Labels[k], yaml.StringTag), CreateKind: yaml.MappingNode, // Labels are MappingNodes. + CreateTag: "!!map", }); err != nil { return nil, err } diff --git a/api/filters/labels/labels_test.go b/api/filters/labels/labels_test.go index 46ffd3233..25b910c85 100644 --- a/api/filters/labels/labels_test.go +++ b/api/filters/labels/labels_test.go @@ -370,6 +370,35 @@ metadata: }, }, }, + + "null_labels": { + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: null +`, + expectedOutput: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + labels: + a: a1 +`, + filter: Filter{ + Labels: labelMap{ + "a": "a1", + }, + FsSlice: []types.FieldSpec{ + { + Path: "metadata/labels", + CreateIfNotPresent: true, + }, + }, + }, + }, } for tn, tc := range testCases { diff --git a/api/filters/namespace/namespace.go b/api/filters/namespace/namespace.go index e9c928ab5..16f588454 100644 --- a/api/filters/namespace/namespace.go +++ b/api/filters/namespace/namespace.go @@ -44,6 +44,7 @@ func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) { FsSlice: ns.FsSlice, SetValue: fsslice.SetScalar(ns.Namespace), CreateKind: yaml.ScalarNode, // Namespace is a ScalarNode + CreateTag: yaml.StringTag, }) return node, err } diff --git a/api/filters/namespace/namespace_test.go b/api/filters/namespace/namespace_test.go index f3d1613cc..74bba6f6d 100644 --- a/api/filters/namespace/namespace_test.go +++ b/api/filters/namespace/namespace_test.go @@ -44,6 +44,37 @@ metadata: filter: namespace.Filter{Namespace: "foo"}, }, + { + name: "null_ns", + input: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + namespace: null +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: null +`, + expected: ` +apiVersion: example.com/v1 +kind: Foo +metadata: + name: instance + namespace: foo +--- +apiVersion: example.com/v1 +kind: Bar +metadata: + name: instance + namespace: foo +`, + filter: namespace.Filter{Namespace: "foo"}, + }, + { name: "add-recurse", input: ` diff --git a/api/filters/prefixsuffix/prefixsuffix.go b/api/filters/prefixsuffix/prefixsuffix.go index f461831fd..ae3e277e6 100644 --- a/api/filters/prefixsuffix/prefixsuffix.go +++ b/api/filters/prefixsuffix/prefixsuffix.go @@ -33,6 +33,7 @@ func (ns Filter) run(node *yaml.RNode) (*yaml.RNode, error) { FsSlice: ns.FsSlice, SetValue: ns.set, CreateKind: yaml.ScalarNode, // Name is a ScalarNode + CreateTag: yaml.StringTag, }) return node, err } diff --git a/api/filters/replicacount/replicacount.go b/api/filters/replicacount/replicacount.go index 8f646b012..12d059529 100644 --- a/api/filters/replicacount/replicacount.go +++ b/api/filters/replicacount/replicacount.go @@ -39,6 +39,7 @@ func (rc Filter) run(node *yaml.RNode) (*yaml.RNode, error) { FsSlice: rc.FsSlice, SetValue: rc.set, CreateKind: yaml.ScalarNode, // replicas is a ScalarNode + CreateTag: yaml.IntTag, }) return node, err } diff --git a/api/filters/replicacount/replicacount_test.go b/api/filters/replicacount/replicacount_test.go index 70c5a8713..811fa4d30 100644 --- a/api/filters/replicacount/replicacount_test.go +++ b/api/filters/replicacount/replicacount_test.go @@ -61,6 +61,41 @@ spec: expected: ` apiVersion: custom/v1 kind: Custom +metadata: + name: cus +spec: + template: + other: something + replicas: 42 +`, + filter: Filter{ + Replica: types.Replica{ + Name: "cus", + Count: 42, + }, + }, + fsslice: types.FsSlice{ + { + Path: "spec/template/replicas", + CreateIfNotPresent: true, + }, + }, + }, + + "add_field_null": { + input: ` +apiVersion: custom/v1 +kind: Custom +metadata: + name: cus +spec: + template: + other: something + replicas: null +`, + expected: ` +apiVersion: custom/v1 +kind: Custom metadata: name: cus spec: