diff --git a/k8sdeps/transformer/factory.go b/k8sdeps/transformer/factory.go index 7388b634d..f88d2bdf4 100644 --- a/k8sdeps/transformer/factory.go +++ b/k8sdeps/transformer/factory.go @@ -20,8 +20,10 @@ package transformer import ( "sigs.k8s.io/kustomize/k8sdeps/transformer/hash" "sigs.k8s.io/kustomize/k8sdeps/transformer/patch" + "sigs.k8s.io/kustomize/k8sdeps/transformer/prune" "sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/transformers" + "sigs.k8s.io/kustomize/pkg/types" ) // FactoryImpl makes patch transformer and name hash transformer @@ -41,3 +43,7 @@ func (p *FactoryImpl) MakePatchTransformer(slice []*resource.Resource, rf *resou func (p *FactoryImpl) MakeHashTransformer() transformers.Transformer { return hash.NewNameHashTransformer() } + +func (p *FactoryImpl) MakePruneTransformer(arg *types.Prune, namespace string, append bool) transformers.Transformer { + return prune.NewPruneTransformer(arg, namespace, append) +} diff --git a/k8sdeps/transformer/hash/hash.go b/k8sdeps/transformer/hash/hash.go index 17e24ff3e..cdab7244b 100644 --- a/k8sdeps/transformer/hash/hash.go +++ b/k8sdeps/transformer/hash/hash.go @@ -20,6 +20,7 @@ import ( "crypto/sha256" "encoding/json" "fmt" + "sort" "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -86,6 +87,21 @@ func SecretHash(sec *v1.Secret) (string, error) { return h, nil } +// SortArrayAndComputeHash sorts a string array and +// returns a hash for it +func SortArrayAndComputeHash(s []string) (string, error) { + sort.Strings(s) + data, err := json.Marshal(s) + if err != nil { + return "", err + } + h, err := encodeHash(hash(string(data))) + if err != nil { + return "", err + } + return h, nil +} + // encodeConfigMap encodes a ConfigMap. // Data, Kind, and Name are taken into account. func encodeConfigMap(cm *v1.ConfigMap) (string, error) { diff --git a/k8sdeps/transformer/hash/hash_test.go b/k8sdeps/transformer/hash/hash_test.go index 2d336f35a..4a3c9365c 100644 --- a/k8sdeps/transformer/hash/hash_test.go +++ b/k8sdeps/transformer/hash/hash_test.go @@ -90,6 +90,28 @@ func TestSecretHash(t *testing.T) { } } +func TestArrayHash(t *testing.T) { + array1 := []string{"a", "b", "c"} + array2 := []string{"c", "b", "a"} + h1, err := SortArrayAndComputeHash(array1) + if err != nil { + t.Errorf("unexpected error %v", err) + } + if h1 == "" { + t.Errorf("failed to hash %v", array1) + } + h2, err := SortArrayAndComputeHash(array2) + if err != nil { + t.Errorf("unexpected error %v", err) + } + if h2 == "" { + t.Errorf("failed to hash %v", array2) + } + if h1 != h2 { + t.Errorf("hash is not consistent with reordered list: %s %s", h1, h2) + } +} + func TestEncodeConfigMap(t *testing.T) { cases := []struct { desc string diff --git a/k8sdeps/transformer/prune/prune.go b/k8sdeps/transformer/prune/prune.go new file mode 100644 index 000000000..def29ad51 --- /dev/null +++ b/k8sdeps/transformer/prune/prune.go @@ -0,0 +1,110 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package prune + +import ( + "fmt" + "sigs.k8s.io/kustomize/k8sdeps/kunstruct" + "sigs.k8s.io/kustomize/k8sdeps/transformer/hash" + "sigs.k8s.io/kustomize/pkg/gvk" + "sigs.k8s.io/kustomize/pkg/resid" + "sigs.k8s.io/kustomize/pkg/resmap" + "sigs.k8s.io/kustomize/pkg/resource" + "sigs.k8s.io/kustomize/pkg/transformers" + "sigs.k8s.io/kustomize/pkg/types" +) + +//const PruneAnnotation = "kustomize.k8s.io/PruneRevision" +const PruneAnnotation = "current" + +// pruneTransformer compute the ConfigMap used in prune +type pruneTransformer struct { + append bool + cmName string + cmNamespace string +} + +var _ transformers.Transformer = &pruneTransformer{} + +// NewPruneTransformer makes a pruneTransformer. +func NewPruneTransformer(p *types.Prune, namespace string, append bool) transformers.Transformer { + if p == nil || p.Type != "alphaConfigMap" || p.AlphaConfigMap.Namespace != namespace { + return transformers.NewNoOpTransformer() + } + return &pruneTransformer{ + append: append, + cmName: p.AlphaConfigMap.Name, + cmNamespace: p.AlphaConfigMap.Namespace, + } +} + +// Transform generates a prune ConfigMap based on the input ResMap. +// this tranformer doesn't change existing resources - +// it just visits resources and accumulates information to make a new ConfigMap. +// The prune ConfigMap is used to support the pruning command in the client side tool, +// which is proposed in https://github.com/kubernetes/enhancements/pull/810 +func (o *pruneTransformer) Transform(m resmap.ResMap) error { + keys := []string{} + for id, r := range m { + s := id.PruneString() + keys = append(keys, s) + for _, refid := range r.GetRefBy() { + keys = append(keys, s+"---"+refid.PruneString()) + } + } + h, err := hash.SortArrayAndComputeHash(keys) + if err != nil { + return err + } + + args := &types.ConfigMapArgs{} + args.Name = o.cmName + args.Namespace = o.cmNamespace + for _, key := range keys { + args.LiteralSources = append(args.LiteralSources, + key+"="+h) + } + opts := &types.GeneratorOptions{ + Annotations: make(map[string]string), + } + opts.Annotations[PruneAnnotation] = h + + kf := kunstruct.NewKunstructuredFactoryImpl() + k, err := kf.MakeConfigMap(nil, opts, args) + if err != nil { + return err + } + + if !o.append { + for k := range m { + delete(m, k) + } + } + + id := resid.NewResIdWithPrefixNamespace( + gvk.Gvk{ + Version: "v1", + Kind: "ConfigMap", + }, + o.cmName, + "", o.cmNamespace) + if _, ok := m[id]; ok { + return fmt.Errorf("id %v is already used, please use a different name in the prune field", id) + } + m[id] = resource.NewFactory(kf).FromKunstructured(k) + return nil +} diff --git a/k8sdeps/transformer/prune/prune_test.go b/k8sdeps/transformer/prune/prune_test.go new file mode 100644 index 000000000..f73d0d7c1 --- /dev/null +++ b/k8sdeps/transformer/prune/prune_test.go @@ -0,0 +1,171 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package prune + +import ( + "reflect" + "testing" + + "sigs.k8s.io/kustomize/k8sdeps/kunstruct" + "sigs.k8s.io/kustomize/pkg/gvk" + "sigs.k8s.io/kustomize/pkg/resid" + "sigs.k8s.io/kustomize/pkg/resmap" + "sigs.k8s.io/kustomize/pkg/resource" + "sigs.k8s.io/kustomize/pkg/types" +) + +var secret = gvk.Gvk{Version: "v1", Kind: "Secret"} +var cmap = gvk.Gvk{Version: "v1", Kind: "ConfigMap"} +var deploy = gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"} + +func makeResMap() resmap.ResMap { + rf := resource.NewFactory( + kunstruct.NewKunstructuredFactoryImpl()) + objs := resmap.ResMap{ + resid.NewResId(cmap, "cm1"): rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "cm1", + }, + }), + resid.NewResId(secret, "secret1"): rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "Secret", + "metadata": map[string]interface{}{ + "name": "secret1", + }, + }), + resid.NewResId(deploy, "deploy1"): rf.FromMap( + map[string]interface{}{ + "group": "apps", + "apiVersion": "v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "deploy1", + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx", + "image": "nginx:1.7.9", + "env": []interface{}{ + map[string]interface{}{ + "name": "CM_FOO", + "valueFrom": map[string]interface{}{ + "configMapKeyRef": map[string]interface{}{ + "name": "cm1", + "key": "somekey", + }, + }, + }, + }, + "envFrom": []interface{}{ + map[string]interface{}{ + "configMapRef": map[string]interface{}{ + "name": "cm1", + "key": "somekey", + }, + }, + map[string]interface{}{ + "secretRef": map[string]interface{}{ + "name": "secret1", + "key": "somekey", + }, + }, + }, + }, + }, + }, + }, + }, + }), + } + objs[resid.NewResId(cmap, "cm1")].AppendRefBy(resid.NewResId(deploy, "deploy1")) + objs[resid.NewResId(secret, "secret1")].AppendRefBy(resid.NewResId(deploy, "deploy1")) + return objs +} + +func TestPruneTransformer(t *testing.T) { + rf := resource.NewFactory( + kunstruct.NewKunstructuredFactoryImpl()) + + // hash is derived based on all keys in the ConfigMap data field. + // It is added to annotations as + // current: hash + // When seeing the same annotation, prune binary assumes no + // clean up is needed + hash := "k777d7h45b" + // This is the root or inventory object which tracks all + // the applied resources - this is the thing we expect the transformer to create. + pruneMap := rf.FromMap( + map[string]interface{}{ + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": map[string]interface{}{ + "name": "pruneCM", + "namespace": "default", + "annotations": map[string]interface{}{ + "current": hash, + }, + }, + "data": map[string]interface{}{ + "_ConfigMap__cm1": hash, + "_Secret__secret1": hash, + "apps_Deployment__deploy1": hash, + "_ConfigMap__cm1---apps_Deployment__deploy1": hash, + "_Secret__secret1---apps_Deployment__deploy1": hash, + }, + }) + expected := resmap.ResMap{ + resid.NewResIdWithPrefixNamespace(cmap, "pruneCM", "", "default"): pruneMap, + } + + p := &types.Prune{ + Type: "alphaConfigMap", + AlphaConfigMap: types.NameArgs{ + Name: "pruneCM", + Namespace: "default", + }, + } + objs := makeResMap() + + // include the original resmap; only return the ConfigMap for pruning + tran := NewPruneTransformer(p, "default", false) + tran.Transform(objs) + + if !reflect.DeepEqual(objs, expected) { + err := expected.ErrorIfNotEqual(objs) + t.Fatalf("actual doesn't match expected: %v", err) + } + + objs = makeResMap() + expected = objs.DeepCopy(rf) + expected[resid.NewResIdWithPrefixNamespace(cmap, "pruneCM", "", "default")] = pruneMap + // append the ConfigMap for pruning to the original resmap + tran = NewPruneTransformer(p, "default", true) + tran.Transform(objs) + + if !reflect.DeepEqual(objs, expected) { + err := expected.ErrorIfNotEqual(objs) + t.Fatalf("actual doesn't match expected: %v", err) + } +} diff --git a/pkg/commands/build/build.go b/pkg/commands/build/build.go index 2267f61f5..7da9ee1e3 100644 --- a/pkg/commands/build/build.go +++ b/pkg/commands/build/build.go @@ -84,6 +84,8 @@ func NewCmdBuild( &o.outputPath, "output", "o", "", "If specified, write the build output to this path.") + + cmd.AddCommand(NewCmdBuildPrune(out, fs, rf, ptf, b)) return cmd } @@ -130,3 +132,55 @@ func (o *Options) RunBuild( _, err = out.Write(res) return err } + +func (o *Options) RunBuildPrune( + out io.Writer, fSys fs.FileSystem, + rf *resmap.Factory, ptf transformer.Factory, + b bool) error { + ldr, err := loader.NewLoader(o.kustomizationPath, fSys) + if err != nil { + return err + } + defer ldr.Cleanup() + kt, err := target.NewKustTarget(ldr, rf, ptf, b) + if err != nil { + return err + } + allResources, err := kt.MakePruneConfigMap() + if err != nil { + return err + } + // Output the objects. + res, err := allResources.EncodeAsYaml() + if err != nil { + return err + } + if o.outputPath != "" { + return fSys.WriteFile(o.outputPath, res) + } + _, err = out.Write(res) + return err +} + +func NewCmdBuildPrune( + out io.Writer, fs fs.FileSystem, + rf *resmap.Factory, + ptf transformer.Factory, + b bool) *cobra.Command { + var o Options + + cmd := &cobra.Command{ + Use: "prune [path]", + Short: "Print configmap to prune previous applied objects", + Example: examples, + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + err := o.Validate(args) + if err != nil { + return err + } + return o.RunBuildPrune(out, fs, rf, ptf, b) + }, + } + return cmd +} diff --git a/pkg/commands/kustfile/kustomizationfile.go b/pkg/commands/kustfile/kustomizationfile.go index b31fc8464..1fe781995 100644 --- a/pkg/commands/kustfile/kustomizationfile.go +++ b/pkg/commands/kustfile/kustomizationfile.go @@ -69,6 +69,7 @@ func determineFieldOrder() []string { "Configurations", "Generators", "Transformers", + "Prune", } // Add deprecated fields here. diff --git a/pkg/commands/kustfile/kustomizationfile_test.go b/pkg/commands/kustfile/kustomizationfile_test.go index 20751ab74..c552cebfd 100644 --- a/pkg/commands/kustfile/kustomizationfile_test.go +++ b/pkg/commands/kustfile/kustomizationfile_test.go @@ -48,6 +48,7 @@ func TestFieldOrder(t *testing.T) { "Configurations", "Generators", "Transformers", + "Prune", } actual := determineFieldOrder() if len(expected) != len(actual) { diff --git a/pkg/ifc/transformer/factory.go b/pkg/ifc/transformer/factory.go index 0a74c2809..c9507d632 100644 --- a/pkg/ifc/transformer/factory.go +++ b/pkg/ifc/transformer/factory.go @@ -20,10 +20,12 @@ package transformer import ( "sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/transformers" + "sigs.k8s.io/kustomize/pkg/types" ) // Factory makes transformers type Factory interface { MakePatchTransformer(slice []*resource.Resource, rf *resource.Factory) (transformers.Transformer, error) MakeHashTransformer() transformers.Transformer + MakePruneTransformer(p *types.Prune, namespace string, append bool) transformers.Transformer } diff --git a/pkg/resid/resid.go b/pkg/resid/resid.go index dbf9a3e92..6c5945e3b 100644 --- a/pkg/resid/resid.go +++ b/pkg/resid/resid.go @@ -205,3 +205,13 @@ func (n ResId) prefixList() []string { func (n ResId) suffixList() []string { return strings.Split(n.suffix, ":") } + +// PruneString returns a string which can be used +// as a key in a Prune ConfigMap +func (n ResId) PruneString() string { + name := n.prefix + n.name + n.suffix + return n.gvKind.Group + + "_" + n.gvKind.Kind + + "_" + n.namespace + + "_" + name +} diff --git a/pkg/resource/resource.go b/pkg/resource/resource.go index 122fb6d6f..fca4ec69c 100644 --- a/pkg/resource/resource.go +++ b/pkg/resource/resource.go @@ -30,6 +30,7 @@ import ( type Resource struct { ifc.Kunstructured options *types.GenArgs + refBy []resid.ResId } // String returns resource as JSON. @@ -43,10 +44,16 @@ func (r *Resource) String() string { // DeepCopy returns a new copy of resource func (r *Resource) DeepCopy() *Resource { - return &Resource{ + rc := &Resource{ Kunstructured: r.Kunstructured.Copy(), options: r.options, } + if len(r.refBy) > 0 { + refby := make([]resid.ResId, len(r.refBy)) + copy(refby, r.refBy) + rc.refBy = refby + } + return rc } // Behavior returns the behavior for the resource. @@ -65,12 +72,26 @@ func (r *Resource) Id() resid.ResId { return resid.NewResIdWithPrefixNamespace(r.GetGvk(), r.GetName(), "", namespace) } +// GetRefBy returns the ResIds that referred to current resource +func (r *Resource) GetRefBy() []resid.ResId { + return r.refBy +} + +// AppendRefBy appends a ResId into the refBy list +func (r *Resource) AppendRefBy(id resid.ResId) { + r.refBy = append(r.refBy, id) +} + // Merge performs merge with other resource. func (r *Resource) Merge(other *Resource) { r.Replace(other) mergeConfigmap(r.Map(), other.Map(), r.Map()) } +func (r *Resource) PruneString() string { + return r.Id().PruneString() +} + // Replace performs replace with other resource. func (r *Resource) Replace(other *Resource) { r.SetLabels(mergeStringMaps(other.GetLabels(), r.GetLabels())) diff --git a/pkg/resource/resource_test.go b/pkg/resource/resource_test.go index b507d2633..57fd0d6ff 100644 --- a/pkg/resource/resource_test.go +++ b/pkg/resource/resource_test.go @@ -17,6 +17,7 @@ limitations under the License. package resource_test import ( + "reflect" "testing" "sigs.k8s.io/kustomize/k8sdeps/kunstruct" @@ -94,3 +95,20 @@ func TestResourceId(t *testing.T) { } } } + +func TestDeepCopy(t *testing.T) { + r := factory.FromMap( + map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "pooh", + }, + }) + r.AppendRefBy(resid.NewResId(gvk.Gvk{Group: "somegroup", Kind: "MyKind"}, "random")) + + cr := r.DeepCopy() + if !reflect.DeepEqual(r, cr) { + t.Errorf("expected %v\nbut got%v", r, cr) + } +} diff --git a/pkg/target/kusttarget.go b/pkg/target/kusttarget.go index cb5e2e3c2..2a24dd597 100644 --- a/pkg/target/kusttarget.go +++ b/pkg/target/kusttarget.go @@ -143,7 +143,47 @@ func (kt *KustTarget) MakeCustomizedResMap() (resmap.ResMap, error) { } // With all the back references fixed, it's OK to resolve Vars. err = ra.ResolveVars() - return ra.ResMap(), err + if err != nil { + return nil, err + } + + rm := ra.ResMap() + pt := kt.tFactory.MakePruneTransformer(kt.kustomization.Prune, kt.kustomization.Namespace, true) + err = pt.Transform(rm) + if err != nil { + return nil, err + } + return rm, nil +} + +func (kt *KustTarget) MakePruneConfigMap() (resmap.ResMap, error) { + ra, err := kt.AccumulateTarget() + if err != nil { + return nil, err + } + err = ra.Transform(kt.tFactory.MakeHashTransformer()) + if err != nil { + return nil, err + } + // Given that names have changed (prefixs/suffixes added), + // fix all the back references to those names. + err = ra.FixBackReferences() + if err != nil { + return nil, err + } + // With all the back references fixed, it's OK to resolve Vars. + err = ra.ResolveVars() + if err != nil { + return nil, err + } + + rm := ra.ResMap() + pt := kt.tFactory.MakePruneTransformer(kt.kustomization.Prune, kt.kustomization.Namespace, false) + err = pt.Transform(rm) + if err != nil { + return nil, err + } + return rm, nil } func (kt *KustTarget) shouldAddHashSuffixesToGeneratedResources() bool { diff --git a/pkg/target/pruneconfigmap_test.go b/pkg/target/pruneconfigmap_test.go new file mode 100644 index 000000000..339cd0089 --- /dev/null +++ b/pkg/target/pruneconfigmap_test.go @@ -0,0 +1,180 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package target_test + +import ( + "testing" +) + +func TestPruneConfigMap(t *testing.T) { + th := NewKustTestHarness(t, "/app/base") + th.writeK("/app/base", ` +resources: +- deployment.yaml +- service.yaml +- secret.yaml + +prune: + type: alphaConfigMap + alphaConfigMap: + name: haha + namespace: default + +namePrefix: my- +namespace: default +`) + th.writeF("/app/base/deployment.yaml", ` +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: mysql + labels: + app: mysql +spec: + selector: + matchLabels: + app: mysql + strategy: + type: Recreate + template: + metadata: + labels: + app: mysql + spec: + containers: + - image: mysql:5.6 + name: mysql + env: + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: pass + key: password + ports: + - containerPort: 3306 + name: mysql + volumeMounts: + - name: mysql-persistent-storage + mountPath: /var/lib/mysql + volumes: + - name: mysql-persistent-storage + emptyDir: {} +`) + th.writeF("/app/base/service.yaml", ` +apiVersion: v1 +kind: Service +metadata: + name: mmmysql + labels: + app: mysql +spec: + ports: + - port: 3306 + selector: + app: mysql +`) + th.writeF("/app/base/secret.yaml", ` +apiVersion: v1 +kind: Secret +metadata: + name: pass +type: Opaque +data: + # Default password is "admin". + password: YWRtaW4= + username: jingfang +`) + + m, err := th.makeKustTarget().MakeCustomizedResMap() + if err != nil { + t.Fatalf("Err: %v", err) + } + th.assertActualEqualsExpected(m, ` +apiVersion: v1 +data: + _Secret_default_my-pass: 54f87m6fd6 + _Secret_default_my-pass---apps_Deployment_default_my-mysql: 54f87m6fd6 + _Service_default_my-mmmysql: 54f87m6fd6 + apps_Deployment_default_my-mysql: 54f87m6fd6 +kind: ConfigMap +metadata: + annotations: + current: 54f87m6fd6 + name: haha + namespace: default +--- +apiVersion: v1 +data: + password: YWRtaW4= + username: jingfang +kind: Secret +metadata: + name: my-pass + namespace: default +type: Opaque +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: mysql + name: my-mmmysql + namespace: default +spec: + ports: + - port: 3306 + selector: + app: mysql +--- +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + labels: + app: mysql + name: my-mysql + namespace: default +spec: + selector: + matchLabels: + app: mysql + strategy: + type: Recreate + template: + metadata: + labels: + app: mysql + spec: + containers: + - env: + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + key: password + name: my-pass + image: mysql:5.6 + name: mysql + ports: + - containerPort: 3306 + name: mysql + volumeMounts: + - mountPath: /var/lib/mysql + name: mysql-persistent-storage + volumes: + - emptyDir: {} + name: mysql-persistent-storage +`) +} diff --git a/pkg/transformers/namereference.go b/pkg/transformers/namereference.go index a4e8a7f8e..8081ddeed 100644 --- a/pkg/transformers/namereference.go +++ b/pkg/transformers/namereference.go @@ -21,6 +21,7 @@ import ( "log" "sigs.k8s.io/kustomize/pkg/gvk" + "sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/transformers/config" ) @@ -70,7 +71,7 @@ func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error { m[id].Map(), fSpec.PathSlice(), fSpec.CreateIfNotPresent, o.updateNameReference( - backRef.Gvk, m.FilterBy(id))) + id, backRef.Gvk, m.FilterBy(id))) if err != nil { return err } @@ -82,7 +83,7 @@ func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error { } func (o *nameReferenceTransformer) updateNameReference( - backRef gvk.Gvk, m resmap.ResMap) func(in interface{}) (interface{}, error) { + rid resid.ResId, backRef gvk.Gvk, m resmap.ResMap) func(in interface{}) (interface{}, error) { return func(in interface{}) (interface{}, error) { switch in.(type) { case string: @@ -98,6 +99,7 @@ func (o *nameReferenceTransformer) updateNameReference( } // Return transformed name of the object, // complete with prefixes, hashes, etc. + res.AppendRefBy(rid) return res.GetName(), nil } } @@ -123,6 +125,7 @@ func (o *nameReferenceTransformer) updateNameReference( for _, index := range indexes { l[index] = res.GetName() } + res.AppendRefBy(rid) return l, nil } } diff --git a/pkg/transformers/namereference_test.go b/pkg/transformers/namereference_test.go index dd16c8980..c5535fec9 100644 --- a/pkg/transformers/namereference_test.go +++ b/pkg/transformers/namereference_test.go @@ -618,6 +618,7 @@ func TestNameReferencePersistentVolumeHappyRun(t *testing.T) { }, }), } + expected[resid.NewResId(pv, "volume1")].AppendRefBy(resid.NewResId(pvc, "claim1")) nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference) err := nrt.Transform(m) if err != nil { diff --git a/pkg/types/kustomization.go b/pkg/types/kustomization.go index 0cbbc0f42..465c96b80 100644 --- a/pkg/types/kustomization.go +++ b/pkg/types/kustomization.go @@ -136,6 +136,9 @@ type Kustomization struct { // Transformers is a list of files containing transformers Transformers []string `json:"transformers,omitempty" yaml:"transformers,omitempty"` + + // Name of the ConfigMap used in Prune + Prune *Prune `json:"prune,omitempty" yaml:"prune:omitempty"` } // DealWithMissingFields fills the missing fields @@ -289,3 +292,13 @@ type KVSource struct { Name string `json:"name,omitempty" yaml:"name,omitempty"` Args []string `json:"args,omitempty" yaml:"args,omitempty"` } + +type Prune struct { + Type string `json:"type,omitempty" yaml:"type,omitempty"` + AlphaConfigMap NameArgs `json:"alphaConfigMap,omitempty" yaml:"alphaConfigMap,omitempty"` +} + +type NameArgs struct { + Name string `json:"name,omitempty" yaml:"name,omitempty"` + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` +}