Maintain resources in order loaded.

This commit is contained in:
Jeffrey Regan
2019-06-03 11:22:53 -07:00
parent af57fc3ece
commit 4162dbc2d8
39 changed files with 1074 additions and 617 deletions

View File

@@ -44,7 +44,7 @@ func (tf *transformer) Transform(baseResourceMap resmap.ResMap) error {
} }
// Strategic merge the resources exist in both base and patches. // Strategic merge the resources exist in both base and patches.
for _, patch := range patches { for _, patch := range patches.Resources() {
// Merge patches with base resource. // Merge patches with base resource.
id := patch.Id() id := patch.Id()
matchedIds := baseResourceMap.GetMatchingIds(id.GvknEquals) matchedIds := baseResourceMap.GetMatchingIds(id.GvknEquals)
@@ -55,7 +55,7 @@ func (tf *transformer) Transform(baseResourceMap resmap.ResMap) error {
return fmt.Errorf("found multiple objects %#v targeted by patch %#v (ambiguous)", matchedIds, id) return fmt.Errorf("found multiple objects %#v targeted by patch %#v (ambiguous)", matchedIds, id)
} }
id = matchedIds[0] id = matchedIds[0]
base := baseResourceMap[id] base := baseResourceMap.GetById(id)
merged := map[string]interface{}{} merged := map[string]interface{}{}
versionedObj, err := scheme.Scheme.New(toSchemaGvk(id.Gvk())) versionedObj, err := scheme.Scheme.New(toSchemaGvk(id.Gvk()))
baseName := base.GetName() baseName := base.GetName()
@@ -97,7 +97,7 @@ func (tf *transformer) Transform(baseResourceMap resmap.ResMap) error {
return err return err
} }
} }
baseResourceMap[id].SetMap(merged) baseResourceMap.GetById(id).SetMap(merged)
base.SetName(baseName) base.SetName(baseName)
} }
return nil return nil
@@ -106,12 +106,12 @@ func (tf *transformer) Transform(baseResourceMap resmap.ResMap) error {
// mergePatches merge and index patches by Id. // mergePatches merge and index patches by Id.
// It errors out if there is conflict between patches. // It errors out if there is conflict between patches.
func (tf *transformer) mergePatches() (resmap.ResMap, error) { func (tf *transformer) mergePatches() (resmap.ResMap, error) {
rc := resmap.ResMap{} rc := resmap.New()
for ix, patch := range tf.patches { for ix, patch := range tf.patches {
id := patch.Id() id := patch.Id()
existing, found := rc[id] existing := rc.GetById(id)
if !found { if existing == nil {
rc[id] = patch rc.AppendWithId(id, patch)
continue continue
} }
@@ -146,7 +146,7 @@ func (tf *transformer) mergePatches() (resmap.ResMap, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
rc[id] = merged rc.ReplaceResource(id, merged)
} }
return rc, nil return rc, nil
} }

View File

@@ -21,7 +21,7 @@ var deploy = gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"}
var foo = gvk.Gvk{Group: "example.com", Version: "v1", Kind: "Foo"} var foo = gvk.Gvk{Group: "example.com", Version: "v1", Kind: "Foo"}
func TestOverlayRun(t *testing.T) { func TestOverlayRun(t *testing.T) {
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(deploy, "deploy1"): rf.FromMap( resid.NewResId(deploy, "deploy1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -47,7 +47,7 @@ func TestOverlayRun(t *testing.T) {
}, },
}, },
}), }),
} })
patch := []*resource.Resource{ patch := []*resource.Resource{
rf.FromMap(map[string]interface{}{ rf.FromMap(map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -81,7 +81,7 @@ func TestOverlayRun(t *testing.T) {
}, },
), ),
} }
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(deploy, "deploy1"): rf.FromMap( resid.NewResId(deploy, "deploy1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -114,7 +114,7 @@ func TestOverlayRun(t *testing.T) {
}, },
}, },
}), }),
} })
lt, err := NewTransformer(patch, rf) lt, err := NewTransformer(patch, rf)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@@ -130,7 +130,7 @@ func TestOverlayRun(t *testing.T) {
} }
func TestMultiplePatches(t *testing.T) { func TestMultiplePatches(t *testing.T) {
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(deploy, "deploy1"): rf.FromMap( resid.NewResId(deploy, "deploy1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -151,7 +151,7 @@ func TestMultiplePatches(t *testing.T) {
}, },
}, },
}), }),
} })
patch := []*resource.Resource{ patch := []*resource.Resource{
rf.FromMap(map[string]interface{}{ rf.FromMap(map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -209,7 +209,7 @@ func TestMultiplePatches(t *testing.T) {
}, },
), ),
} }
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(deploy, "deploy1"): rf.FromMap( resid.NewResId(deploy, "deploy1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -244,7 +244,7 @@ func TestMultiplePatches(t *testing.T) {
}, },
}, },
}), }),
} })
lt, err := NewTransformer(patch, rf) lt, err := NewTransformer(patch, rf)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@@ -260,7 +260,7 @@ func TestMultiplePatches(t *testing.T) {
} }
func TestMultiplePatchesWithConflict(t *testing.T) { func TestMultiplePatchesWithConflict(t *testing.T) {
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(deploy, "deploy1"): rf.FromMap( resid.NewResId(deploy, "deploy1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -281,7 +281,7 @@ func TestMultiplePatchesWithConflict(t *testing.T) {
}, },
}, },
}), }),
} })
patch := []*resource.Resource{ patch := []*resource.Resource{
rf.FromMap(map[string]interface{}{ rf.FromMap(map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -345,7 +345,7 @@ func TestMultiplePatchesWithConflict(t *testing.T) {
} }
func TestNoSchemaOverlayRun(t *testing.T) { func TestNoSchemaOverlayRun(t *testing.T) {
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(foo, "my-foo"): rf.FromMap( resid.NewResId(foo, "my-foo"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "example.com/v1", "apiVersion": "example.com/v1",
@@ -360,7 +360,7 @@ func TestNoSchemaOverlayRun(t *testing.T) {
}, },
}, },
}), }),
} })
patch := []*resource.Resource{ patch := []*resource.Resource{
rf.FromMap(map[string]interface{}{ rf.FromMap(map[string]interface{}{
"apiVersion": "example.com/v1", "apiVersion": "example.com/v1",
@@ -377,7 +377,7 @@ func TestNoSchemaOverlayRun(t *testing.T) {
}, },
), ),
} }
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(foo, "my-foo"): rf.FromMap( resid.NewResId(foo, "my-foo"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "example.com/v1", "apiVersion": "example.com/v1",
@@ -392,7 +392,7 @@ func TestNoSchemaOverlayRun(t *testing.T) {
}, },
}, },
}), }),
} })
lt, err := NewTransformer(patch, rf) lt, err := NewTransformer(patch, rf)
if err != nil { if err != nil {
@@ -408,7 +408,7 @@ func TestNoSchemaOverlayRun(t *testing.T) {
} }
func TestNoSchemaMultiplePatches(t *testing.T) { func TestNoSchemaMultiplePatches(t *testing.T) {
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(foo, "my-foo"): rf.FromMap( resid.NewResId(foo, "my-foo"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "example.com/v1", "apiVersion": "example.com/v1",
@@ -423,7 +423,7 @@ func TestNoSchemaMultiplePatches(t *testing.T) {
}, },
}, },
}), }),
} })
patch := []*resource.Resource{ patch := []*resource.Resource{
rf.FromMap(map[string]interface{}{ rf.FromMap(map[string]interface{}{
"apiVersion": "example.com/v1", "apiVersion": "example.com/v1",
@@ -457,7 +457,7 @@ func TestNoSchemaMultiplePatches(t *testing.T) {
}, },
), ),
} }
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(foo, "my-foo"): rf.FromMap( resid.NewResId(foo, "my-foo"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "example.com/v1", "apiVersion": "example.com/v1",
@@ -476,7 +476,7 @@ func TestNoSchemaMultiplePatches(t *testing.T) {
}, },
}, },
}), }),
} })
lt, err := NewTransformer(patch, rf) lt, err := NewTransformer(patch, rf)
if err != nil { if err != nil {
@@ -492,7 +492,7 @@ func TestNoSchemaMultiplePatches(t *testing.T) {
} }
func TestNoSchemaMultiplePatchesWithConflict(t *testing.T) { func TestNoSchemaMultiplePatchesWithConflict(t *testing.T) {
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(foo, "my-foo"): rf.FromMap( resid.NewResId(foo, "my-foo"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "example.com/v1", "apiVersion": "example.com/v1",
@@ -507,7 +507,7 @@ func TestNoSchemaMultiplePatchesWithConflict(t *testing.T) {
}, },
}, },
}), }),
} })
patch := []*resource.Resource{ patch := []*resource.Resource{
rf.FromMap(map[string]interface{}{ rf.FromMap(map[string]interface{}{
"apiVersion": "example.com/v1", "apiVersion": "example.com/v1",

View File

@@ -26,7 +26,7 @@ type ResAccumulator struct {
func MakeEmptyAccumulator() *ResAccumulator { func MakeEmptyAccumulator() *ResAccumulator {
ra := &ResAccumulator{} ra := &ResAccumulator{}
ra.resMap = make(resmap.ResMap) ra.resMap = resmap.New()
ra.tConfig = &config.TransformerConfig{} ra.tConfig = &config.TransformerConfig{}
ra.varSet = types.VarSet{} ra.varSet = types.VarSet{}
return ra return ra
@@ -34,11 +34,7 @@ func MakeEmptyAccumulator() *ResAccumulator {
// ResMap returns a copy of the internal resMap. // ResMap returns a copy of the internal resMap.
func (ra *ResAccumulator) ResMap() resmap.ResMap { func (ra *ResAccumulator) ResMap() resmap.ResMap {
result := make(resmap.ResMap) return ra.resMap.ShallowCopy()
for k, v := range ra.resMap {
result[k] = v
}
return result
} }
// Vars returns a copy of underlying vars. // Vars returns a copy of underlying vars.
@@ -101,7 +97,7 @@ func (ra *ResAccumulator) makeVarReplacementMap() (map[string]string, error) {
len(matched), v) len(matched), v)
} }
if len(matched) == 1 { if len(matched) == 1 {
s, err := ra.resMap[matched[0]].GetFieldValue(v.FieldRef.FieldPath) s, err := ra.resMap.GetById(matched[0]).GetFieldValue(v.FieldRef.FieldPath)
if err != nil { if err != nil {
return nil, fmt.Errorf( return nil, fmt.Errorf(
"field specified in var '%v' "+ "field specified in var '%v' "+

View File

@@ -41,7 +41,8 @@ func makeResAccumulator() (*ResAccumulator, *resource.Factory, error) {
} }
rf := resource.NewFactory( rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()) kunstruct.NewKunstructuredFactoryImpl())
ra.MergeResourcesWithErrorOnIdCollision(resmap.ResMap{ err = ra.MergeResourcesWithErrorOnIdCollision(
resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId( resid.NewResId(
gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"}, gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"},
"deploy1"): rf.FromMap( "deploy1"): rf.FromMap(
@@ -87,8 +88,8 @@ func makeResAccumulator() (*ResAccumulator, *resource.Factory, error) {
"name": "backendTwo", "name": "backendTwo",
}, },
}), }),
}) }))
return ra, rf, nil return ra, rf, err
} }
func TestResolveVarsHappy(t *testing.T) { func TestResolveVarsHappy(t *testing.T) {
@@ -172,7 +173,8 @@ func TestResolveVarsVarNeedsDisambiguation(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected err: %v", err) t.Fatalf("unexpected err: %v", err)
} }
ra.MergeResourcesWithErrorOnIdCollision(resmap.ResMap{
rm0 := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixNamespace( resid.NewResIdWithPrefixNamespace(
gvk.Gvk{Version: "v1", Kind: "Service"}, gvk.Gvk{Version: "v1", Kind: "Service"},
"backendOne", "", "fooNamespace"): rf.FromMap( "backendOne", "", "fooNamespace"): rf.FromMap(
@@ -181,10 +183,16 @@ func TestResolveVarsVarNeedsDisambiguation(t *testing.T) {
"kind": "Service", "kind": "Service",
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "backendOne", "name": "backendOne",
"namespace": "fooNamespace",
}, },
}), }),
}) })
err = ra.MergeResourcesWithErrorOnIdCollision(rm0)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
err = ra.MergeVars([]types.Var{ err = ra.MergeVars([]types.Var{
{ {
Name: "SERVICE_ONE", Name: "SERVICE_ONE",
@@ -263,7 +271,7 @@ func TestResolveVarsUnmappableVar(t *testing.T) {
} }
func find(name string, resMap resmap.ResMap) *resource.Resource { func find(name string, resMap resmap.ResMap) *resource.Resource {
for k, v := range resMap { for k, v := range resMap.AsMap() {
if k.Name() == name { if k.Name() == name {
return v return v
} }

View File

@@ -188,7 +188,7 @@ func NewCmdBuildPrune(
func writeIndividualFiles( func writeIndividualFiles(
fSys fs.FileSystem, folderPath string, m resmap.ResMap) error { fSys fs.FileSystem, folderPath string, m resmap.ResMap) error {
for _, res := range m { for _, res := range m.Resources() {
filename := filepath.Join( filename := filepath.Join(
folderPath, folderPath,
fmt.Sprintf( fmt.Sprintf(

View File

@@ -185,7 +185,7 @@ func TestNewPatchJson6902FactoryMulti(t *testing.T) {
} }
id := resid.NewResId(gvk.FromKind("foo"), "some-name") id := resid.NewResId(gvk.FromKind("foo"), "some-name")
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
id: rf.FromMap( id: rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"kind": "foo", "kind": "foo",
@@ -210,8 +210,8 @@ func TestNewPatchJson6902FactoryMulti(t *testing.T) {
}, },
}, },
}), }),
} })
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
id: rf.FromMap( id: rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"kind": "foo", "kind": "foo",
@@ -242,7 +242,7 @@ func TestNewPatchJson6902FactoryMulti(t *testing.T) {
}, },
}, },
}), }),
} })
err = tr.Transform(base) err = tr.Transform(base)
if err != nil { if err != nil {
t.Fatalf("unexpected error : %v", err) t.Fatalf("unexpected error : %v", err)
@@ -299,7 +299,7 @@ func TestNewPatchJson6902FactoryMultiConflict(t *testing.T) {
} }
id := resid.NewResId(gvk.FromKind("foo"), "some-name") id := resid.NewResId(gvk.FromKind("foo"), "some-name")
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
id: rf.FromMap( id: rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"kind": "foo", "kind": "foo",
@@ -324,7 +324,7 @@ func TestNewPatchJson6902FactoryMultiConflict(t *testing.T) {
}, },
}, },
}), }),
} })
err = tr.Transform(base) err = tr.Transform(base)
if err == nil { if err == nil {

View File

@@ -109,5 +109,5 @@ func (t *patchJson6902JSONTransformer) findTargetObj(
"found multiple targets %v matching %v for json patch", "found multiple targets %v matching %v for json patch",
matched, t.target) matched, t.target)
} }
return m[matched[0]], nil return m.GetById(matched[0]), nil
} }

View File

@@ -34,7 +34,7 @@ func TestJsonPatchJSONTransformer_Transform(t *testing.T) {
rf := resource.NewFactory( rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()) kunstruct.NewKunstructuredFactoryImpl())
id := resid.NewResId(deploy, "deploy1") id := resid.NewResId(deploy, "deploy1")
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
id: rf.FromMap( id: rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -60,7 +60,7 @@ func TestJsonPatchJSONTransformer_Transform(t *testing.T) {
}, },
}, },
}), }),
} })
operations := []byte(`[ operations := []byte(`[
{"op": "replace", "path": "/spec/template/spec/containers/0/name", "value": "my-nginx"}, {"op": "replace", "path": "/spec/template/spec/containers/0/name", "value": "my-nginx"},
@@ -68,7 +68,7 @@ func TestJsonPatchJSONTransformer_Transform(t *testing.T) {
{"op": "add", "path": "/spec/template/spec/containers/0/command", "value": ["arg1", "arg2", "arg3"]} {"op": "add", "path": "/spec/template/spec/containers/0/command", "value": ["arg1", "arg2", "arg3"]}
]`) ]`)
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
id: rf.FromMap( id: rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -100,7 +100,7 @@ func TestJsonPatchJSONTransformer_Transform(t *testing.T) {
}, },
}, },
}), }),
} })
jpt, err := newPatchJson6902JSONTransformer(id, operations) jpt, err := newPatchJson6902JSONTransformer(id, operations)
if err != nil { if err != nil {
t.Fatalf("unexpected error : %v", err) t.Fatalf("unexpected error : %v", err)
@@ -119,7 +119,7 @@ func TestJsonPatchJSONTransformer_UnHappyTransform(t *testing.T) {
rf := resource.NewFactory( rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()) kunstruct.NewKunstructuredFactoryImpl())
id := resid.NewResId(deploy, "deploy1") id := resid.NewResId(deploy, "deploy1")
base := resmap.ResMap{ base := resmap.FromMap(map[resid.ResId]*resource.Resource{
id: rf.FromMap( id: rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -145,7 +145,7 @@ func TestJsonPatchJSONTransformer_UnHappyTransform(t *testing.T) {
}, },
}, },
}), }),
} })
operations := []byte(`[ operations := []byte(`[
{"op": "add", "path": "/spec/template/spec/containers/0/command/", "value": ["arg1", "arg2", "arg3"]} {"op": "add", "path": "/spec/template/spec/containers/0/command/", "value": ["arg1", "arg2", "arg3"]}

View File

@@ -191,8 +191,8 @@ func (p *ExecPlugin) getEnv() []string {
// Returns a new copy of the given ResMap with the ResIds annotated in each Resource // Returns a new copy of the given ResMap with the ResIds annotated in each Resource
func (p *ExecPlugin) getResMapWithIdAnnotation(rm resmap.ResMap) (resmap.ResMap, error) { func (p *ExecPlugin) getResMapWithIdAnnotation(rm resmap.ResMap) (resmap.ResMap, error) {
inputRM := rm.DeepCopy(p.rf.RF()) inputRM := rm.DeepCopy()
for id, r := range inputRM { for id, r := range inputRM.AsMap() {
idString, err := yaml.Marshal(id) idString, err := yaml.Marshal(id)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -216,7 +216,7 @@ func (p *ExecPlugin) updateResMapValues(output []byte, rm resmap.ResMap) error {
if err != nil { if err != nil {
return err return err
} }
for _, r := range outputRM { for _, r := range outputRM.Resources() {
// for each emitted Resource, find the matching Resource in the original ResMap // for each emitted Resource, find the matching Resource in the original ResMap
// using its id // using its id
annotations := r.GetAnnotations() annotations := r.GetAnnotations()
@@ -230,8 +230,8 @@ func (p *ExecPlugin) updateResMapValues(output []byte, rm resmap.ResMap) error {
if err != nil { if err != nil {
return err return err
} }
res, ok := rm[id] res := rm.GetById(id)
if !ok { if res == nil {
return fmt.Errorf("unable to find id %s in resource map", id.String()) return fmt.Errorf("unable to find id %s in resource map", id.String())
} }
// remove the annotation set by Kustomize to track the resource // remove the annotation set by Kustomize to track the resource

View File

@@ -35,7 +35,7 @@ func NewLoader(
func (l *Loader) LoadGenerators( func (l *Loader) LoadGenerators(
ldr ifc.Loader, rm resmap.ResMap) ([]transformers.Generator, error) { ldr ifc.Loader, rm resmap.ResMap) ([]transformers.Generator, error) {
var result []transformers.Generator var result []transformers.Generator
for _, res := range rm { for _, res := range rm.Resources() {
g, err := l.LoadGenerator(ldr, res) g, err := l.LoadGenerator(ldr, res)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -61,7 +61,7 @@ func (l *Loader) LoadGenerator(
func (l *Loader) LoadTransformers( func (l *Loader) LoadTransformers(
ldr ifc.Loader, rm resmap.ResMap) ([]transformers.Transformer, error) { ldr ifc.Loader, rm resmap.ResMap) ([]transformers.Transformer, error) {
var result []transformers.Transformer var result []transformers.Transformer
for _, res := range rm { for _, res := range rm.Resources() {
t, err := l.LoadTransformer(ldr, res) t, err := l.LoadTransformer(ldr, res)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -17,7 +17,6 @@ limitations under the License.
package resid package resid
import ( import (
"fmt"
"strings" "strings"
"sigs.k8s.io/kustomize/pkg/gvk" "sigs.k8s.io/kustomize/pkg/gvk"
@@ -54,8 +53,10 @@ func (i ItemId) String() string {
[]string{i.Gvk.String(), ns, nm}, separator) []string{i.Gvk.String(), ns, nm}, separator)
} }
func (i ItemId) Equals(b fmt.Stringer) bool { func (i ItemId) Equals(o ItemId) bool {
return i.String() == b.String() return i.Name == o.Name &&
i.Namespace == o.Namespace &&
i.Gvk.Equals(o.Gvk)
} }
func NewItemId(g gvk.Gvk, ns, nm string) ItemId { func NewItemId(g gvk.Gvk, ns, nm string) ItemId {

View File

@@ -113,6 +113,7 @@ func (n ResId) GvknEquals(id ResId) bool {
// NsGvknEquals returns true if the other id matches // NsGvknEquals returns true if the other id matches
// namespace/Group/Version/Kind/name. // namespace/Group/Version/Kind/name.
func (n ResId) NsGvknEquals(id ResId) bool { func (n ResId) NsGvknEquals(id ResId) bool {
// TODO: same a n.ItemId.Equals(id.ItemId)
return n.ItemId.Namespace == id.ItemId.Namespace && n.GvknEquals(id) return n.ItemId.Namespace == id.ItemId.Namespace && n.GvknEquals(id)
} }

View File

@@ -1,27 +1,13 @@
/* // Copyright 2019 The Kubernetes Authors.
Copyright 2018 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0
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 resmap package resmap
import ( import (
"fmt"
"github.com/pkg/errors" "github.com/pkg/errors"
"sigs.k8s.io/kustomize/internal/kusterr" "sigs.k8s.io/kustomize/internal/kusterr"
"sigs.k8s.io/kustomize/pkg/ifc" "sigs.k8s.io/kustomize/pkg/ifc"
"sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/kustomize/pkg/types"
) )
@@ -41,11 +27,17 @@ func (rmF *Factory) RF() *resource.Factory {
return rmF.resF return rmF.resF
} }
func New() ResMap {
return newOne()
}
// FromResource returns a ResMap with one entry. // FromResource returns a ResMap with one entry.
func (rmF *Factory) FromResource(res *resource.Resource) ResMap { func (rmF *Factory) FromResource(res *resource.Resource) ResMap {
result := ResMap{} m, err := newResMapFromResourceSlice([]*resource.Resource{res})
result[res.Id()] = res if err != nil {
return result panic(err)
}
return m
} }
// FromFile returns a ResMap given a resource path. // FromFile returns a ResMap given a resource path.
@@ -55,26 +47,41 @@ func (rmF *Factory) FromFile(
if err != nil { if err != nil {
return nil, err return nil, err
} }
res, err := rmF.NewResMapFromBytes(content) m, err := rmF.NewResMapFromBytes(content)
if err != nil { if err != nil {
return nil, kusterr.Handler(err, path) return nil, kusterr.Handler(err, path)
} }
return res, nil return m, nil
} }
// newResMapFromBytes decodes a list of objects in byte array format. // NewResMapFromBytes decodes a list of objects in byte array format.
func (rmF *Factory) NewResMapFromBytes(b []byte) (ResMap, error) { func (rmF *Factory) NewResMapFromBytes(b []byte) (ResMap, error) {
resources, err := rmF.resF.SliceFromBytes(b) resources, err := rmF.resF.SliceFromBytes(b)
if err != nil { if err != nil {
return nil, err return nil, err
} }
result := ResMap{} return newResMapFromResourceSlice(resources)
for _, res := range resources { }
id := res.Id()
if _, found := result[id]; found { // Deprecated.
return result, fmt.Errorf("GroupVersionKindName: %#v already exists in the map", id) // FromMap returns a ResMap with arbitrary internal ordering,
// panicing on error. For tests only.
// See also ErrorIfNotEqual.
func FromMap(arg map[resid.ResId]*resource.Resource) ResMap {
result, err := fromMap(arg)
if err != nil {
panic(err)
}
return result
}
func fromMap(arg map[resid.ResId]*resource.Resource) (ResMap, error) {
result := New()
for id, r := range arg {
err := result.AppendWithId(id, r)
if err != nil {
return nil, err
} }
result[id] = res
} }
return result, nil return result, nil
} }
@@ -136,13 +143,12 @@ func (rmF *Factory) FromSecretArgs(
} }
func newResMapFromResourceSlice(resources []*resource.Resource) (ResMap, error) { func newResMapFromResourceSlice(resources []*resource.Resource) (ResMap, error) {
result := ResMap{} result := New()
for _, res := range resources { for _, res := range resources {
id := res.Id() err := result.Append(res)
if _, found := result[id]; found { if err != nil {
return nil, fmt.Errorf("duplicated %#v is not allowed", id) return nil, err
} }
result[id] = res
} }
return result, nil return result, nil
} }

View File

@@ -1,18 +1,5 @@
/* // Copyright 2019 The Kubernetes Authors.
Copyright 2018 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0
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 resmap_test package resmap_test
@@ -21,6 +8,8 @@ import (
"reflect" "reflect"
"testing" "testing"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/internal/loadertest" "sigs.k8s.io/kustomize/internal/loadertest"
"sigs.k8s.io/kustomize/pkg/fs" "sigs.k8s.io/kustomize/pkg/fs"
"sigs.k8s.io/kustomize/pkg/gvk" "sigs.k8s.io/kustomize/pkg/gvk"
@@ -57,7 +46,8 @@ metadata:
if ferr := l.AddFile("/whatever/project/deployment.yaml", []byte(resourceStr)); ferr != nil { if ferr := l.AddFile("/whatever/project/deployment.yaml", []byte(resourceStr)); ferr != nil {
t.Fatalf("Error adding fake file: %v\n", ferr) t.Fatalf("Error adding fake file: %v\n", ferr)
} }
expected := ResMap{resid.NewResId(deploy, "dply1"): rf.FromMap( expected := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(deploy, "dply1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
"kind": "Deployment", "kind": "Deployment",
@@ -82,11 +72,10 @@ metadata:
"namespace": "test", "namespace": "test",
}, },
}), }),
} })
m, _ := rmF.FromFile(l, "deployment.yaml") m, _ := rmF.FromFile(l, "deployment.yaml")
if len(m) != 3 { if m.Size() != 3 {
t.Fatalf("%#v should contain 3 appResource, but got %d", m, len(m)) t.Fatalf("result should contain 3, but got %d", m.Size())
} }
if err := expected.ErrorIfNotEqual(m); err != nil { if err := expected.ErrorIfNotEqual(m); err != nil {
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
@@ -104,24 +93,23 @@ kind: ConfigMap
metadata: metadata:
name: cm2 name: cm2
`) `)
expected := ResMap{ expected := New()
resid.NewResId(cmap, "cm1"): rf.FromMap( expected.Append(rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "cm1", "name": "cm1",
}, },
}), }))
resid.NewResId(cmap, "cm2"): rf.FromMap( expected.Append(rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "cm2", "name": "cm2",
}, },
}), }))
}
m, err := rmF.NewResMapFromBytes(encoded) m, err := rmF.NewResMapFromBytes(encoded)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@@ -158,7 +146,7 @@ func TestNewFromConfigMaps(t *testing.T) {
}, },
filepath: "/whatever/project/app.env", filepath: "/whatever/project/app.env",
content: "DB_USERNAME=admin\nDB_PASSWORD=somepw", content: "DB_USERNAME=admin\nDB_PASSWORD=somepw",
expected: ResMap{ expected: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "envConfigMap"): rf.FromMapAndOption( resid.NewResId(cmap, "envConfigMap"): rf.FromMapAndOption(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -171,7 +159,7 @@ func TestNewFromConfigMaps(t *testing.T) {
"DB_PASSWORD": "somepw", "DB_PASSWORD": "somepw",
}, },
}, &types.GeneratorArgs{}, nil), }, &types.GeneratorArgs{}, nil),
}, }),
}, },
{ {
@@ -187,7 +175,7 @@ func TestNewFromConfigMaps(t *testing.T) {
}, },
filepath: "/whatever/project/app-init.ini", filepath: "/whatever/project/app-init.ini",
content: "FOO=bar\nBAR=baz\n", content: "FOO=bar\nBAR=baz\n",
expected: ResMap{ expected: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "fileConfigMap"): rf.FromMapAndOption( resid.NewResId(cmap, "fileConfigMap"): rf.FromMapAndOption(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -201,7 +189,7 @@ BAR=baz
`, `,
}, },
}, &types.GeneratorArgs{}, nil), }, &types.GeneratorArgs{}, nil),
}, }),
}, },
{ {
description: "construct config map from literal", description: "construct config map from literal",
@@ -215,7 +203,7 @@ BAR=baz
}, },
}, },
}, },
expected: ResMap{ expected: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "literalConfigMap"): rf.FromMapAndOption( resid.NewResId(cmap, "literalConfigMap"): rf.FromMapAndOption(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -230,7 +218,7 @@ BAR=baz
"d": "false", "d": "false",
}, },
}, &types.GeneratorArgs{}, nil), }, &types.GeneratorArgs{}, nil),
}, }),
}, },
// TODO: add testcase for data coming from multiple sources like // TODO: add testcase for data coming from multiple sources like
@@ -275,7 +263,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
expected := ResMap{ expected := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(secret, "apple"): rf.FromMapAndOption( resid.NewResId(secret, "apple"): rf.FromMapAndOption(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -289,7 +277,7 @@ func TestNewResMapFromSecretArgs(t *testing.T) {
"DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")), "DB_PASSWORD": base64.StdEncoding.EncodeToString([]byte("somepw")),
}, },
}, &types.GeneratorArgs{}, nil), }, &types.GeneratorArgs{}, nil),
} })
if !reflect.DeepEqual(actual, expected) { if !reflect.DeepEqual(actual, expected) {
t.Fatalf("%#v\ndoesn't match expected:\n%#v", actual, expected) t.Fatalf("%#v\ndoesn't match expected:\n%#v", actual, expected)
} }

View File

@@ -1,44 +1,348 @@
/* // Copyright 2019 The Kubernetes Authors.
Copyright 2018 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License"); // Package resmap implements a map from ResId to Resource that
you may not use this file except in compliance with the License. // tracks all resources in a kustomization.
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 resmap implements a map from ResId to Resource that tracks all resources in a kustomization.
package resmap package resmap
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"reflect"
"sort" "sort"
"github.com/pkg/errors"
"sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/kustomize/pkg/types"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
) )
// ResMap is a map from ResId to Resource. // ResMap is an interface describing operations on the
type ResMap map[resid.ResId]*resource.Resource // core kustomize data structure.
//
// TODO: delete the commentary below when/if the issues
// discussed are addressed.
//
// It's a temporary(?) interface used during a refactoring
// from a bare map (map[resid.ResId]*resource.Resource) to a
// pointer to struct (currently named *resWrangler).
// Replacing it with a ptr to struct will ease click-thrus
// to implementation during development.
// OTOH, hackery in a PR might be easier to see if the
// interface is left in place.
//
// The old (bare map) ResMap had pervasive problems:
//
// * It was mutated inside loops over itself.
//
// Bugs introduced this way were hard to find since the
// bare map was recursively passed everywhere, sometimes
// mid loop.
//
// * Its keys (ResId) aren't opaque, and are effectively
// mutated (via copy and replace) for data storage reasons
// as a hack.
//
// ResId was modified a long time ago as a hack to
// store name transformation data (prefix and suffix),
// destabilizing the basic map concept and resulting
// in the need for silly ResId functions like
// NewResIdWithPrefixSuffixNamespace, NsGvknEquals,
// HasSameLeftmostPrefix, CopyWithNewPrefixSuffix, etc.
// plus logic to use them, and overly complex tests.
//
// If this data were stored in the Resource object
// (not in Kunstructured, but as a sibling to it next to
// GenArgs, references, etc.) then much code could be
// deleted and the remainder simplified.
//
// * It doesn't preserve (by definition) value order.
//
// Preserving order is now needed to support
// transformer plugins (they aren't commutative).
//
// One way to fix this is deprecate use of ResId as the
// key in favor of ItemId. See use of the resmap.Remove
// function to spot the places that need fixing to allow
// this.
type ResMap interface {
// Size reports the number of resources.
Size() int
// Resources provides a discardable slice
// of resource pointers, returned in the order
// as appended.
Resources() []*resource.Resource
// Append adds a Resource, automatically computing its
// associated Id.
// Error on Id collision.
Append(*resource.Resource) error
// AppendWithId adds a Resource with the given Id.
// Error on Id collision.
AppendWithId(resid.ResId, *resource.Resource) error
// AsMap returns ResId, *Resource pairs in
// arbitrary order via a map.
// The map is discardable, and edits to map structure
// have no impact on the ResMap.
// The Ids are copies, but the resources are pointers,
// so the resources themselves can be modified.
AsMap() map[resid.ResId]*resource.Resource
// EncodeAsYaml emits the resources as YAML in a byte slice.
// Resources are separated by `---`.
EncodeAsYaml() ([]byte, error)
// Gets the resource with the given Id, else nil.
GetById(resid.ResId) *resource.Resource
// ReplaceResource associates a new resource with
// an _existing_ Id.
// Error if Id unknown, or if some other Id points
// to the same resource object.
ReplaceResource(resid.ResId, *resource.Resource) error
// AllIds returns all known Ids.
// Result order is arbitrary.
AllIds() []resid.ResId
// GetMatchingIds returns a slice of Ids that
// satisfy the given matcher function.
// Result order is arbitrary.
GetMatchingIds(IdMatcher) []resid.ResId
// Remove removes the Id and the resource it points to.
Remove(resid.ResId) error
// ResourcesThatCouldReference returns a new ResMap with
// resources that _might_ reference the resource represented
// by the argument Id, excluding resources that should
// _never_ reference the Id. E.g., if the Id
// refers to a ConfigMap, the returned set may include a
// Deployment from the same namespace and exclude Deployments
// from other namespaces. Cluster wide objects are
// never excluded.
ResourcesThatCouldReference(resid.ResId) ResMap
// DeepCopy copies the ResMap and underlying resources.
DeepCopy() ResMap
// ShallowCopy copies the ResMap but
// not the underlying resources.
ShallowCopy() ResMap
// ErrorIfNotEqual returns an error if the
// argument doesn't have the same Ids and resource
// data as self. Ordering is _not_ taken into account,
// as this function was solely used in tests written
// before internal resource order was maintained,
// and those tests are initialized with maps which
// by definition have random ordering, and will
// fail spuriously.
// TODO: rename to ErrorIfNotEqualSets
// TODO: modify tests to not use resmap.FromMap,
// TODO: - and replace this with a stricter equals.
ErrorIfNotEqual(ResMap) error
// Debug prints the ResMap.
Debug(title string)
}
// resWrangler holds the content manipulated by kustomize.
type resWrangler struct {
// Resource list maintained in load (append) order.
// This is important for transformers, which must
// be performed in a specific order, and for users
// who for whatever reasons wish the order they
// specify in kustomizations to be maintained and
// available as an option for final YAML rendering.
rList []*resource.Resource
// A map from id to an index into rList.
// At the time of writing, the ids used as keys in
// this map cannot be assumed to match the id
// generated from the resource.Id() method pointed
// to by the map's value (via rList). These keys
// have been hacked to store prefix/suffix data.
rIndex map[resid.ResId]int
}
func newOne() *resWrangler {
result := &resWrangler{}
result.rIndex = make(map[resid.ResId]int)
return result
}
// Size implements ResMap.
func (m *resWrangler) Size() int {
if len(m.rList) != len(m.rIndex) {
panic(errors.New("class invariant violation"))
}
return len(m.rList)
}
func (m *resWrangler) indexOfResource(other *resource.Resource) int {
for i, r := range m.rList {
if r == other {
return i
}
}
return -1
}
// Resources implements ResMap.
func (m *resWrangler) Resources() []*resource.Resource {
tmp := make([]*resource.Resource, len(m.rList))
copy(tmp, m.rList)
return tmp
}
// GetById implements ResMap.
func (m *resWrangler) GetById(id resid.ResId) *resource.Resource {
if i, ok := m.rIndex[id]; ok {
return m.rList[i]
}
return nil
}
// Append implements ResMap.
func (m *resWrangler) Append(res *resource.Resource) error {
return m.AppendWithId(res.Id(), res)
}
// Remove implements ResMap.
func (m *resWrangler) Remove(adios resid.ResId) error {
tmp := newOne()
for i, r := range m.rList {
id, err := m.idMappingToIndex(i)
if err != nil {
return errors.Wrap(err, "assumption failure in remove")
}
if id != adios {
tmp.AppendWithId(id, r)
}
}
if tmp.Size() != m.Size()-1 {
return fmt.Errorf("id %s not found in removal", adios)
}
m.rIndex = tmp.rIndex
m.rList = tmp.rList
return nil
}
// AppendWithId implements ResMap.
func (m *resWrangler) AppendWithId(id resid.ResId, res *resource.Resource) error {
if already, ok := m.rIndex[id]; ok {
return fmt.Errorf(
"attempt to add res %s at id %s; that id already maps to %d",
res, id, already)
}
i := m.indexOfResource(res)
if i >= 0 {
return fmt.Errorf(
"attempt to add res %s that is already held",
res)
}
m.rList = append(m.rList, res)
m.rIndex[id] = len(m.rList) - 1
return nil
}
// ReplaceResource implements ResMap.
func (m *resWrangler) ReplaceResource(
id resid.ResId, newGuy *resource.Resource) error {
insertAt, ok := m.rIndex[id]
if !ok {
return fmt.Errorf(
"attempt to reset resource at id %s; that id not used", id)
}
existingSpot := m.indexOfResource(newGuy)
if insertAt == existingSpot {
// Be idempotent.
return nil
}
if existingSpot >= 0 {
return fmt.Errorf(
"the new resource %s is already present", newGuy.Id())
}
m.rList[insertAt] = newGuy
return nil
}
// AsMap implements ResMap.
func (m *resWrangler) AsMap() map[resid.ResId]*resource.Resource {
result := make(map[resid.ResId]*resource.Resource, m.Size())
for id, i := range m.rIndex {
result[id] = m.rList[i]
}
return result
}
// AllIds implements ResMap.
func (m *resWrangler) AllIds() (ids []resid.ResId) {
ids = make([]resid.ResId, m.Size())
i := 0
for id := range m.rIndex {
ids[i] = id
i++
}
return
}
// Debug implements ResMap.
func (m *resWrangler) Debug(title string) {
fmt.Println("--------------------------- " + title)
firstObj := true
for i, r := range m.rList {
if firstObj {
firstObj = false
} else {
fmt.Println("---")
}
fmt.Printf("# %d %s\n", i, m.debugIdMappingToIndex(i))
blob, err := yaml.Marshal(r.Map())
if err != nil {
panic(err)
}
fmt.Println(string(blob))
}
}
func (m *resWrangler) debugIdMappingToIndex(i int) string {
id, err := m.idMappingToIndex(i)
if err != nil {
return err.Error()
}
return id.String()
}
func (m *resWrangler) idMappingToIndex(i int) (resid.ResId, error) {
var foundId resid.ResId
found := false
for id, index := range m.rIndex {
if index == i {
if found {
return foundId, fmt.Errorf("found multiple")
}
found = true
foundId = id
}
}
if !found {
return foundId, fmt.Errorf("cannot find index %d", i)
}
return foundId, nil
}
type IdMatcher func(resid.ResId) bool type IdMatcher func(resid.ResId) bool
// GetMatchingIds returns a slice of ResId keys from the map // GetMatchingIds implements ResMap.
// that all satisfy the given matcher function. func (m *resWrangler) GetMatchingIds(matches IdMatcher) []resid.ResId {
func (m ResMap) GetMatchingIds(matches IdMatcher) []resid.ResId {
var result []resid.ResId var result []resid.ResId
for id := range m { for id := range m.rIndex {
if matches(id) { if matches(id) {
result = append(result, id) result = append(result, id)
} }
@@ -46,19 +350,19 @@ func (m ResMap) GetMatchingIds(matches IdMatcher) []resid.ResId {
return result return result
} }
// EncodeAsYaml encodes a ResMap to YAML; encoded objects separated by `---`. // EncodeAsYaml implements ResMap.
func (m ResMap) EncodeAsYaml() ([]byte, error) { func (m *resWrangler) EncodeAsYaml() ([]byte, error) {
var ids []resid.ResId // TODO: should be able to suppress this sort
for id := range m { // and rely on ordering as specified in the ResMap
ids = append(ids, id) // internal rList.
} ids := m.AllIds()
sort.Sort(IdSlice(ids)) sort.Sort(IdSlice(ids))
firstObj := true firstObj := true
var b []byte var b []byte
buf := bytes.NewBuffer(b) buf := bytes.NewBuffer(b)
for _, id := range ids { for _, id := range ids {
obj := m[id] obj := m.GetById(id)
out, err := yaml.Marshal(obj.Map()) out, err := yaml.Marshal(obj.Map())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -79,54 +383,80 @@ func (m ResMap) EncodeAsYaml() ([]byte, error) {
return buf.Bytes(), nil return buf.Bytes(), nil
} }
// ErrorIfNotEqual returns error if maps are not equal. // ErrorIfNotEqual implements ResMap.
func (m ResMap) ErrorIfNotEqual(m2 ResMap) error { func (m *resWrangler) ErrorIfNotEqual(other ResMap) error {
if len(m) != len(m2) { m2, ok := other.(*resWrangler)
var keySet1 []resid.ResId if !ok {
var keySet2 []resid.ResId panic(fmt.Errorf("bad cast to resmapImpl"))
for id := range m {
keySet1 = append(keySet1, id)
} }
for id := range m2 { if m.Size() != m2.Size() {
keySet2 = append(keySet2, id) return fmt.Errorf(
"lists have different number of entries: %#v doesn't equal %#v",
m.rList, m2.rList)
} }
return fmt.Errorf("maps has different number of entries: %#v doesn't equals %#v", keySet1, keySet2) for id, i := range m.rIndex {
r1 := m.rList[i]
r2 := m2.GetById(id)
if r2 == nil {
return fmt.Errorf("id in self missing from other; id: %s", id)
} }
for id, obj1 := range m { if !r1.KunstructEqual(r2) {
obj2, found := m2[id] return fmt.Errorf(
if !found { "kuns equal mismatch: \n -- %s,\n -- %s\n\n--\n%#v\n------\n%#v\n",
return fmt.Errorf("%#v doesn't exist in %#v", id, m2) r1, r2, r1, r2)
}
if !reflect.DeepEqual(obj1, obj2) {
return fmt.Errorf("%#v doesn't deep equal %#v", obj1, obj2)
} }
} }
return nil return nil
} }
// DeepCopy clone the resmap into a new one type resCopier func(r *resource.Resource) *resource.Resource
func (m ResMap) DeepCopy(rf *resource.Factory) ResMap {
mcopy := make(ResMap) // ShallowCopy implements ResMap.
for id, obj := range m { func (m *resWrangler) ShallowCopy() ResMap {
mcopy[id] = obj.DeepCopy() return m.makeCopy(
} func(r *resource.Resource) *resource.Resource {
return mcopy return r
})
} }
// FilterBy returns a subset ResMap containing ResIds with // DeepCopy implements ResMap.
// the same namespace and leftmost name prefix and rightmost name func (m *resWrangler) DeepCopy() ResMap {
// as the inputId. If inputId is a cluster level resource, this return m.makeCopy(
// returns the original ResMap. func(r *resource.Resource) *resource.Resource {
func (m ResMap) FilterBy(inputId resid.ResId) ResMap { return r.DeepCopy()
})
}
// makeCopy copies the ResMap.
func (m *resWrangler) makeCopy(copier resCopier) ResMap {
result := &resWrangler{}
result.rIndex = make(map[resid.ResId]int, m.Size())
result.rList = make([]*resource.Resource, m.Size())
for i, r := range m.rList {
result.rList[i] = copier(r)
id, err := m.idMappingToIndex(i)
if err != nil {
panic(fmt.Errorf("corrupt index map"))
}
result.rIndex[id] = i
}
return result
}
// ResourcesThatCouldReference implements ResMap.
func (m *resWrangler) ResourcesThatCouldReference(inputId resid.ResId) ResMap {
if inputId.Gvk().IsClusterKind() { if inputId.Gvk().IsClusterKind() {
return m return m
} }
result := ResMap{} result := New()
for id, res := range m { for id, i := range m.rIndex {
if id.Gvk().IsClusterKind() || id.Namespace() == inputId.Namespace() && if id.Gvk().IsClusterKind() || id.Namespace() == inputId.Namespace() &&
id.HasSameLeftmostPrefix(inputId) && id.HasSameLeftmostPrefix(inputId) &&
id.HasSameRightmostSuffix(inputId) { id.HasSameRightmostSuffix(inputId) {
result[id] = res err := result.AppendWithId(id, m.rList[i])
if err != nil {
panic(err)
}
} }
} }
return result return result
@@ -136,16 +466,16 @@ func (m ResMap) FilterBy(inputId resid.ResId) ResMap {
// key collision and skipping nil maps. // key collision and skipping nil maps.
// If all of the maps are nil, an empty ResMap is returned. // If all of the maps are nil, an empty ResMap is returned.
func MergeWithErrorOnIdCollision(maps ...ResMap) (ResMap, error) { func MergeWithErrorOnIdCollision(maps ...ResMap) (ResMap, error) {
result := ResMap{} result := New()
for _, m := range maps { for _, m := range maps {
if m == nil { if m == nil {
continue continue
} }
for id, res := range m { for id, res := range m.AsMap() {
if _, found := result[id]; found { err := result.AppendWithId(id, res)
return nil, fmt.Errorf("id '%q' already used", id) if err != nil {
return nil, err
} }
result[id] = res
} }
} }
return result, nil return result, nil
@@ -161,26 +491,40 @@ func MergeWithErrorOnIdCollision(maps ...ResMap) (ResMap, error) {
// resource X is found to be already in the combined map, then the behavior // resource X is found to be already in the combined map, then the behavior
// field for X must be BehaviorMerge or BehaviorReplace. If X is not in the // field for X must be BehaviorMerge or BehaviorReplace. If X is not in the
// map, then it's behavior cannot be merge or replace. // map, then it's behavior cannot be merge or replace.
// nolint: gocyclo
func MergeWithOverride(maps ...ResMap) (ResMap, error) { func MergeWithOverride(maps ...ResMap) (ResMap, error) {
if len(maps) == 0 {
return New(), nil
}
result := maps[0] result := maps[0]
if result == nil { if result == nil {
result = ResMap{} result = New()
} }
for _, m := range maps[1:] { for _, m := range maps[1:] {
if m == nil { if m == nil {
continue continue
} }
for id, r := range m { for id, r := range m.AsMap() {
matchedId := result.GetMatchingIds(id.GvknEquals) matchedId := result.GetMatchingIds(id.GvknEquals)
if len(matchedId) == 1 { if len(matchedId) == 1 {
id = matchedId[0] id = matchedId[0]
old := result.GetById(id)
if old == nil {
return nil, fmt.Errorf("id lookup failure")
}
switch r.Behavior() { switch r.Behavior() {
case types.BehaviorReplace: case types.BehaviorReplace:
r.Replace(result[id]) r.Replace(old)
result[id] = r err := result.ReplaceResource(id, r)
if err != nil {
return nil, err
}
case types.BehaviorMerge: case types.BehaviorMerge:
r.Merge(result[id]) r.Merge(old)
result[id] = r err := result.ReplaceResource(id, r)
if err != nil {
return nil, err
}
default: default:
return nil, fmt.Errorf("id %#v exists; must merge or replace", id) return nil, fmt.Errorf("id %#v exists; must merge or replace", id)
} }
@@ -189,7 +533,10 @@ func MergeWithOverride(maps ...ResMap) (ResMap, error) {
case types.BehaviorMerge, types.BehaviorReplace: case types.BehaviorMerge, types.BehaviorReplace:
return nil, fmt.Errorf("id %#v does not exist; cannot merge or replace", id) return nil, fmt.Errorf("id %#v does not exist; cannot merge or replace", id)
default: default:
result[id] = r err := result.AppendWithId(id, r)
if err != nil {
return nil, err
}
} }
} else { } else {
return nil, fmt.Errorf("merge conflict, found multiple objects %v the Resmap %v can merge into", matchedId, id) return nil, fmt.Errorf("merge conflict, found multiple objects %v the Resmap %v can merge into", matchedId, id)

View File

@@ -1,22 +1,10 @@
/* // Copyright 2019 The Kubernetes Authors.
Copyright 2018 The Kubernetes Authors. // SPDX-License-Identifier: Apache-2.0
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 resmap_test package resmap_test
import ( import (
"fmt"
"reflect" "reflect"
"testing" "testing"
@@ -34,6 +22,134 @@ var rf = resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()) kunstruct.NewKunstructuredFactoryImpl())
var rmF = NewFactory(rf) var rmF = NewFactory(rf)
func doAppend(t *testing.T, w ResMap, r *resource.Resource) {
err := w.Append(r)
if err != nil {
t.Fatalf("append error: %v", err)
}
}
func doRemove(t *testing.T, w ResMap, id resid.ResId) {
err := w.Remove(id)
if err != nil {
t.Fatalf("remove error: %v", err)
}
}
// Make a resource with a predictable name.
func makeCm(i int) *resource.Resource {
return rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": fmt.Sprintf("cm%03d", i),
},
})
}
func TestAppendRemove(t *testing.T) {
w1 := New()
doAppend(t, w1, makeCm(1))
doAppend(t, w1, makeCm(2))
doAppend(t, w1, makeCm(3))
doAppend(t, w1, makeCm(4))
doAppend(t, w1, makeCm(5))
doAppend(t, w1, makeCm(6))
doAppend(t, w1, makeCm(7))
doRemove(t, w1, makeCm(1).Id())
doRemove(t, w1, makeCm(3).Id())
doRemove(t, w1, makeCm(5).Id())
doRemove(t, w1, makeCm(7).Id())
w2 := New()
doAppend(t, w2, makeCm(2))
doAppend(t, w2, makeCm(4))
doAppend(t, w2, makeCm(6))
if !reflect.DeepEqual(w1, w1) {
w1.Debug("w1")
w2.Debug("w2")
t.Fatalf("mismatch")
}
err := w2.Append(makeCm(6))
if err == nil {
t.Fatalf("expected error")
}
}
func TestRemove(t *testing.T) {
w := New()
r := makeCm(1)
err := w.Remove(r.Id())
if err == nil {
t.Fatalf("expected error")
}
err = w.Append(r)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = w.Remove(r.Id())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = w.Remove(r.Id())
if err == nil {
t.Fatalf("expected error")
}
}
func TestReplaceResource(t *testing.T) {
cm5 := makeCm(5)
cm700 := makeCm(700)
cm888 := makeCm(888)
w := New()
doAppend(t, w, makeCm(1))
doAppend(t, w, makeCm(2))
doAppend(t, w, makeCm(3))
doAppend(t, w, makeCm(4))
doAppend(t, w, cm5)
doAppend(t, w, makeCm(6))
doAppend(t, w, makeCm(7))
oldSize := w.Size()
err := w.ReplaceResource(cm5.Id(), cm700)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if w.Size() != oldSize {
t.Fatalf("unexpected size %d", w.Size())
}
if w.GetById(cm5.Id()) != cm700 {
t.Fatalf("unexpected result")
}
if err := w.Append(cm5); err == nil {
t.Fatalf("expected id already there error")
}
if err := w.AppendWithId(cm888.Id(), cm5); err != nil {
// Okay to add with some unused Id.
t.Fatalf("unexpected error: %v", err)
}
if err := w.Append(cm700); err == nil {
t.Fatalf("expected resource already there error")
}
if err := w.Remove(cm5.Id()); err != nil {
t.Fatalf("unexpected err: %v", err)
}
if err := w.Append(cm700); err != nil {
t.Fatalf("unexpected err: %v", err)
}
if err := w.Append(cm5); err == nil {
t.Fatalf("expected err; object is still there under id 888")
}
if err := w.Remove(cm888.Id()); err != nil {
t.Fatalf("unexpected err: %v", err)
}
if err := w.Append(cm5); err != nil {
t.Fatalf("unexpected err; %v", err)
}
}
func TestEncodeAsYaml(t *testing.T) { func TestEncodeAsYaml(t *testing.T) {
encoded := []byte(`apiVersion: v1 encoded := []byte(`apiVersion: v1
kind: ConfigMap kind: ConfigMap
@@ -45,7 +161,7 @@ kind: ConfigMap
metadata: metadata:
name: cm2 name: cm2
`) `)
input := ResMap{ input := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -62,7 +178,7 @@ metadata:
"name": "cm2", "name": "cm2",
}, },
}), }),
} })
out, err := input.EncodeAsYaml() out, err := input.EncodeAsYaml()
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
@@ -73,7 +189,7 @@ metadata:
} }
func TestDemandOneGvknMatchForId(t *testing.T) { func TestDemandOneGvknMatchForId(t *testing.T) {
rm1 := ResMap{ rm1 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixNamespace(cmap, "cm1", "prefix1", "ns1"): rf.FromMap( resid.NewResIdWithPrefixNamespace(cmap, "cm1", "prefix1", "ns1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -90,7 +206,7 @@ func TestDemandOneGvknMatchForId(t *testing.T) {
"name": "cm2", "name": "cm2",
}, },
}), }),
} })
result := rm1.GetMatchingIds( result := rm1.GetMatchingIds(
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix1", "ns1").GvknEquals) resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix1", "ns1").GvknEquals)
@@ -129,7 +245,8 @@ func TestFilterBy(t *testing.T) {
expected ResMap expected ResMap
}{ }{
"different namespace": { "different namespace": {
resMap: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix", "namespace1"): rf.FromMap( resMap: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix", "namespace1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -137,12 +254,13 @@ func TestFilterBy(t *testing.T) {
"name": "config-map", "name": "config-map",
}, },
}), }),
}, }),
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix", "namespace2"), filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix", "namespace2"),
expected: ResMap{}, expected: New(),
}, },
"different prefix": { "different prefix": {
resMap: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix1", "suffix", "namespace"): rf.FromMap( resMap: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix1", "suffix", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -150,12 +268,13 @@ func TestFilterBy(t *testing.T) {
"name": "config-map", "name": "config-map",
}, },
}), }),
}, }),
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix2", "suffix", "namespace"), filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix2", "suffix", "namespace"),
expected: ResMap{}, expected: New(),
}, },
"different suffix": { "different suffix": {
resMap: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix1", "namespace"): rf.FromMap( resMap: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix1", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -163,12 +282,13 @@ func TestFilterBy(t *testing.T) {
"name": "config-map", "name": "config-map",
}, },
}), }),
}, }),
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix2", "namespace"), filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix2", "namespace"),
expected: ResMap{}, expected: New(),
}, },
"same namespace, same prefix": { "same namespace, same prefix": {
resMap: ResMap{resid.NewResIdWithPrefixNamespace(cmap, "config-map1", "prefix", "namespace"): rf.FromMap( resMap: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixNamespace(cmap, "config-map1", "prefix", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -176,9 +296,10 @@ func TestFilterBy(t *testing.T) {
"name": "config-map1", "name": "config-map1",
}, },
}), }),
}, }),
filter: resid.NewResIdWithPrefixNamespace(cmap, "config-map2", "prefix", "namespace"), filter: resid.NewResIdWithPrefixNamespace(cmap, "config-map2", "prefix", "namespace"),
expected: ResMap{resid.NewResIdWithPrefixNamespace(cmap, "config-map1", "prefix", "namespace"): rf.FromMap( expected: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixNamespace(cmap, "config-map1", "prefix", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -186,10 +307,11 @@ func TestFilterBy(t *testing.T) {
"name": "config-map1", "name": "config-map1",
}, },
}), }),
}, }),
}, },
"same namespace, same suffix": { "same namespace, same suffix": {
resMap: ResMap{resid.NewResIdWithSuffixNamespace(cmap, "config-map1", "suffix", "namespace"): rf.FromMap( resMap: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithSuffixNamespace(cmap, "config-map1", "suffix", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -197,9 +319,10 @@ func TestFilterBy(t *testing.T) {
"name": "config-map1", "name": "config-map1",
}, },
}), }),
}, }),
filter: resid.NewResIdWithSuffixNamespace(cmap, "config-map2", "suffix", "namespace"), filter: resid.NewResIdWithSuffixNamespace(cmap, "config-map2", "suffix", "namespace"),
expected: ResMap{resid.NewResIdWithSuffixNamespace(cmap, "config-map1", "suffix", "namespace"): rf.FromMap( expected: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithSuffixNamespace(cmap, "config-map1", "suffix", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -207,10 +330,11 @@ func TestFilterBy(t *testing.T) {
"name": "config-map1", "name": "config-map1",
}, },
}), }),
}, }),
}, },
"same namespace, same prefix, same suffix": { "same namespace, same prefix, same suffix": {
resMap: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map1", "prefix", "suffix", "namespace"): rf.FromMap( resMap: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map1", "prefix", "suffix", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -218,9 +342,10 @@ func TestFilterBy(t *testing.T) {
"name": "config-map", "name": "config-map",
}, },
}), }),
}, }),
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map2", "prefix", "suffix", "namespace"), filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map2", "prefix", "suffix", "namespace"),
expected: ResMap{resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map1", "prefix", "suffix", "namespace"): rf.FromMap( expected: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map1", "prefix", "suffix", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -228,10 +353,11 @@ func TestFilterBy(t *testing.T) {
"name": "config-map", "name": "config-map",
}, },
}), }),
}, }),
}, },
"filter by cluster-level Gvk": { "filter by cluster-level Gvk": {
resMap: ResMap{resid.NewResIdWithPrefixNamespace(cmap, "config-map", "prefix", "namespace"): rf.FromMap( resMap: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixNamespace(cmap, "config-map", "prefix", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -239,9 +365,10 @@ func TestFilterBy(t *testing.T) {
"name": "config-map", "name": "config-map",
}, },
}), }),
}, }),
filter: resid.NewResId(gvk.Gvk{Kind: "ClusterRoleBinding"}, "cluster-role-binding"), filter: resid.NewResId(gvk.Gvk{Kind: "ClusterRoleBinding"}, "cluster-role-binding"),
expected: ResMap{resid.NewResIdWithPrefixNamespace(cmap, "config-map", "prefix", "namespace"): rf.FromMap( expected: FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixNamespace(cmap, "config-map", "prefix", "namespace"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "ConfigMap", "kind": "ConfigMap",
@@ -249,15 +376,16 @@ func TestFilterBy(t *testing.T) {
"name": "config-map", "name": "config-map",
}, },
}), }),
}, }),
}, },
} }
for name, test := range tests { for name, test := range tests {
test := test test := test
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
got := test.resMap.FilterBy(test.filter) got := test.resMap.ResourcesThatCouldReference(test.filter)
if !reflect.DeepEqual(test.expected, got) { err := test.expected.ErrorIfNotEqual(got)
if err != nil {
t.Fatalf("Expected %v but got back %v", test.expected, got) t.Fatalf("Expected %v but got back %v", test.expected, got)
} }
}) })
@@ -265,7 +393,7 @@ func TestFilterBy(t *testing.T) {
} }
func TestDeepCopy(t *testing.T) { func TestDeepCopy(t *testing.T) {
rm1 := ResMap{ rm1 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -282,46 +410,63 @@ func TestDeepCopy(t *testing.T) {
"name": "cm2", "name": "cm2",
}, },
}), }),
} })
rm2 := rm1.DeepCopy(rf) rm2 := rm1.DeepCopy()
if &rm1 == &rm2 { if &rm1 == &rm2 {
t.Fatal("DeepCopy returned a reference to itself instead of a copy") t.Fatal("DeepCopy returned a reference to itself instead of a copy")
} }
err := rm1.ErrorIfNotEqual(rm1)
if !reflect.DeepEqual(rm1, rm2) { if err != nil {
t.Fatalf("%v doesn't equal it's deep copy %v", rm1, rm2) t.Fatal(err)
} }
} }
func TestGetMatchingIds(t *testing.T) { func TestGetMatchingIds(t *testing.T) {
// These ids used as map keys.
// They must be different to avoid overwriting m := FromMap(map[resid.ResId]*resource.Resource{
// map entries during construction.
ids := []resid.ResId{
resid.NewResId( resid.NewResId(
gvk.Gvk{Kind: "vegetable"}, gvk.Gvk{Kind: "vegetable"},
"bedlam"), "bedlam"): rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "whatever1",
},
}),
resid.NewResId( resid.NewResId(
gvk.Gvk{Group: "g1", Version: "v1", Kind: "vegetable"}, gvk.Gvk{Group: "g1", Version: "v1", Kind: "vegetable"},
"domino"), "domino"): rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "whatever2",
},
}),
resid.NewResIdWithPrefixNamespace( resid.NewResIdWithPrefixNamespace(
gvk.Gvk{Kind: "vegetable"}, gvk.Gvk{Kind: "vegetable"},
"peter", "p", "happy"), "peter", "p", "happy"): rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "whatever3",
},
}),
resid.NewResIdWithPrefixNamespace( resid.NewResIdWithPrefixNamespace(
gvk.Gvk{Version: "v1", Kind: "fruit"}, gvk.Gvk{Version: "v1", Kind: "fruit"},
"shatterstar", "p", "happy"), "shatterstar", "p", "happy"): rf.FromMap(
} map[string]interface{}{
"apiVersion": "v1",
m := ResMap{} "kind": "ConfigMap",
for _, id := range ids { "metadata": map[string]interface{}{
// Resources values don't matter in this test. "name": "whatever4",
m[id] = nil },
} }),
if len(m) != len(ids) { })
t.Fatalf("unexpected map len %d presumably due to duplicate keys", len(m))
}
tests := []struct { tests := []struct {
name string name string
@@ -362,7 +507,7 @@ func TestGetMatchingIds(t *testing.T) {
} }
func TestErrorIfNotEqual(t *testing.T) { func TestErrorIfNotEqual(t *testing.T) {
rm1 := ResMap{ rm1 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -379,14 +524,14 @@ func TestErrorIfNotEqual(t *testing.T) {
"name": "cm2", "name": "cm2",
}, },
}), }),
} })
err := rm1.ErrorIfNotEqual(rm1) err := rm1.ErrorIfNotEqual(rm1)
if err != nil { if err != nil {
t.Fatalf("%v should equal itself %v", rm1, err) t.Fatalf("%v should equal itself %v", rm1, err)
} }
rm2 := ResMap{ rm2 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -395,7 +540,7 @@ func TestErrorIfNotEqual(t *testing.T) {
"name": "cm1", "name": "cm1",
}, },
}), }),
} })
// test the different number of keys path // test the different number of keys path
err = rm1.ErrorIfNotEqual(rm2) err = rm1.ErrorIfNotEqual(rm2)
@@ -403,7 +548,7 @@ func TestErrorIfNotEqual(t *testing.T) {
t.Fatalf("%v should not equal %v %v", rm1, rm2, err) t.Fatalf("%v should not equal %v %v", rm1, rm2, err)
} }
rm3 := ResMap{ rm3 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm2"): rf.FromMap( resid.NewResId(cmap, "cm2"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -412,7 +557,7 @@ func TestErrorIfNotEqual(t *testing.T) {
"name": "cm1", "name": "cm1",
}, },
}), }),
} })
// test the different key values path // test the different key values path
err = rm2.ErrorIfNotEqual(rm3) err = rm2.ErrorIfNotEqual(rm3)
@@ -420,7 +565,7 @@ func TestErrorIfNotEqual(t *testing.T) {
t.Fatalf("%v should not equal %v %v", rm1, rm2, err) t.Fatalf("%v should not equal %v %v", rm1, rm2, err)
} }
rm4 := ResMap{ rm4 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -429,7 +574,7 @@ func TestErrorIfNotEqual(t *testing.T) {
"name": "cm3", "name": "cm3",
}, },
}), }),
} })
// test the deepcopy path // test the deepcopy path
err = rm2.ErrorIfNotEqual(rm4) err = rm2.ErrorIfNotEqual(rm4)
@@ -440,7 +585,7 @@ func TestErrorIfNotEqual(t *testing.T) {
} }
func TestMergeWithoutOverride(t *testing.T) { func TestMergeWithoutOverride(t *testing.T) {
input1 := ResMap{ input1 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(deploy, "deploy1"): rf.FromMap( resid.NewResId(deploy, "deploy1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -449,8 +594,8 @@ func TestMergeWithoutOverride(t *testing.T) {
"name": "foo-deploy1", "name": "foo-deploy1",
}, },
}), }),
} })
input2 := ResMap{ input2 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(statefulset, "stateful1"): rf.FromMap( resid.NewResId(statefulset, "stateful1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -459,9 +604,9 @@ func TestMergeWithoutOverride(t *testing.T) {
"name": "bar-stateful", "name": "bar-stateful",
}, },
}), }),
} })
input := []ResMap{input1, input2} input := []ResMap{input1, input2}
expected := ResMap{ expected := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(deploy, "deploy1"): rf.FromMap( resid.NewResId(deploy, "deploy1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -478,12 +623,13 @@ func TestMergeWithoutOverride(t *testing.T) {
"name": "bar-stateful", "name": "bar-stateful",
}, },
}), }),
} })
merged, err := MergeWithErrorOnIdCollision(input...) merged, err := MergeWithErrorOnIdCollision(input...)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(merged, expected) { err = expected.ErrorIfNotEqual(merged)
if err != nil {
t.Fatalf("%#v doesn't equal expected %#v", merged, expected) t.Fatalf("%#v doesn't equal expected %#v", merged, expected)
} }
input3 := []ResMap{merged, nil} input3 := []ResMap{merged, nil}
@@ -491,21 +637,23 @@ func TestMergeWithoutOverride(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(merged1, expected) { err = expected.ErrorIfNotEqual(merged1)
t.Fatalf("%#v doesn't equal expected %#v", merged1, expected) if err != nil {
t.Fatal(err)
} }
input4 := []ResMap{nil, merged} input4 := []ResMap{nil, merged}
merged2, err := MergeWithErrorOnIdCollision(input4...) merged2, err := MergeWithErrorOnIdCollision(input4...)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(merged2, expected) { err = expected.ErrorIfNotEqual(merged2)
t.Fatalf("%#v doesn't equal expected %#v", merged2, expected) if err != nil {
t.Fatal(err)
} }
} }
func generateMergeFixtures(b types.GenerationBehavior) []ResMap { func generateMergeFixtures(b types.GenerationBehavior) []ResMap {
input1 := ResMap{ input1 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cmap"): rf.FromMapAndOption( resid.NewResId(cmap, "cmap"): rf.FromMapAndOption(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -520,8 +668,8 @@ func generateMergeFixtures(b types.GenerationBehavior) []ResMap {
}, &types.GeneratorArgs{ }, &types.GeneratorArgs{
Behavior: "create", Behavior: "create",
}, nil), }, nil),
} })
input2 := ResMap{ input2 := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cmap"): rf.FromMapAndOption( resid.NewResId(cmap, "cmap"): rf.FromMapAndOption(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -537,12 +685,12 @@ func generateMergeFixtures(b types.GenerationBehavior) []ResMap {
}, &types.GeneratorArgs{ }, &types.GeneratorArgs{
Behavior: b.String(), Behavior: b.String(),
}, nil), }, nil),
} })
return []ResMap{input1, input2} return []ResMap{input1, input2}
} }
func TestMergeWithOverride(t *testing.T) { func TestMergeWithOverride(t *testing.T) {
expected := ResMap{ expected := FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cmap"): rf.FromMapAndOption( resid.NewResId(cmap, "cmap"): rf.FromMapAndOption(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@@ -560,41 +708,43 @@ func TestMergeWithOverride(t *testing.T) {
}, &types.GeneratorArgs{ }, &types.GeneratorArgs{
Behavior: "create", Behavior: "create",
}, nil), }, nil),
} })
merged, err := MergeWithOverride(generateMergeFixtures(types.BehaviorMerge)...) merged, err := MergeWithOverride(generateMergeFixtures(types.BehaviorMerge)...)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(merged, expected) { err = expected.ErrorIfNotEqual(merged)
t.Fatalf("%#v doesn't equal expected %#v", merged, expected) if err != nil {
t.Fatal(err)
} }
input3 := []ResMap{merged, nil} input3 := []ResMap{merged, nil}
merged1, err := MergeWithOverride(input3...) merged1, err := MergeWithOverride(input3...)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(merged1, expected) { err = expected.ErrorIfNotEqual(merged1)
t.Fatalf("%#v doesn't equal expected %#v", merged1, expected) if err != nil {
t.Fatal(err)
} }
input4 := []ResMap{nil, merged} input4 := []ResMap{nil, merged}
merged2, err := MergeWithOverride(input4...) merged2, err := MergeWithOverride(input4...)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(merged2, expected) { err = expected.ErrorIfNotEqual(merged2)
t.Fatalf("%#v doesn't equal expected %#v", merged2, expected) if err != nil {
t.Fatal(err)
} }
inputs := generateMergeFixtures(types.BehaviorReplace) inputs := generateMergeFixtures(types.BehaviorReplace)
replaced, err := MergeWithOverride(inputs...) replaced, err := MergeWithOverride(inputs...)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
expectedReplaced := inputs[1] expectedReplaced := inputs[1]
if !reflect.DeepEqual(replaced, expectedReplaced) { err = expectedReplaced.ErrorIfNotEqual(replaced)
t.Fatalf("%#v doesn't equal expected %#v", replaced, expectedReplaced) if err != nil {
t.Fatal(err)
} }
_, err = MergeWithOverride(generateMergeFixtures(types.BehaviorUnspecified)...) _, err = MergeWithOverride(generateMergeFixtures(types.BehaviorUnspecified)...)
if err == nil { if err == nil {
t.Fatal("Merging with GenerationBehavior BehaviorUnspecified should return an error but does not") t.Fatal("Merging with GenerationBehavior BehaviorUnspecified should return an error but does not")

View File

@@ -18,6 +18,7 @@ limitations under the License.
package resource package resource
import ( import (
"reflect"
"strings" "strings"
"sigs.k8s.io/kustomize/pkg/ifc" "sigs.k8s.io/kustomize/pkg/ifc"
@@ -34,6 +35,10 @@ type Resource struct {
refBy []resid.ResId refBy []resid.ResId
} }
func (r *Resource) KunstructEqual(o *Resource) bool {
return reflect.DeepEqual(r.Kunstructured, o.Kunstructured)
}
// String returns resource as JSON. // String returns resource as JSON.
func (r *Resource) String() string { func (r *Resource) String() string {
bs, err := r.MarshalJSON() bs, err := r.MarshalJSON()

View File

@@ -22,21 +22,6 @@ import (
"sigs.k8s.io/kustomize/pkg/kusttest" "sigs.k8s.io/kustomize/pkg/kusttest"
) )
// TODO(monopole): Add a feature test example covering secret generation.
// WARNING: These tests use a fake file system, and any attempt to use a
// feature that spawns shells will fail, because said shells expect a working
// directory corresponding to a real directory on disk - see
// these lines in secretfactory.go:
// cmd := exec.CommandContext(ctx, commands[0], commands[1:]...)
// cmd.Dir = f.wd
// Worse, the fake directory might match a real directory on the your system,
// making the failure less obvious (and maybe hurting something if your secret
// generation technique writes data to disk). So no use of secret generation
// in these particular tests.
// To eventually fix this, we could write the data to a real filesystem, and
// clean up after, or use some other trick compatible with exec.
func writeMediumBase(th *kusttest_test.KustTestHarness) { func writeMediumBase(th *kusttest_test.KustTestHarness) {
th.WriteK("/app/base", ` th.WriteK("/app/base", `
namePrefix: baseprefix- namePrefix: baseprefix-

View File

@@ -93,7 +93,7 @@ func TestResources(t *testing.T) {
th.WriteF("/whatever/namespace.yaml", namespaceContent) th.WriteF("/whatever/namespace.yaml", namespaceContent)
th.WriteF("/whatever/jsonpatch.json", jsonpatchContent) th.WriteF("/whatever/jsonpatch.json", jsonpatchContent)
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixSuffixNamespace( resid.NewResIdWithPrefixSuffixNamespace(
gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"}, gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"},
"dply1", "foo-", "-bar", "ns1"): th.FromMap( "dply1", "foo-", "-bar", "ns1"): th.FromMap(
@@ -192,14 +192,13 @@ func TestResources(t *testing.T) {
}, },
}, },
}), }),
} })
actual, err := th.MakeKustTarget().MakeCustomizedResMap() actual, err := th.MakeKustTarget().MakeCustomizedResMap()
if err != nil { if err != nil {
t.Fatalf("unexpected Resources error %v", err) t.Fatalf("unexpected Resources error %v", err)
} }
if !reflect.DeepEqual(actual, expected) { if err = expected.ErrorIfNotEqual(actual); err != nil {
err = expected.ErrorIfNotEqual(actual)
t.Fatalf("unexpected inequality: %v", err) t.Fatalf("unexpected inequality: %v", err)
} }
} }
@@ -229,7 +228,7 @@ func TestResourceNotFound(t *testing.T) {
} }
func findSecret(m resmap.ResMap) *resource.Resource { func findSecret(m resmap.ResMap) *resource.Resource {
for id, res := range m { for id, res := range m.AsMap() {
if id.Gvk().Kind == "Secret" { if id.Gvk().Kind == "Secret" {
return res return res
} }

View File

@@ -720,7 +720,7 @@ spec:
`) `)
} }
func TestVariableRefMounthPath(t *testing.T) { func TestVariableRefMountPath(t *testing.T) {
th := kusttest_test.NewKustTestHarness(t, "/app/base") th := kusttest_test.NewKustTestHarness(t, "/app/base")
th.WriteK("/app/base", ` th.WriteK("/app/base", `
resources: resources:

View File

@@ -44,19 +44,18 @@ func (pt *imageTransformer) Transform(m resmap.ResMap) error {
if len(pt.images) == 0 { if len(pt.images) == 0 {
return nil return nil
} }
for id := range m { for _, r := range m.Resources() {
objMap := m[id].Map()
for _, path := range pt.fieldSpecs { for _, path := range pt.fieldSpecs {
if !id.Gvk().IsSelected(&path.Gvk) { if !r.Id().Gvk().IsSelected(&path.Gvk) {
continue continue
} }
err := mutateField(objMap, path.PathSlice(), false, pt.mutateImage) err := mutateField(r.Map(), path.PathSlice(), false, pt.mutateImage)
if err != nil { if err != nil {
return err return err
} }
} }
// Kept for backward compatibility // Kept for backward compatibility
if err := pt.findAndReplaceImage(objMap); err != nil && id.Gvk().Kind != `CustomResourceDefinition` { if err := pt.findAndReplaceImage(r.Map()); err != nil && r.Id().Kind != `CustomResourceDefinition` {
return err return err
} }
} }

View File

@@ -59,13 +59,14 @@ func NewMapTransformer(
// Transform apply each <key, value> pair in the mapTransformer to the // Transform apply each <key, value> pair in the mapTransformer to the
// fields specified in mapTransformer. // fields specified in mapTransformer.
func (o *mapTransformer) Transform(m resmap.ResMap) error { func (o *mapTransformer) Transform(m resmap.ResMap) error {
for id := range m { for _, r := range m.Resources() {
objMap := m[id].Map()
for _, path := range o.fieldSpecs { for _, path := range o.fieldSpecs {
if !id.Gvk().IsSelected(&path.Gvk) { if !r.Id().Gvk().IsSelected(&path.Gvk) {
continue continue
} }
err := mutateField(objMap, path.PathSlice(), path.CreateIfNotPresent, o.addMap) err := mutateField(
r.Map(), path.PathSlice(),
path.CreateIfNotPresent, o.addMap)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -17,7 +17,6 @@ limitations under the License.
package transformers package transformers
import ( import (
"reflect"
"testing" "testing"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct" "sigs.k8s.io/kustomize/k8sdeps/kunstruct"
@@ -34,7 +33,7 @@ var cmap = gvk.Gvk{Version: "v1", Kind: "ConfigMap"}
var ns = gvk.Gvk{Version: "v1", Kind: "Namespace"} var ns = gvk.Gvk{Version: "v1", Kind: "Namespace"}
var deploy = gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"} var deploy = gvk.Gvk{Group: "apps", Version: "v1", Kind: "Deployment"}
var statefulset = gvk.Gvk{Group: "apps", Version: "v1", Kind: "StatefulSet"} var statefulset = gvk.Gvk{Group: "apps", Version: "v1", Kind: "StatefulSet"}
var crd = gvk.Gvk{Group: "apiwctensions.k8s.io", Version: "v1beta1", Kind: "CustomResourceDefinition"} var crd = gvk.Gvk{Group: "apiextensions.k8s.io", Version: "v1beta1", Kind: "CustomResourceDefinition"}
var job = gvk.Gvk{Group: "batch", Version: "v1", Kind: "Job"} var job = gvk.Gvk{Group: "batch", Version: "v1", Kind: "Job"}
var cronjob = gvk.Gvk{Group: "batch", Version: "v1beta1", Kind: "CronJob"} var cronjob = gvk.Gvk{Group: "batch", Version: "v1beta1", Kind: "CronJob"}
var pv = gvk.Gvk{Version: "v1", Kind: "PersistentVolume"} var pv = gvk.Gvk{Version: "v1", Kind: "PersistentVolume"}
@@ -47,7 +46,7 @@ var rf = resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl())
var defaultTransformerConfig = config.MakeDefaultConfig() var defaultTransformerConfig = config.MakeDefaultConfig()
func TestLabelsRun(t *testing.T) { func TestLabelsRun(t *testing.T) {
m := resmap.ResMap{ m := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -198,8 +197,8 @@ func TestLabelsRun(t *testing.T) {
}, },
}, },
}), }),
} })
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -430,7 +429,7 @@ func TestLabelsRun(t *testing.T) {
}, },
}, },
}), }),
} })
lt, err := NewLabelsMapTransformer( lt, err := NewLabelsMapTransformer(
map[string]string{"label-key1": "label-value1", "label-key2": "label-value2"}, map[string]string{"label-key1": "label-value1", "label-key2": "label-value2"},
defaultTransformerConfig.CommonLabels) defaultTransformerConfig.CommonLabels)
@@ -441,14 +440,13 @@ func TestLabelsRun(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(m, expected) { if err = expected.ErrorIfNotEqual(m); err != nil {
err = expected.ErrorIfNotEqual(m)
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }
} }
func TestAnnotationsRun(t *testing.T) { func TestAnnotationsRun(t *testing.T) {
m := resmap.ResMap{ m := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -499,8 +497,8 @@ func TestAnnotationsRun(t *testing.T) {
}, },
}, },
}), }),
} })
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -567,7 +565,7 @@ func TestAnnotationsRun(t *testing.T) {
}, },
}, },
}), }),
} })
at, err := NewAnnotationsMapTransformer( at, err := NewAnnotationsMapTransformer(
map[string]string{"anno-key1": "anno-value1", "anno-key2": "anno-value2"}, map[string]string{"anno-key1": "anno-value1", "anno-key2": "anno-value2"},
defaultTransformerConfig.CommonAnnotations) defaultTransformerConfig.CommonAnnotations)
@@ -578,14 +576,13 @@ func TestAnnotationsRun(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(m, expected) { if err = expected.ErrorIfNotEqual(m); err != nil {
err = expected.ErrorIfNotEqual(m)
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }
} }
func TestAnnotaionsRunWithNullValue(t *testing.T) { func TestAnnotaionsRunWithNullValue(t *testing.T) {
m := resmap.ResMap{ m := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -595,9 +592,9 @@ func TestAnnotaionsRunWithNullValue(t *testing.T) {
"annotations": nil, "annotations": nil,
}, },
}), }),
} })
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -610,7 +607,7 @@ func TestAnnotaionsRunWithNullValue(t *testing.T) {
}, },
}, },
}), }),
} })
at, err := NewAnnotationsMapTransformer( at, err := NewAnnotationsMapTransformer(
map[string]string{"anno-key1": "anno-value1", "anno-key2": "anno-value2"}, map[string]string{"anno-key1": "anno-value1", "anno-key2": "anno-value2"},
@@ -622,8 +619,7 @@ func TestAnnotaionsRunWithNullValue(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(m, expected) { if err = expected.ErrorIfNotEqual(m); err != nil {
err = expected.ErrorIfNotEqual(m)
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }

View File

@@ -18,8 +18,6 @@ package transformers
import ( import (
"fmt" "fmt"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/resmap"
) )
@@ -27,7 +25,6 @@ import (
type multiTransformer struct { type multiTransformer struct {
transformers []Transformer transformers []Transformer
checkConflictEnabled bool checkConflictEnabled bool
rf *resource.Factory
} }
var _ Transformer = &multiTransformer{} var _ Transformer = &multiTransformer{}
@@ -71,7 +68,7 @@ func (o *multiTransformer) transform(m resmap.ResMap) error {
// A spot check to perform when the transformations are supposed to be commutative. // A spot check to perform when the transformations are supposed to be commutative.
// Fail if there's a difference in the result. // Fail if there's a difference in the result.
func (o *multiTransformer) transformWithCheckConflict(m resmap.ResMap) error { func (o *multiTransformer) transformWithCheckConflict(m resmap.ResMap) error {
mcopy := m.DeepCopy(o.rf) mcopy := m.DeepCopy()
err := o.transform(m) err := o.transform(m)
if err != nil { if err != nil {
return err return err

View File

@@ -61,17 +61,18 @@ func NewNameReferenceTransformer(br []config.NameBackReferences) Transformer {
// body of the resource object (the value in the ResMap). // body of the resource object (the value in the ResMap).
func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error { func (o *nameReferenceTransformer) Transform(m resmap.ResMap) error {
// TODO: Too much looping. // TODO: Too much looping.
// Even more hidden loops in FilterBy, // Even more hidden loops in ResourcesThatCouldReference,
// updateNameReference and FindByGVKN. // updateNameReference and FindByGVKN.
for id := range m { for id, r := range m.AsMap() {
for _, backRef := range o.backRefs { for _, backRef := range o.backRefs {
for _, fSpec := range backRef.FieldSpecs { for _, fSpec := range backRef.FieldSpecs {
if id.Gvk().IsSelected(&fSpec.Gvk) { if id.Gvk().IsSelected(&fSpec.Gvk) {
err := mutateField( err := mutateField(
m[id].Map(), fSpec.PathSlice(), r.Map(),
fSpec.PathSlice(),
fSpec.CreateIfNotPresent, fSpec.CreateIfNotPresent,
o.updateNameReference( o.updateNameReference(
id, backRef.Gvk, m.FilterBy(id))) id, backRef.Gvk, m.ResourcesThatCouldReference(id)))
if err != nil { if err != nil {
return err return err
} }
@@ -88,7 +89,7 @@ func (o *nameReferenceTransformer) updateNameReference(
switch in.(type) { switch in.(type) {
case string: case string:
s, _ := in.(string) s, _ := in.(string)
for id, res := range m { for id, res := range m.AsMap() {
if id.Gvk().IsSelected(&backRef) && id.Name() == s { if id.Gvk().IsSelected(&backRef) && id.Name() == s {
matchedIds := m.GetMatchingIds(id.GvknEquals) matchedIds := m.GetMatchingIds(id.GvknEquals)
// If there's more than one match, there's no way // If there's more than one match, there's no way
@@ -114,7 +115,7 @@ func (o *nameReferenceTransformer) updateNameReference(
} }
names = append(names, name) names = append(names, name)
} }
for id, res := range m { for id, res := range m.AsMap() {
indexes := indexOf(id.Name(), names) indexes := indexOf(id.Name(), names)
if id.Gvk().IsSelected(&backRef) && len(indexes) > 0 { if id.Gvk().IsSelected(&backRef) && len(indexes) > 0 {
matchedIds := m.GetMatchingIds(id.GvknEquals) matchedIds := m.GetMatchingIds(id.GvknEquals)

View File

@@ -17,7 +17,6 @@ limitations under the License.
package transformers package transformers
import ( import (
"reflect"
"strings" "strings"
"testing" "testing"
@@ -30,7 +29,7 @@ import (
func TestNameReferenceHappyRun(t *testing.T) { func TestNameReferenceHappyRun(t *testing.T) {
rf := resource.NewFactory( rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()) kunstruct.NewKunstructuredFactoryImpl())
m := resmap.ResMap{ m := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -278,14 +277,11 @@ func TestNameReferenceHappyRun(t *testing.T) {
}, },
}, },
}), }),
} })
expected := resmap.ResMap{} expected := m.ShallowCopy()
for k, v := range m {
expected[k] = v
}
expected[resid.NewResId(deploy, "deploy1")] = rf.FromMap( expected.ReplaceResource(resid.NewResId(deploy, "deploy1"), rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"group": "apps", "group": "apps",
"apiVersion": "v1", "apiVersion": "v1",
@@ -365,8 +361,8 @@ func TestNameReferenceHappyRun(t *testing.T) {
}, },
}, },
}, },
}) }))
expected[resid.NewResId(statefulset, "statefulset1")] = rf.FromMap( expected.ReplaceResource(resid.NewResId(statefulset, "statefulset1"), rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"group": "apps", "group": "apps",
"apiVersion": "v1", "apiVersion": "v1",
@@ -398,8 +394,8 @@ func TestNameReferenceHappyRun(t *testing.T) {
}, },
}, },
}, },
}) }))
expected[resid.NewResId(ingress, "ingress1")] = rf.FromMap( expected.ReplaceResource(resid.NewResId(ingress, "ingress1"), rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"group": "extensions", "group": "extensions",
"apiVersion": "v1beta1", "apiVersion": "v1beta1",
@@ -418,8 +414,8 @@ func TestNameReferenceHappyRun(t *testing.T) {
}, },
}, },
}, },
) ))
expected[resid.NewResId(crb, "crb")] = rf.FromMap( expected.ReplaceResource(resid.NewResId(crb, "crb"), rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1", "apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRoleBinding", "kind": "ClusterRoleBinding",
@@ -433,8 +429,8 @@ func TestNameReferenceHappyRun(t *testing.T) {
"namespace": "test", "namespace": "test",
}, },
}, },
}) }))
expected[resid.NewResId(cr, "cr")] = rf.FromMap( expected.ReplaceResource(resid.NewResId(cr, "cr"), rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1", "apiVersion": "rbac.authorization.k8s.io/v1",
"kind": "ClusterRole", "kind": "ClusterRole",
@@ -453,8 +449,8 @@ func TestNameReferenceHappyRun(t *testing.T) {
}, },
}, },
}, },
}) }))
expected[resid.NewResId(cronjob, "cronjob1")] = rf.FromMap( expected.ReplaceResource(resid.NewResId(cronjob, "cronjob1"), rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "batch/v1beta1", "apiVersion": "batch/v1beta1",
"kind": "CronJob", "kind": "CronJob",
@@ -490,14 +486,13 @@ func TestNameReferenceHappyRun(t *testing.T) {
}, },
}, },
}, },
}) }))
nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference) nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference)
err := nrt.Transform(m) err := nrt.Transform(m)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(m, expected) { if err = expected.ErrorIfNotEqual(m); err != nil {
err = expected.ErrorIfNotEqual(m)
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }
} }
@@ -510,7 +505,7 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
expectedErr string expectedErr string
}{ }{
{ {
resMap: resmap.ResMap{ resMap: resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cr, "cr"): rf.FromMap( resid.NewResId(cr, "cr"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1", "apiVersion": "rbac.authorization.k8s.io/v1",
@@ -529,9 +524,9 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
}, },
}, },
}), }),
}, }),
expectedErr: "is expected to be string"}, expectedErr: "is expected to be string"},
{resMap: resmap.ResMap{ {resMap: resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cr, "cr"): rf.FromMap( resid.NewResId(cr, "cr"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "rbac.authorization.k8s.io/v1", "apiVersion": "rbac.authorization.k8s.io/v1",
@@ -550,7 +545,7 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
}, },
}, },
}), }),
}, }),
expectedErr: "is expected to be either a string or a []interface{}"}, expectedErr: "is expected to be either a string or a []interface{}"},
} }
@@ -571,7 +566,7 @@ func TestNameReferenceUnhappyRun(t *testing.T) {
func TestNameReferencePersistentVolumeHappyRun(t *testing.T) { func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
rf := resource.NewFactory( rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()) kunstruct.NewKunstructuredFactoryImpl())
m := resmap.ResMap{ m := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(pv, "volume1"): rf.FromMap( resid.NewResId(pv, "volume1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -593,9 +588,9 @@ func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
"volumeName": "volume1", "volumeName": "volume1",
}, },
}), }),
} })
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(pv, "volume1"): rf.FromMap( resid.NewResId(pv, "volume1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -617,15 +612,14 @@ func TestNameReferencePersistentVolumeHappyRun(t *testing.T) {
"volumeName": "someprefix-volume1", "volumeName": "someprefix-volume1",
}, },
}), }),
} })
expected[resid.NewResId(pv, "volume1")].AppendRefBy(resid.NewResId(pvc, "claim1")) expected.GetById(resid.NewResId(pv, "volume1")).AppendRefBy(resid.NewResId(pvc, "claim1"))
nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference) nrt := NewNameReferenceTransformer(defaultTransformerConfig.NameReference)
err := nrt.Transform(m) err := nrt.Transform(m)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(m, expected) { if err = expected.ErrorIfNotEqual(m); err != nil {
err = expected.ErrorIfNotEqual(m)
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }
} }

View File

@@ -50,8 +50,8 @@ func NewNamespaceTransformer(ns string, cf []config.FieldSpec) Transformer {
func (o *namespaceTransformer) Transform(m resmap.ResMap) error { func (o *namespaceTransformer) Transform(m resmap.ResMap) error {
mf := o.filterResmap(m) mf := o.filterResmap(m)
for id := range mf { for id, res := range mf.AsMap() {
objMap := mf[id].Map() objMap := res.Map()
for _, path := range o.fieldSpecsToUse { for _, path := range o.fieldSpecsToUse {
switch path.Path { switch path.Path {
// Special casing .metadata.namespace since it is a common metadata field across all runtime.Object // Special casing .metadata.namespace since it is a common metadata field across all runtime.Object
@@ -88,9 +88,9 @@ func (o *namespaceTransformer) Transform(m resmap.ResMap) error {
if !id.Gvk().IsClusterKind() { if !id.Gvk().IsClusterKind() {
newid := id.CopyWithNewNamespace(o.namespace) newid := id.CopyWithNewNamespace(o.namespace)
m[newid] = mf[id] m.AppendWithId(newid, res)
} else { } else {
m[id] = mf[id] m.AppendWithId(id, res)
} }
} }
} }
@@ -99,19 +99,19 @@ func (o *namespaceTransformer) Transform(m resmap.ResMap) error {
} }
func (o *namespaceTransformer) filterResmap(m resmap.ResMap) resmap.ResMap { func (o *namespaceTransformer) filterResmap(m resmap.ResMap) resmap.ResMap {
mf := resmap.ResMap{} mf := resmap.New()
for id := range m { for id, res := range m.AsMap() {
found := false found := false
for _, path := range o.fieldSpecsToSkip { for _, path := range o.fieldSpecsToSkip {
if id.Gvk().IsSelected(&path.Gvk) { if id.Gvk().IsSelected(&path.Gvk) {
found = true found = true
mf[id] = m[id] mf.AppendWithId(id, res)
delete(m, id) m.Remove(id)
} }
} }
if !found { if !found {
mf[id] = m[id] mf.AppendWithId(id, res)
delete(m, id) m.Remove(id)
} }
} }
return mf return mf
@@ -119,17 +119,17 @@ func (o *namespaceTransformer) filterResmap(m resmap.ResMap) resmap.ResMap {
func (o *namespaceTransformer) updateClusterRoleBinding(m resmap.ResMap) { func (o *namespaceTransformer) updateClusterRoleBinding(m resmap.ResMap) {
saMap := map[string]bool{} saMap := map[string]bool{}
for id := range m { for id := range m.AsMap() {
if id.Gvk().Equals(gvk.Gvk{Version: "v1", Kind: "ServiceAccount"}) { if id.Gvk().Equals(gvk.Gvk{Version: "v1", Kind: "ServiceAccount"}) {
saMap[id.Name()] = true saMap[id.Name()] = true
} }
} }
for id := range m { for id, res := range m.AsMap() {
if id.Gvk().Kind != "ClusterRoleBinding" && id.Gvk().Kind != "RoleBinding" { if id.Gvk().Kind != "ClusterRoleBinding" && id.Gvk().Kind != "RoleBinding" {
continue continue
} }
objMap := m[id].Map() objMap := res.Map()
subjects, ok := objMap["subjects"].([]interface{}) subjects, ok := objMap["subjects"].([]interface{})
if subjects == nil || !ok { if subjects == nil || !ok {
continue continue

View File

@@ -17,7 +17,6 @@ limitations under the License.
package transformers package transformers
import ( import (
"reflect"
"testing" "testing"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct" "sigs.k8s.io/kustomize/k8sdeps/kunstruct"
@@ -29,7 +28,7 @@ import (
func TestNamespaceRun(t *testing.T) { func TestNamespaceRun(t *testing.T) {
rf := resource.NewFactory( rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()) kunstruct.NewKunstructuredFactoryImpl())
m := resmap.ResMap{ m := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -109,8 +108,8 @@ func TestNamespaceRun(t *testing.T) {
"name": "crd", "name": "crd",
}, },
}), }),
} })
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixNamespace(ns, "ns1", "", ""): rf.FromMap( resid.NewResIdWithPrefixNamespace(ns, "ns1", "", ""): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -191,15 +190,14 @@ func TestNamespaceRun(t *testing.T) {
"name": "crd", "name": "crd",
}, },
}), }),
} })
nst := NewNamespaceTransformer("test", defaultTransformerConfig.NameSpace) nst := NewNamespaceTransformer("test", defaultTransformerConfig.NameSpace)
err := nst.Transform(m) err := nst.Transform(m)
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(m, expected) { if err = expected.ErrorIfNotEqual(m); err != nil {
err = expected.ErrorIfNotEqual(m)
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }
} }
@@ -207,7 +205,7 @@ func TestNamespaceRun(t *testing.T) {
func TestNamespaceRunForClusterLevelKind(t *testing.T) { func TestNamespaceRunForClusterLevelKind(t *testing.T) {
rf := resource.NewFactory( rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()) kunstruct.NewKunstructuredFactoryImpl())
m := resmap.ResMap{ m := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(ns, "ns1"): rf.FromMap( resid.NewResId(ns, "ns1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -245,9 +243,9 @@ func TestNamespaceRunForClusterLevelKind(t *testing.T) {
}, },
"subjects": []interface{}{}, "subjects": []interface{}{},
}), }),
} })
expected := m.DeepCopy(rf) expected := m.DeepCopy()
nst := NewNamespaceTransformer("test", defaultTransformerConfig.NameSpace) nst := NewNamespaceTransformer("test", defaultTransformerConfig.NameSpace)
@@ -255,8 +253,7 @@ func TestNamespaceRunForClusterLevelKind(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(m, expected) { if err = expected.ErrorIfNotEqual(m); err != nil {
err = expected.ErrorIfNotEqual(m)
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }
} }

View File

@@ -59,12 +59,14 @@ func NewNamePrefixSuffixTransformer(
} }
// Transform prepends the name prefix and appends the name suffix. // Transform prepends the name prefix and appends the name suffix.
// TODO: this transformer breaks internal
// ordering and depends on Id hackery. Rewrite completely.
func (o *namePrefixSuffixTransformer) Transform(m resmap.ResMap) error { func (o *namePrefixSuffixTransformer) Transform(m resmap.ResMap) error {
// Fill map "mf" with entries subject to name modification, and // Fill map "mf" with entries subject to name modification, and
// delete these entries from "m", so that for now m retains only // delete these entries from "m", so that for now m retains only
// the entries whose names will not be modified. // the entries whose names will not be modified.
mf := resmap.ResMap{} mf := resmap.New()
for id := range m { for id, r := range m.AsMap() {
found := false found := false
for _, path := range o.fieldSpecsToSkip { for _, path := range o.fieldSpecsToSkip {
if id.Gvk().IsSelected(&path.Gvk) { if id.Gvk().IsSelected(&path.Gvk) {
@@ -73,13 +75,13 @@ func (o *namePrefixSuffixTransformer) Transform(m resmap.ResMap) error {
} }
} }
if !found { if !found {
mf[id] = m[id] mf.AppendWithId(id, r)
delete(m, id) m.Remove(id)
} }
} }
for id := range mf { for id, r := range mf.AsMap() {
objMap := mf[id].Map() objMap := r.Map()
for _, path := range o.fieldSpecsToUse { for _, path := range o.fieldSpecsToUse {
if !id.Gvk().IsSelected(&path.Gvk) { if !id.Gvk().IsSelected(&path.Gvk) {
continue continue
@@ -93,7 +95,7 @@ func (o *namePrefixSuffixTransformer) Transform(m resmap.ResMap) error {
return err return err
} }
newId := id.CopyWithNewPrefixSuffix(o.prefix, o.suffix) newId := id.CopyWithNewPrefixSuffix(o.prefix, o.suffix)
m[newId] = mf[id] m.AppendWithId(newId, r)
} }
} }
return nil return nil

View File

@@ -17,7 +17,6 @@ limitations under the License.
package transformers package transformers
import ( import (
"reflect"
"testing" "testing"
"sigs.k8s.io/kustomize/k8sdeps/kunstruct" "sigs.k8s.io/kustomize/k8sdeps/kunstruct"
@@ -29,7 +28,7 @@ import (
func TestPrefixSuffixNameRun(t *testing.T) { func TestPrefixSuffixNameRun(t *testing.T) {
rf := resource.NewFactory( rf := resource.NewFactory(
kunstruct.NewKunstructuredFactoryImpl()) kunstruct.NewKunstructuredFactoryImpl())
m := resmap.ResMap{ m := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -54,8 +53,8 @@ func TestPrefixSuffixNameRun(t *testing.T) {
"name": "crd", "name": "crd",
}, },
}), }),
} })
expected := resmap.ResMap{ expected := resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResIdWithPrefixSuffix(cmap, "cm1", "someprefix-", "-somesuffix"): rf.FromMap( resid.NewResIdWithPrefixSuffix(cmap, "cm1", "someprefix-", "-somesuffix"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -80,7 +79,7 @@ func TestPrefixSuffixNameRun(t *testing.T) {
"name": "crd", "name": "crd",
}, },
}), }),
} })
npst, err := NewNamePrefixSuffixTransformer( npst, err := NewNamePrefixSuffixTransformer(
"someprefix-", "-somesuffix", defaultTransformerConfig.NamePrefix) "someprefix-", "-somesuffix", defaultTransformerConfig.NamePrefix)
@@ -91,8 +90,7 @@ func TestPrefixSuffixNameRun(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if !reflect.DeepEqual(m, expected) { if err = expected.ErrorIfNotEqual(m); err != nil {
err = expected.ErrorIfNotEqual(m)
t.Fatalf("actual doesn't match expected: %v", err) t.Fatalf("actual doesn't match expected: %v", err)
} }
} }

View File

@@ -95,9 +95,9 @@ func (rv *RefVarTransformer) Transform(m resmap.ResMap) error {
rv.replacementCounts = make(map[string]int) rv.replacementCounts = make(map[string]int)
rv.mappingFunc = expansion.MappingFuncFor( rv.mappingFunc = expansion.MappingFuncFor(
rv.replacementCounts, rv.varMap) rv.replacementCounts, rv.varMap)
for id, res := range m { for _, res := range m.Resources() {
for _, fieldSpec := range rv.fieldSpecs { for _, fieldSpec := range rv.fieldSpecs {
if id.Gvk().IsSelected(&fieldSpec.Gvk) { if res.Id().Gvk().IsSelected(&fieldSpec.Gvk) {
if err := mutateField( if err := mutateField(
res.Map(), fieldSpec.PathSlice(), res.Map(), fieldSpec.PathSlice(),
false, rv.replaceVars); err != nil { false, rv.replaceVars); err != nil {

View File

@@ -22,6 +22,7 @@ import (
"sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resid"
"sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/resmap"
"sigs.k8s.io/kustomize/pkg/resource"
"sigs.k8s.io/kustomize/pkg/transformers/config" "sigs.k8s.io/kustomize/pkg/transformers/config"
) )
@@ -50,7 +51,7 @@ func TestVarRef(t *testing.T) {
fs: []config.FieldSpec{ fs: []config.FieldSpec{
{Gvk: cmap, Path: "data"}, {Gvk: cmap, Path: "data"},
}, },
res: resmap.ResMap{ res: resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -63,10 +64,10 @@ func TestVarRef(t *testing.T) {
"item2": "bla", "item2": "bla",
}, },
}), }),
}, }),
}, },
expected: expected{ expected: expected{
res: resmap.ResMap{ res: resmap.FromMap(map[resid.ResId]*resource.Resource{
resid.NewResId(cmap, "cm1"): rf.FromMap( resid.NewResId(cmap, "cm1"): rf.FromMap(
map[string]interface{}{ map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
@@ -79,7 +80,7 @@ func TestVarRef(t *testing.T) {
"item2": "bla", "item2": "bla",
}, },
}), }),
}, }),
unused: []string{"BAR"}, unused: []string{"BAR"},
}, },
}, },

View File

@@ -24,7 +24,7 @@ func (p *HashTransformerPlugin) Config(
// Transform appends hash to generated resources. // Transform appends hash to generated resources.
func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error { func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error {
for _, res := range m { for _, res := range m.Resources() {
if res.NeedHashSuffix() { if res.NeedHashSuffix() {
h, err := p.hasher.Hash(res) h, err := p.hasher.Hash(res)
if err != nil { if err != nil {

View File

@@ -86,25 +86,18 @@ func (p *InventoryTransformerPlugin) Transform(m resmap.ResMap) error {
} }
if p.Policy == types.GarbageCollect.String() { if p.Policy == types.GarbageCollect.String() {
for byeBye := range m { for _, byeBye := range m.AllIds() {
delete(m, byeBye) m.Remove(byeBye)
} }
} }
return m.Append(cm)
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) ( func makeInventory(m resmap.ResMap) (
inv *inventory.Inventory, hash string, err error) { inv *inventory.Inventory, hash string, err error) {
inv = inventory.NewInventory() inv = inventory.NewInventory()
var keys []string var keys []string
for _, r := range m { for _, r := range m.Resources() {
ns := getNamespace(r) ns := getNamespace(r)
item := resid.NewItemId(r.GetGvk(), ns, r.GetName()) item := resid.NewItemId(r.GetGvk(), ns, r.GetName())
if _, ok := inv.Current[item]; ok { if _, ok := inv.Current[item]; ok {
@@ -128,7 +121,7 @@ func getNamespace(r *resource.Resource) string {
func computeRefs(r *resource.Resource, m resmap.ResMap) (refs []resid.ItemId) { func computeRefs(r *resource.Resource, m resmap.ResMap) (refs []resid.ItemId) {
for _, refid := range r.GetRefBy() { for _, refid := range r.GetRefBy() {
ref := m[refid] ref := m.GetById(refid)
ns := getNamespace(ref) ns := getNamespace(ref)
refs = append(refs, resid.NewItemId(ref.GetGvk(), ns, ref.GetName())) refs = append(refs, resid.NewItemId(ref.GetGvk(), ns, ref.GetName()))
} }

View File

@@ -38,8 +38,8 @@ func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
return r.ItemId.Name == p.Replica.Name return r.ItemId.Name == p.Replica.Name
} }
for _, r := range m.GetMatchingIds(matcher) { for _, id := range m.GetMatchingIds(matcher) {
kMap := m[r].Map() kMap := m.GetById(id).Map()
specInterface, ok := kMap[fldSpec] specInterface, ok := kMap[fldSpec]
if !ok { if !ok {

View File

@@ -25,7 +25,7 @@ func (p *plugin) Config(
// Transform appends hash to generated resources. // Transform appends hash to generated resources.
func (p *plugin) Transform(m resmap.ResMap) error { func (p *plugin) Transform(m resmap.ResMap) error {
for _, res := range m { for _, res := range m.Resources() {
if res.NeedHashSuffix() { if res.NeedHashSuffix() {
h, err := p.hasher.Hash(res) h, err := p.hasher.Hash(res)
if err != nil { if err != nil {

View File

@@ -87,25 +87,18 @@ func (p *plugin) Transform(m resmap.ResMap) error {
} }
if p.Policy == types.GarbageCollect.String() { if p.Policy == types.GarbageCollect.String() {
for byeBye := range m { for _, byeBye := range m.AllIds() {
delete(m, byeBye) m.Remove(byeBye)
} }
} }
return m.Append(cm)
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) ( func makeInventory(m resmap.ResMap) (
inv *inventory.Inventory, hash string, err error) { inv *inventory.Inventory, hash string, err error) {
inv = inventory.NewInventory() inv = inventory.NewInventory()
var keys []string var keys []string
for _, r := range m { for _, r := range m.Resources() {
ns := getNamespace(r) ns := getNamespace(r)
item := resid.NewItemId(r.GetGvk(), ns, r.GetName()) item := resid.NewItemId(r.GetGvk(), ns, r.GetName())
if _, ok := inv.Current[item]; ok { if _, ok := inv.Current[item]; ok {
@@ -129,7 +122,7 @@ func getNamespace(r *resource.Resource) string {
func computeRefs(r *resource.Resource, m resmap.ResMap) (refs []resid.ItemId) { func computeRefs(r *resource.Resource, m resmap.ResMap) (refs []resid.ItemId) {
for _, refid := range r.GetRefBy() { for _, refid := range r.GetRefBy() {
ref := m[refid] ref := m.GetById(refid)
ns := getNamespace(ref) ns := getNamespace(ref)
refs = append(refs, resid.NewItemId(ref.GetGvk(), ns, ref.GetName())) refs = append(refs, resid.NewItemId(ref.GetGvk(), ns, ref.GetName()))
} }

View File

@@ -122,4 +122,3 @@ namespace: default
th.AssertActualEqualsExpected(rm, inv+"---"+content) th.AssertActualEqualsExpected(rm, inv+"---"+content)
} }

View File

@@ -39,8 +39,8 @@ func (p *plugin) Transform(m resmap.ResMap) error {
return r.ItemId.Name == p.Replica.Name return r.ItemId.Name == p.Replica.Name
} }
for _, r := range m.GetMatchingIds(matcher) { for _, id := range m.GetMatchingIds(matcher) {
kMap := m[r].Map() kMap := m.GetById(id).Map()
specInterface, ok := kMap[fldSpec] specInterface, ok := kMap[fldSpec]
if !ok { if !ok {