diff --git a/k8sdeps/kunstruct/factory.go b/k8sdeps/kunstruct/factory.go index a2a2b3479..1d7a06adc 100644 --- a/k8sdeps/kunstruct/factory.go +++ b/k8sdeps/kunstruct/factory.go @@ -18,13 +18,20 @@ import ( // KunstructuredFactoryImpl hides construction using apimachinery types. type KunstructuredFactoryImpl struct { + hasher *kustHash } var _ ifc.KunstructuredFactory = &KunstructuredFactoryImpl{} // NewKunstructuredFactoryImpl returns a factory. func NewKunstructuredFactoryImpl() ifc.KunstructuredFactory { - return &KunstructuredFactoryImpl{} + return &KunstructuredFactoryImpl{hasher: NewKustHash()} +} + +// Hasher returns a kunstructured hasher +// input: kunstructured; output: string hash. +func (kf *KunstructuredFactoryImpl) Hasher() ifc.KunstructuredHasher { + return kf.hasher } // SliceFromBytes returns a slice of Kunstructured. diff --git a/k8sdeps/transformer/hash/kusthash.go b/k8sdeps/kunstruct/hasher.go similarity index 93% rename from k8sdeps/transformer/hash/kusthash.go rename to k8sdeps/kunstruct/hasher.go index c755698d6..0d708023e 100644 --- a/k8sdeps/transformer/hash/kusthash.go +++ b/k8sdeps/kunstruct/hasher.go @@ -1,7 +1,7 @@ // Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -package hash +package kunstruct import ( "encoding/json" @@ -10,6 +10,7 @@ import ( "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "sigs.k8s.io/kustomize/pkg/hasher" + "sigs.k8s.io/kustomize/pkg/ifc" ) // kustHash computes a hash of an unstructured object. @@ -21,9 +22,9 @@ func NewKustHash() *kustHash { } // Hash returns a hash of either a ConfigMap or a Secret -func (h *kustHash) Hash(m map[string]interface{}) (string, error) { +func (h *kustHash) Hash(m ifc.Kunstructured) (string, error) { u := unstructured.Unstructured{ - Object: m, + Object: m.Map(), } kind := u.GetKind() switch kind { @@ -42,7 +43,8 @@ func (h *kustHash) Hash(m map[string]interface{}) (string, error) { return secretHash(sec) default: return "", fmt.Errorf( - "type %s is not supported for hashing in %v", kind, m) + "type %s is not supported for hashing in %v", + kind, m.Map()) } } diff --git a/k8sdeps/transformer/hash/kusthash_test.go b/k8sdeps/kunstruct/hasher_test.go similarity index 99% rename from k8sdeps/transformer/hash/kusthash_test.go rename to k8sdeps/kunstruct/hasher_test.go index 006de9f8d..e0981bfca 100644 --- a/k8sdeps/transformer/hash/kusthash_test.go +++ b/k8sdeps/kunstruct/hasher_test.go @@ -1,7 +1,7 @@ // Copyright 2019 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0 -package hash +package kunstruct import ( "reflect" diff --git a/k8sdeps/transformer/factory.go b/k8sdeps/transformer/factory.go index 5a6a0b8d3..75020282c 100644 --- a/k8sdeps/transformer/factory.go +++ b/k8sdeps/transformer/factory.go @@ -5,7 +5,6 @@ package transformer import ( - "sigs.k8s.io/kustomize/k8sdeps/transformer/hash" "sigs.k8s.io/kustomize/k8sdeps/transformer/inventory" "sigs.k8s.io/kustomize/k8sdeps/transformer/patch" "sigs.k8s.io/kustomize/pkg/ifc" @@ -29,11 +28,6 @@ func (p *FactoryImpl) MakePatchTransformer( return patch.NewTransformer(slice, rf) } -// MakeHashTransformer makes a new name hash transformer -func (p *FactoryImpl) MakeHashTransformer() transformers.Transformer { - return hash.NewTransformer() -} - func (p *FactoryImpl) MakeInventoryTransformer( arg *types.Inventory, ldr ifc.Loader, diff --git a/k8sdeps/transformer/hash/transformer.go b/k8sdeps/transformer/hash/transformer.go deleted file mode 100644 index 3e0e3123a..000000000 --- a/k8sdeps/transformer/hash/transformer.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package hash - -import ( - "fmt" - - "sigs.k8s.io/kustomize/pkg/resmap" - "sigs.k8s.io/kustomize/pkg/transformers" -) - -type transformer struct{} - -var _ transformers.Transformer = &transformer{} - -// NewTransformer make a hash transformer. -func NewTransformer() transformers.Transformer { - return &transformer{} -} - -// Transform appends hash to generated resources. -func (tf *transformer) Transform(m resmap.ResMap) error { - for _, res := range m { - if res.NeedHashSuffix() { - h, err := NewKustHash().Hash(res.Map()) - if err != nil { - return err - } - res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h)) - } - } - return nil -} diff --git a/k8sdeps/transformer/hash/transformer_test.go b/k8sdeps/transformer/hash/transformer_test.go deleted file mode 100644 index cf182671e..000000000 --- a/k8sdeps/transformer/hash/transformer_test.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2019 The Kubernetes Authors. -// SPDX-License-Identifier: Apache-2.0 - -package hash - -import ( - "reflect" - "testing" - - "sigs.k8s.io/kustomize/k8sdeps/kunstruct" - "sigs.k8s.io/kustomize/pkg/resid" - "sigs.k8s.io/kustomize/pkg/resmap" - "sigs.k8s.io/kustomize/pkg/resource" - "sigs.k8s.io/kustomize/pkg/types" -) - -func TestNameHashTransformer(t *testing.T) { - 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(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{}{ - "metadata": map[string]interface{}{ - "labels": map[string]interface{}{ - "old-label": "old-value", - }, - }, - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "name": "nginx", - "image": "nginx:1.7.9", - }, - }, - }, - }, - }, - }), - resid.NewResId(service, "svc1"): rf.FromMap( - map[string]interface{}{ - "apiVersion": "v1", - "kind": "Service", - "metadata": map[string]interface{}{ - "name": "svc1", - }, - "spec": map[string]interface{}{ - "ports": []interface{}{ - map[string]interface{}{ - "name": "port1", - "port": "12345", - }, - }, - }, - }), - resid.NewResId(secret, "secret1"): rf.FromMapAndOption( - map[string]interface{}{ - "apiVersion": "v1", - "kind": "Secret", - "metadata": map[string]interface{}{ - "name": "secret1", - }, - }, &types.GeneratorArgs{Behavior: "create"}, &types.GeneratorOptions{DisableNameSuffixHash: false}), - } - - expected := resmap.ResMap{ - resid.NewResId(cmap, "cm1"): rf.FromMap( - map[string]interface{}{ - "apiVersion": "v1", - "kind": "ConfigMap", - "metadata": map[string]interface{}{ - "name": "cm1", - }, - }), - 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{}{ - "metadata": map[string]interface{}{ - "labels": map[string]interface{}{ - "old-label": "old-value", - }, - }, - "spec": map[string]interface{}{ - "containers": []interface{}{ - map[string]interface{}{ - "name": "nginx", - "image": "nginx:1.7.9", - }, - }, - }, - }, - }, - }), - resid.NewResId(service, "svc1"): rf.FromMap( - map[string]interface{}{ - "apiVersion": "v1", - "kind": "Service", - "metadata": map[string]interface{}{ - "name": "svc1", - }, - "spec": map[string]interface{}{ - "ports": []interface{}{ - map[string]interface{}{ - "name": "port1", - "port": "12345", - }, - }, - }, - }), - resid.NewResId(secret, "secret1"): rf.FromMapAndOption( - map[string]interface{}{ - "apiVersion": "v1", - "kind": "Secret", - "metadata": map[string]interface{}{ - "name": "secret1-7kc45hd5f7", - }, - }, &types.GeneratorArgs{Behavior: "create"}, &types.GeneratorOptions{DisableNameSuffixHash: false}), - } - - tran := NewTransformer() - 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/ifc/ifc.go b/pkg/ifc/ifc.go index 637a831d1..e26059537 100644 --- a/pkg/ifc/ifc.go +++ b/pkg/ifc/ifc.go @@ -58,6 +58,7 @@ type Kunstructured interface { type KunstructuredFactory interface { SliceFromBytes([]byte) ([]Kunstructured, error) FromMap(m map[string]interface{}) Kunstructured + Hasher() KunstructuredHasher MakeConfigMap( ldr Loader, options *types.GeneratorOptions, @@ -68,5 +69,11 @@ type KunstructuredFactory interface { args *types.SecretArgs) (Kunstructured, error) } +// KunstructuredHasher returns a hash of the argument +// or an error. +type KunstructuredHasher interface { + Hash(Kunstructured) (string, error) +} + // See core.v1.SecretTypeOpaque const SecretTypeOpaque = "Opaque" diff --git a/pkg/ifc/transformer/factory.go b/pkg/ifc/transformer/factory.go index 238ebbc2d..f04d58f55 100644 --- a/pkg/ifc/transformer/factory.go +++ b/pkg/ifc/transformer/factory.go @@ -14,7 +14,6 @@ import ( // Factory makes transformers type Factory interface { MakePatchTransformer(slice []*resource.Resource, rf *resource.Factory) (transformers.Transformer, error) - MakeHashTransformer() transformers.Transformer MakeInventoryTransformer( p *types.Inventory, ldr ifc.Loader, diff --git a/pkg/resource/factory.go b/pkg/resource/factory.go index d6760a53d..1ce850472 100644 --- a/pkg/resource/factory.go +++ b/pkg/resource/factory.go @@ -37,6 +37,10 @@ func NewFactory(kf ifc.KunstructuredFactory) *Factory { return &Factory{kf: kf} } +func (rf *Factory) Hasher() ifc.KunstructuredHasher { + return rf.kf.Hasher() +} + // FromMap returns a new instance of Resource. func (rf *Factory) FromMap(m map[string]interface{}) *Resource { return &Resource{ diff --git a/pkg/target/kusttarget.go b/pkg/target/kusttarget.go index 21a285548..0cd069547 100644 --- a/pkg/target/kusttarget.go +++ b/pkg/target/kusttarget.go @@ -21,6 +21,7 @@ import ( "sigs.k8s.io/kustomize/pkg/transformers" "sigs.k8s.io/kustomize/pkg/transformers/config" "sigs.k8s.io/kustomize/pkg/types" + "sigs.k8s.io/kustomize/plugin/builtin" "sigs.k8s.io/yaml" ) @@ -126,10 +127,18 @@ func (kt *KustTarget) makeCustomizedResMap( if err != nil { return nil, err } - err = ra.Transform(kt.tFactory.MakeHashTransformer()) + // This must be done last, and not as part of + // the recursion implicit in AccumulateTarget. + p := builtin.NewHashTransformerPlugin() + err = kt.configureBuiltinPlugin(p, nil, "hash") if err != nil { return nil, err } + err = ra.Transform(p) + 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() diff --git a/pkg/target/kusttarget_configplugin.go b/pkg/target/kusttarget_configplugin.go index 915b10c8f..71dcd0454 100644 --- a/pkg/target/kusttarget_configplugin.go +++ b/pkg/target/kusttarget_configplugin.go @@ -234,10 +234,14 @@ func (kt *KustTarget) configureBuiltinImageTagTransformer( } func (kt *KustTarget) configureBuiltinPlugin( - p plugins.Configurable, c interface{}, id string) error { - y, err := yaml.Marshal(c) - if err != nil { - return errors.Wrapf(err, "builtin %s marshal", id) + p plugins.Configurable, c interface{}, id string) (err error) { + var y []byte + if c != nil { + y, err = yaml.Marshal(c) + if err != nil { + return errors.Wrapf( + err, "builtin %s marshal", id) + } } err = p.Config(kt.ldr, kt.rFactory, y) if err != nil { diff --git a/plugin/builtin/HashTransformer.go b/plugin/builtin/HashTransformer.go new file mode 100644 index 000000000..5376898aa --- /dev/null +++ b/plugin/builtin/HashTransformer.go @@ -0,0 +1,36 @@ +// Code generated by pluginator on HashTransformer; DO NOT EDIT. +package builtin + +import ( + "fmt" + "sigs.k8s.io/kustomize/pkg/ifc" + "sigs.k8s.io/kustomize/pkg/resmap" +) + +type HashTransformerPlugin struct { + hasher ifc.KunstructuredHasher +} + +func NewHashTransformerPlugin() *HashTransformerPlugin { + return &HashTransformerPlugin{} +} + +func (p *HashTransformerPlugin) Config( + ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) { + p.hasher = rf.RF().Hasher() + return nil +} + +// Transform appends hash to generated resources. +func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error { + for _, res := range m { + if res.NeedHashSuffix() { + h, err := p.hasher.Hash(res) + if err != nil { + return err + } + res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h)) + } + } + return nil +} diff --git a/plugin/builtin/hashtransformer/HashTransformer.go b/plugin/builtin/hashtransformer/HashTransformer.go new file mode 100644 index 000000000..44ce49044 --- /dev/null +++ b/plugin/builtin/hashtransformer/HashTransformer.go @@ -0,0 +1,38 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +//go:generate go run sigs.k8s.io/kustomize/plugin/pluginator +package main + +import ( + "fmt" + + "sigs.k8s.io/kustomize/pkg/ifc" + "sigs.k8s.io/kustomize/pkg/resmap" +) + +type plugin struct { + hasher ifc.KunstructuredHasher +} + +var KustomizePlugin plugin + +func (p *plugin) Config( + ldr ifc.Loader, rf *resmap.Factory, config []byte) (err error) { + p.hasher = rf.RF().Hasher() + return nil +} + +// Transform appends hash to generated resources. +func (p *plugin) Transform(m resmap.ResMap) error { + for _, res := range m { + if res.NeedHashSuffix() { + h, err := p.hasher.Hash(res) + if err != nil { + return err + } + res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h)) + } + } + return nil +} diff --git a/plugin/builtin/hashtransformer/HashTransformer_test.go b/plugin/builtin/hashtransformer/HashTransformer_test.go new file mode 100644 index 000000000..fd209e7fb --- /dev/null +++ b/plugin/builtin/hashtransformer/HashTransformer_test.go @@ -0,0 +1,87 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package main_test + +import ( + "testing" + + "sigs.k8s.io/kustomize/pkg/kusttest" + "sigs.k8s.io/kustomize/plugin" +) + +func TestHashTransformer(t *testing.T) { + tc := plugin.NewEnvForTest(t).Set() + defer tc.Reset() + + tc.BuildGoPlugin( + "builtin", "", "HashTransformer") + + th := kusttest_test.NewKustTestPluginHarness(t, "/app") + + rm := th.LoadAndRunTransformer(` +apiVersion: builtin +kind: HashTransformer +metadata: + name: myMapGen +name: myMap +envs: +- devops.env +- uxteam.env +literals: +- FRUIT=apple +- VEGETABLE=carrot +`, ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 +--- +apiVersion: v1 +kind: Secret +metadata: + name: secret1 +--- +apiVersion: v1 +group: apps +kind: Deployment +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-lab: old-val + spec: + containers: + - name: ngnix + image: nginx:1.7.9 +`) + + th.AssertActualEqualsExpected(rm, ` +apiVersion: v1 +kind: ConfigMap +metadata: + name: cm1 +--- +apiVersion: v1 +kind: Secret +metadata: + name: secret1 +--- +apiVersion: v1 +group: apps +kind: Deployment +metadata: + name: deploy1 +spec: + template: + metadata: + labels: + old-lab: old-val + spec: + containers: + - image: nginx:1.7.9 + name: ngnix +`) +}