mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-18 07:45:23 +00:00
Convert inventory transformer to plugin, reduce k8sdeps.
This commit is contained in:
3
go.mod
3
go.mod
@@ -27,8 +27,7 @@ require (
|
||||
github.com/spf13/cobra v0.0.2
|
||||
github.com/spf13/pflag v1.0.1
|
||||
github.com/stretchr/testify v1.3.0 // indirect
|
||||
golang.org/x/net v0.0.0-20190225153610-fe579d43d832 // indirect
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 // indirect
|
||||
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.1
|
||||
k8s.io/api v0.0.0-20180510062335-53d615ae3f44
|
||||
|
||||
7
go.sum
7
go.sum
@@ -58,16 +58,23 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190225153610-fe579d43d832 h1:2IdId8zoI92l1bUzjAOygcAOkmCe13HY1j0rqPPPzB8=
|
||||
golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d h1:bt+R27hbE7uVf7PY9S6wpNg9Xo2WRe/XQT0uGq9RQQw=
|
||||
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
|
||||
@@ -5,12 +5,9 @@
|
||||
package transformer
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/k8sdeps/transformer/inventory"
|
||||
"sigs.k8s.io/kustomize/k8sdeps/transformer/patch"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"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
|
||||
@@ -27,11 +24,3 @@ func (p *FactoryImpl) MakePatchTransformer(
|
||||
rf *resource.Factory) (transformers.Transformer, error) {
|
||||
return patch.NewTransformer(slice, rf)
|
||||
}
|
||||
|
||||
func (p *FactoryImpl) MakeInventoryTransformer(
|
||||
arg *types.Inventory,
|
||||
ldr ifc.Loader,
|
||||
namespace string,
|
||||
gp types.GarbagePolicy) transformers.Transformer {
|
||||
return inventory.NewTransformer(arg, ldr, namespace, gp)
|
||||
}
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package inventory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"sigs.k8s.io/kustomize/pkg/hasher"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/inventory"
|
||||
"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"
|
||||
)
|
||||
|
||||
// transformer compute the inventory object used in prune
|
||||
type transformer struct {
|
||||
garbagePolicy types.GarbagePolicy
|
||||
ldr ifc.Loader
|
||||
cmName string
|
||||
cmNamespace string
|
||||
}
|
||||
|
||||
var _ transformers.Transformer = &transformer{}
|
||||
|
||||
// NewTransformer makes a new inventory transformer.
|
||||
func NewTransformer(
|
||||
p *types.Inventory,
|
||||
ldr ifc.Loader,
|
||||
namespace string,
|
||||
gp types.GarbagePolicy) transformers.Transformer {
|
||||
if p == nil || p.Type != "ConfigMap" || p.ConfigMap.Namespace != namespace {
|
||||
return transformers.NewNoOpTransformer()
|
||||
}
|
||||
return &transformer{
|
||||
garbagePolicy: gp,
|
||||
ldr: ldr,
|
||||
cmName: p.ConfigMap.Name,
|
||||
cmNamespace: p.ConfigMap.Namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// Transform generates an inventory object based on the input ResMap.
|
||||
// this transformer 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
|
||||
// The inventory data is written to annotation since
|
||||
// 1. The key in data field is constrained and couldn't include arbitrary letters
|
||||
// 2. The annotation can be put into any kind of objects
|
||||
func (tf *transformer) Transform(m resmap.ResMap) error {
|
||||
invty := inventory.NewInventory()
|
||||
var keys []string
|
||||
for _, r := range m {
|
||||
ns, _ := r.GetFieldValue("metadata.namespace")
|
||||
item := resid.NewItemId(r.GetGvk(), ns, r.GetName())
|
||||
var refs []resid.ItemId
|
||||
|
||||
for _, refid := range r.GetRefBy() {
|
||||
ref := m[refid]
|
||||
ns, _ := ref.GetFieldValue("metadata.namespace")
|
||||
refs = append(refs, resid.NewItemId(ref.GetGvk(), ns, ref.GetName()))
|
||||
}
|
||||
invty.Current[item] = refs
|
||||
keys = append(keys, item.String())
|
||||
}
|
||||
h, err := hasher.SortArrayAndComputeHash(keys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := &types.ConfigMapArgs{}
|
||||
args.Name = tf.cmName
|
||||
args.Namespace = tf.cmNamespace
|
||||
opts := &types.GeneratorOptions{
|
||||
Annotations: make(map[string]string),
|
||||
}
|
||||
opts.Annotations[inventory.HashAnnotation] = h
|
||||
err = invty.UpdateAnnotations(opts.Annotations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kf := kunstruct.NewKunstructuredFactoryImpl()
|
||||
k, err := kf.MakeConfigMap(tf.ldr, opts, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if tf.garbagePolicy == types.GarbageCollect {
|
||||
for k := range m {
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
|
||||
id := resid.NewResIdWithPrefixNamespace(
|
||||
gvk.Gvk{
|
||||
Version: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
tf.cmName,
|
||||
"", tf.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
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package inventory
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/pkg/fs"
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"sigs.k8s.io/kustomize/pkg/loader"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
"sigs.k8s.io/kustomize/pkg/validators"
|
||||
)
|
||||
|
||||
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{}{
|
||||
"apiVersion": "apps/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 TestInventoryTransformer(t *testing.T) {
|
||||
rf := resource.NewFactory(
|
||||
kunstruct.NewKunstructuredFactoryImpl())
|
||||
ldr := loader.NewFileLoaderAtCwd(validators.MakeFakeValidator(), fs.MakeFakeFS())
|
||||
|
||||
// hash is derived based on all keys in the Inventory
|
||||
// It is added to annotations as
|
||||
// kustomize.config.k8s.io/InventoryHash: hash
|
||||
// When seeing the same annotation, prune binary assumes no
|
||||
// clean up is needed
|
||||
hash := "h44788gt7g"
|
||||
|
||||
// inventory is the derived json string for an Inventory object
|
||||
// It is added to annotations as
|
||||
// kustomize.config.k8s.io/Inventory: inventory
|
||||
inventory := "{\"current\":{\"apps_v1_Deployment|~X|deploy1\":null,\"~G_v1_ConfigMap|~X|cm1\":[{\"group\":\"apps\",\"version\":\"v1\",\"kind\":\"Deployment\",\"name\":\"deploy1\"}],\"~G_v1_Secret|~X|secret1\":[{\"group\":\"apps\",\"version\":\"v1\",\"kind\":\"Deployment\",\"name\":\"deploy1\"}]}}" // nolint
|
||||
|
||||
// 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{}{
|
||||
"kustomize.config.k8s.io/Inventory": inventory,
|
||||
"kustomize.config.k8s.io/InventoryHash": hash,
|
||||
},
|
||||
},
|
||||
})
|
||||
expected := resmap.ResMap{
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "pruneCM", "", "default"): pruneMap,
|
||||
}
|
||||
|
||||
p := &types.Inventory{
|
||||
Type: "ConfigMap",
|
||||
ConfigMap: types.NameArgs{
|
||||
Name: "pruneCM",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
objs := makeResMap()
|
||||
|
||||
// include the original resmap; only return the ConfigMap for pruning
|
||||
tran := NewTransformer(p, ldr, "default", types.GarbageCollect)
|
||||
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 = NewTransformer(p, ldr, "default", types.GarbageIgnore)
|
||||
tran.Transform(objs)
|
||||
|
||||
if !reflect.DeepEqual(objs, expected) {
|
||||
err := expected.ErrorIfNotEqual(objs)
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,5 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package accumulator
|
||||
|
||||
@@ -29,7 +16,8 @@ import (
|
||||
)
|
||||
|
||||
// ResAccumulator accumulates resources and the rules
|
||||
// used to customize those resources.
|
||||
// used to customize those resources. It's a ResMap
|
||||
// plus stuff needed to modify the ResMap.
|
||||
type ResAccumulator struct {
|
||||
resMap resmap.ResMap
|
||||
tConfig *config.TransformerConfig
|
||||
|
||||
@@ -5,18 +5,13 @@
|
||||
package transformer
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/transformers"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
|
||||
// Factory makes transformers
|
||||
// Factory makes transformers that require k8sdeps.
|
||||
type Factory interface {
|
||||
MakePatchTransformer(slice []*resource.Resource, rf *resource.Factory) (transformers.Transformer, error)
|
||||
MakeInventoryTransformer(
|
||||
p *types.Inventory,
|
||||
ldr ifc.Loader,
|
||||
namespace string,
|
||||
gp types.GarbagePolicy) transformers.Transformer
|
||||
MakePatchTransformer(
|
||||
slice []*resource.Resource,
|
||||
rf *resource.Factory) (transformers.Transformer, error)
|
||||
}
|
||||
|
||||
@@ -127,14 +127,11 @@ func (kt *KustTarget) makeCustomizedResMap(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// This must be done last, and not as part of
|
||||
|
||||
// The following steps must be done last, 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)
|
||||
|
||||
err = kt.addHashesToNames(ra)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -145,22 +142,60 @@ func (kt *KustTarget) makeCustomizedResMap(
|
||||
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.MakeInventoryTransformer(
|
||||
kt.kustomization.Inventory, kt.ldr,
|
||||
kt.kustomization.Namespace,
|
||||
garbagePolicy)
|
||||
err = pt.Transform(rm)
|
||||
err = kt.computeInventory(ra, garbagePolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rm, nil
|
||||
|
||||
return ra.ResMap(), nil
|
||||
}
|
||||
|
||||
func (kt *KustTarget) addHashesToNames(
|
||||
ra *accumulator.ResAccumulator) error {
|
||||
p := builtin.NewHashTransformerPlugin()
|
||||
err := kt.configureBuiltinPlugin(p, nil, "hash")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ra.Transform(p)
|
||||
}
|
||||
|
||||
func (kt *KustTarget) computeInventory(
|
||||
ra *accumulator.ResAccumulator, garbagePolicy types.GarbagePolicy) error {
|
||||
inv := kt.kustomization.Inventory
|
||||
if inv == nil {
|
||||
return nil
|
||||
}
|
||||
if inv.Type != "ConfigMap" {
|
||||
return fmt.Errorf("don't know how to do that")
|
||||
}
|
||||
|
||||
if inv.ConfigMap.Namespace != kt.kustomization.Namespace {
|
||||
return fmt.Errorf("namespace mismatch")
|
||||
}
|
||||
|
||||
p := builtin.NewInventoryTransformerPlugin()
|
||||
var c struct {
|
||||
Policy string
|
||||
Name string
|
||||
Namespace string
|
||||
}
|
||||
c.Name = inv.ConfigMap.Name
|
||||
c.Namespace = inv.ConfigMap.Namespace
|
||||
c.Policy = garbagePolicy.String()
|
||||
|
||||
err := kt.configureBuiltinPlugin(p, c, "inventory")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ra.Transform(p)
|
||||
}
|
||||
|
||||
func (kt *KustTarget) shouldAddHashSuffixesToGeneratedResources() bool {
|
||||
|
||||
25
pkg/types/garbagepolicy_string.go
Normal file
25
pkg/types/garbagepolicy_string.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Code generated by "stringer -type=GarbagePolicy"; DO NOT EDIT.
|
||||
|
||||
package types
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[GarbageIgnore-1]
|
||||
_ = x[GarbageCollect-2]
|
||||
}
|
||||
|
||||
const _GarbagePolicy_name = "GarbageIgnoreGarbageCollect"
|
||||
|
||||
var _GarbagePolicy_index = [...]uint8{0, 13, 27}
|
||||
|
||||
func (i GarbagePolicy) String() string {
|
||||
i -= 1
|
||||
if i < 0 || i >= GarbagePolicy(len(_GarbagePolicy_index)-1) {
|
||||
return "GarbagePolicy(" + strconv.FormatInt(int64(i+1), 10) + ")"
|
||||
}
|
||||
return _GarbagePolicy_name[_GarbagePolicy_index[i]:_GarbagePolicy_index[i+1]]
|
||||
}
|
||||
@@ -146,11 +146,11 @@ type Kustomization struct {
|
||||
Inventory *Inventory `json:"inventory,omitempty" yaml:"inventory:omitempty"`
|
||||
}
|
||||
|
||||
//go:generate stringer -type=GarbagePolicy
|
||||
type GarbagePolicy int
|
||||
|
||||
const (
|
||||
GarbageUnknown GarbagePolicy = iota
|
||||
GarbageIgnore
|
||||
GarbageIgnore GarbagePolicy = iota + 1
|
||||
GarbageCollect
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package builtin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
)
|
||||
|
||||
136
plugin/builtin/InventoryTransformer.go
Normal file
136
plugin/builtin/InventoryTransformer.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Code generated by pluginator on InventoryTransformer; DO NOT EDIT.
|
||||
package builtin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/hasher"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/inventory"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type InventoryTransformerPlugin struct {
|
||||
ldr ifc.Loader
|
||||
rf *resmap.Factory
|
||||
Policy string `json:"policy,omitempty" yaml:"policy,omitempty"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
func NewInventoryTransformerPlugin() *InventoryTransformerPlugin {
|
||||
return &InventoryTransformerPlugin{}
|
||||
}
|
||||
|
||||
func (p *InventoryTransformerPlugin) Config(
|
||||
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
|
||||
p.ldr = ldr
|
||||
p.rf = rf
|
||||
err = yaml.Unmarshal(c, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p.Policy == "" {
|
||||
p.Policy = types.GarbageIgnore.String()
|
||||
}
|
||||
if p.Policy != types.GarbageCollect.String() &&
|
||||
p.Policy != types.GarbageIgnore.String() {
|
||||
return fmt.Errorf(
|
||||
"unrecognized garbagePolicy '%s'", p.Policy)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transform generates an inventory object from the input ResMap.
|
||||
// This ConfigMap supports the pruning command in
|
||||
// the client side tool proposed here:
|
||||
// https://github.com/kubernetes/enhancements/pull/810
|
||||
//
|
||||
// The inventory data is written to the ConfigMap's
|
||||
// annotations, rather than to the key-value pairs in
|
||||
// the ConfigMap's data field, since
|
||||
// 1. Keys in a ConfigMap's data field are too
|
||||
// constrained for this purpose.
|
||||
// 2. Using annotations allow any object to be used,
|
||||
// not just a ConfigMap, should some other object
|
||||
// (e.g. some App object) become more desirable
|
||||
// for this purpose.
|
||||
func (p *InventoryTransformerPlugin) Transform(m resmap.ResMap) error {
|
||||
|
||||
inv, h, err := makeInventory(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := types.ConfigMapArgs{}
|
||||
args.Name = p.Name
|
||||
args.Namespace = p.Namespace
|
||||
opts := &types.GeneratorOptions{
|
||||
Annotations: make(map[string]string),
|
||||
}
|
||||
opts.Annotations[inventory.HashAnnotation] = h
|
||||
err = inv.UpdateAnnotations(opts.Annotations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cm, err := p.rf.RF().MakeConfigMap(p.ldr, opts, &args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Policy == types.GarbageCollect.String() {
|
||||
for byeBye := range m {
|
||||
delete(m, byeBye)
|
||||
}
|
||||
}
|
||||
|
||||
id := cm.Id()
|
||||
if _, ok := m[id]; ok {
|
||||
return fmt.Errorf(
|
||||
"id '%v' already used; use a different name", id)
|
||||
}
|
||||
m[id] = cm
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeInventory(m resmap.ResMap) (
|
||||
inv *inventory.Inventory, hash string, err error) {
|
||||
inv = inventory.NewInventory()
|
||||
var keys []string
|
||||
for _, r := range m {
|
||||
ns := getNamespace(r)
|
||||
item := resid.NewItemId(r.GetGvk(), ns, r.GetName())
|
||||
if _, ok := inv.Current[item]; ok {
|
||||
return nil, "", fmt.Errorf(
|
||||
"item '%v' already in inventory", item)
|
||||
}
|
||||
inv.Current[item] = computeRefs(r, m)
|
||||
keys = append(keys, item.String())
|
||||
}
|
||||
h, err := hasher.SortArrayAndComputeHash(keys)
|
||||
return inv, h, err
|
||||
}
|
||||
|
||||
func getNamespace(r *resource.Resource) string {
|
||||
ns, err := r.GetFieldValue("metadata.namespace")
|
||||
if err != nil && !strings.Contains(err.Error(), "no field named") {
|
||||
panic(err)
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
func computeRefs(r *resource.Resource, m resmap.ResMap) (refs []resid.ItemId) {
|
||||
for _, refid := range r.GetRefBy() {
|
||||
ref := m[refid]
|
||||
ns := getNamespace(ref)
|
||||
refs = append(refs, resid.NewItemId(ref.GetGvk(), ns, ref.GetName()))
|
||||
}
|
||||
return
|
||||
}
|
||||
137
plugin/builtin/inventorytransformer/InventoryTransformer.go
Normal file
137
plugin/builtin/inventorytransformer/InventoryTransformer.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/hasher"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/inventory"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type plugin struct {
|
||||
ldr ifc.Loader
|
||||
rf *resmap.Factory
|
||||
Policy string `json:"policy,omitempty" yaml:"policy,omitempty"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
var KustomizePlugin plugin
|
||||
|
||||
func (p *plugin) Config(
|
||||
ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) {
|
||||
p.ldr = ldr
|
||||
p.rf = rf
|
||||
err = yaml.Unmarshal(c, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if p.Policy == "" {
|
||||
p.Policy = types.GarbageIgnore.String()
|
||||
}
|
||||
if p.Policy != types.GarbageCollect.String() &&
|
||||
p.Policy != types.GarbageIgnore.String() {
|
||||
return fmt.Errorf(
|
||||
"unrecognized garbagePolicy '%s'", p.Policy)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transform generates an inventory object from the input ResMap.
|
||||
// This ConfigMap supports the pruning command in
|
||||
// the client side tool proposed here:
|
||||
// https://github.com/kubernetes/enhancements/pull/810
|
||||
//
|
||||
// The inventory data is written to the ConfigMap's
|
||||
// annotations, rather than to the key-value pairs in
|
||||
// the ConfigMap's data field, since
|
||||
// 1. Keys in a ConfigMap's data field are too
|
||||
// constrained for this purpose.
|
||||
// 2. Using annotations allow any object to be used,
|
||||
// not just a ConfigMap, should some other object
|
||||
// (e.g. some App object) become more desirable
|
||||
// for this purpose.
|
||||
func (p *plugin) Transform(m resmap.ResMap) error {
|
||||
|
||||
inv, h, err := makeInventory(m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args := types.ConfigMapArgs{}
|
||||
args.Name = p.Name
|
||||
args.Namespace = p.Namespace
|
||||
opts := &types.GeneratorOptions{
|
||||
Annotations: make(map[string]string),
|
||||
}
|
||||
opts.Annotations[inventory.HashAnnotation] = h
|
||||
err = inv.UpdateAnnotations(opts.Annotations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cm, err := p.rf.RF().MakeConfigMap(p.ldr, opts, &args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Policy == types.GarbageCollect.String() {
|
||||
for byeBye := range m {
|
||||
delete(m, byeBye)
|
||||
}
|
||||
}
|
||||
|
||||
id := cm.Id()
|
||||
if _, ok := m[id]; ok {
|
||||
return fmt.Errorf(
|
||||
"id '%v' already used; use a different name", id)
|
||||
}
|
||||
m[id] = cm
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeInventory(m resmap.ResMap) (
|
||||
inv *inventory.Inventory, hash string, err error) {
|
||||
inv = inventory.NewInventory()
|
||||
var keys []string
|
||||
for _, r := range m {
|
||||
ns := getNamespace(r)
|
||||
item := resid.NewItemId(r.GetGvk(), ns, r.GetName())
|
||||
if _, ok := inv.Current[item]; ok {
|
||||
return nil, "", fmt.Errorf(
|
||||
"item '%v' already in inventory", item)
|
||||
}
|
||||
inv.Current[item] = computeRefs(r, m)
|
||||
keys = append(keys, item.String())
|
||||
}
|
||||
h, err := hasher.SortArrayAndComputeHash(keys)
|
||||
return inv, h, err
|
||||
}
|
||||
|
||||
func getNamespace(r *resource.Resource) string {
|
||||
ns, err := r.GetFieldValue("metadata.namespace")
|
||||
if err != nil && !strings.Contains(err.Error(), "no field named") {
|
||||
panic(err)
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
func computeRefs(r *resource.Resource, m resmap.ResMap) (refs []resid.ItemId) {
|
||||
for _, refid := range r.GetRefBy() {
|
||||
ref := m[refid]
|
||||
ns := getNamespace(ref)
|
||||
refs = append(refs, resid.NewItemId(ref.GetGvk(), ns, ref.GetName()))
|
||||
}
|
||||
return
|
||||
}
|
||||
125
plugin/builtin/inventorytransformer/InventoryTransformer_test.go
Normal file
125
plugin/builtin/inventorytransformer/InventoryTransformer_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
const (
|
||||
content = `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: cm1
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: secret1
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: deploy1
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
name: CM_FOO
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
key: someKey
|
||||
name: cm1
|
||||
envFrom:
|
||||
configMapRef:
|
||||
key: someKey
|
||||
name: cm1
|
||||
secretRef:
|
||||
key: someKey
|
||||
name: secret1
|
||||
image: nginx:1.7.9
|
||||
name: nginx
|
||||
`
|
||||
inv = `
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
annotations:
|
||||
kustomize.config.k8s.io/Inventory: '{"current":{"apps_v1_Deployment|~X|deploy1":null,"~G_v1_ConfigMap|~X|cm1":null,"~G_v1_Secret|~X|secret1":null}}'
|
||||
kustomize.config.k8s.io/InventoryHash: h44788gt7g
|
||||
name: pruneCM
|
||||
namespace: default
|
||||
`
|
||||
)
|
||||
|
||||
func TestInventoryTransformerCollect(t *testing.T) {
|
||||
tc := plugin.NewEnvForTest(t).Set()
|
||||
defer tc.Reset()
|
||||
|
||||
tc.BuildGoPlugin(
|
||||
"builtin", "", "InventoryTransformer")
|
||||
|
||||
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
|
||||
|
||||
rm := th.LoadAndRunTransformer(`
|
||||
apiVersion: builtin
|
||||
kind: InventoryTransformer
|
||||
metadata:
|
||||
name: notImportantHere
|
||||
policy: GarbageCollect
|
||||
name: pruneCM
|
||||
namespace: default
|
||||
`, content)
|
||||
|
||||
th.AssertActualEqualsExpected(rm, inv)
|
||||
}
|
||||
|
||||
func TestInventoryTransformerIgnore(t *testing.T) {
|
||||
tc := plugin.NewEnvForTest(t).Set()
|
||||
defer tc.Reset()
|
||||
|
||||
tc.BuildGoPlugin(
|
||||
"builtin", "", "InventoryTransformer")
|
||||
|
||||
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
|
||||
|
||||
rm := th.LoadAndRunTransformer(`
|
||||
apiVersion: builtin
|
||||
kind: InventoryTransformer
|
||||
metadata:
|
||||
name: notImportantHere
|
||||
policy: GarbageIgnore
|
||||
name: pruneCM
|
||||
namespace: default
|
||||
`, content)
|
||||
|
||||
th.AssertActualEqualsExpected(rm, inv+"---"+content)
|
||||
}
|
||||
|
||||
func TestInventoryTransformerDefaultPolicy(t *testing.T) {
|
||||
tc := plugin.NewEnvForTest(t).Set()
|
||||
defer tc.Reset()
|
||||
|
||||
tc.BuildGoPlugin(
|
||||
"builtin", "", "InventoryTransformer")
|
||||
|
||||
th := kusttest_test.NewKustTestPluginHarness(t, "/app")
|
||||
|
||||
rm := th.LoadAndRunTransformer(`
|
||||
apiVersion: builtin
|
||||
kind: InventoryTransformer
|
||||
metadata:
|
||||
name: notImportantHere
|
||||
name: pruneCM
|
||||
namespace: default
|
||||
`, content)
|
||||
|
||||
th.AssertActualEqualsExpected(rm, inv+"---"+content)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user