Refactored resource to store all previous names and namespaces

This commit is contained in:
Natasha Sarkar
2021-02-01 14:50:20 -08:00
parent 6246262965
commit f71854a0c8
23 changed files with 376 additions and 442 deletions

View File

@@ -28,7 +28,7 @@ func (p *HashTransformerPlugin) Transform(m resmap.ResMap) error {
if err != nil { if err != nil {
return err return err
} }
res.SetOriginalName(res.GetName(), false) res.StorePreviousId()
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h)) res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
} }
} }

View File

@@ -34,7 +34,7 @@ func (p *NamespaceTransformerPlugin) Transform(m resmap.ResMap) error {
// Don't mutate empty objects? // Don't mutate empty objects?
continue continue
} }
r.SetOriginalNs(r.GetNamespace(), false) r.StorePreviousId()
err := r.ApplyFilter(namespace.Filter{ err := r.ApplyFilter(namespace.Filter{
Namespace: p.Namespace, Namespace: p.Namespace,
FsSlice: p.FieldSpecs, FsSlice: p.FieldSpecs,

View File

@@ -104,7 +104,7 @@ func (p *PatchTransformerPlugin) transformJson6902(m resmap.ResMap, patch jsonpa
return err return err
} }
for _, res := range resources { for _, res := range resources {
res.SetOriginalName(res.GetName(), false) res.StorePreviousId()
err = res.ApplyFilter(patchjson6902.Filter{ err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.Patch, Patch: p.Patch,
}) })

View File

@@ -70,7 +70,7 @@ func (p *PrefixSuffixTransformerPlugin) Transform(m resmap.ResMap) error {
r.AddNamePrefix(p.Prefix) r.AddNamePrefix(p.Prefix)
r.AddNameSuffix(p.Suffix) r.AddNameSuffix(p.Suffix)
if p.Prefix != "" || p.Suffix != "" { if p.Prefix != "" || p.Suffix != "" {
r.SetOriginalName(r.GetName(), false) r.StorePreviousId()
} }
} }
err := r.ApplyFilter(prefixsuffix.Filter{ err := r.ApplyFilter(prefixsuffix.Filter{

View File

@@ -31,9 +31,7 @@ func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error {
found := false found := false
for _, fs := range p.FieldSpecs { for _, fs := range p.FieldSpecs {
matcher := p.createMatcher(fs) matcher := p.createMatcher(fs)
matchOriginal := m.GetMatchingResourcesByOriginalId(matcher) resList := m.GetMatchingResourcesByAnyId(matcher)
resList := append(
matchOriginal, m.GetMatchingResourcesByCurrentId(matcher)...)
if len(resList) > 0 { if len(resList) > 0 {
found = true found = true
for _, r := range resList { for _, r := range resList {

View File

@@ -241,7 +241,7 @@ func acceptAll(r *resource.Resource) bool {
func originalNameMatches(name string) sieveFunc { func originalNameMatches(name string) sieveFunc {
return func(r *resource.Resource) bool { return func(r *resource.Resource) bool {
return r.GetOriginalName() == name return r.OrgId().Name == name
} }
} }
@@ -304,7 +304,7 @@ func (f Filter) sameCurrentNamespaceAsReferrer() sieveFunc {
// selectReferral picks the best referral from a list of candidates. // selectReferral picks the best referral from a list of candidates.
func (f Filter) selectReferral( func (f Filter) selectReferral(
// The name referral that may need to be updated. // The name referral that may need to be updated.
oldName string, oldName string,
candidates []*resource.Resource) (*resource.Resource, error) { candidates []*resource.Resource) (*resource.Resource, error) {
candidates = doSieve(candidates, originalNameMatches(oldName)) candidates = doSieve(candidates, originalNameMatches(oldName))

View File

@@ -77,7 +77,7 @@ func (ra *ResAccumulator) MergeVars(incoming []types.Var) error {
// wildcard search on the namespace hence we still use GvknEquals // wildcard search on the namespace hence we still use GvknEquals
idMatcher = targetId.Equals idMatcher = targetId.Equals
} }
matched := ra.resMap.GetMatchingResourcesByOriginalId(idMatcher) matched := ra.resMap.GetMatchingResourcesByAnyId(idMatcher)
if len(matched) > 1 { if len(matched) > 1 {
return fmt.Errorf( return fmt.Errorf(
"found %d resId matches for var %s "+ "found %d resId matches for var %s "+

View File

@@ -344,20 +344,20 @@ func TestResolveVarsWithNoambiguation(t *testing.T) {
}, },
}, },
}}). }}).
// Make it seem like this resource
// went through a prefix transformer.
Add(map[string]interface{}{ Add(map[string]interface{}{
"apiVersion": "v1", "apiVersion": "v1",
"kind": "Service", "kind": "Service",
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"name": "backendOne", "name": "sub-backendOne",
"annotations": map[string]interface{}{
"config.kubernetes.io/originalName": "backendOne",
"config.kubernetes.io/originalNs": "default",
"config.kubernetes.io/prefixes": "sub-",
},
}}).ResMap() }}).ResMap()
// Make it seem like this resource
// went through a prefix transformer.
r := m.GetByIndex(1)
r.AddNamePrefix("sub-")
r.SetName("sub-backendOne")
r.SetOriginalName("backendOne", true)
err = ra2.AppendAll(m) err = ra2.AppendAll(m)
if err != nil { if err != nil {
t.Fatalf("unexpected err: %v", err) t.Fatalf("unexpected err: %v", err)

View File

@@ -135,9 +135,6 @@ func GetResMapWithIDAnnotation(rm resmap.ResMap) (resmap.ResMap, error) {
return nil, err return nil, err
} }
annotations := r.GetAnnotations() annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
annotations[idAnnotation] = string(idString) annotations[idAnnotation] = string(idString)
r.SetAnnotations(annotations) r.SetAnnotations(annotations)
} }

View File

@@ -47,7 +47,7 @@ func (m *merginator) ConflatePatches(in []*resource.Resource) (ResMap, error) {
func (m *merginator) appendIfNoMatch(index int) (*resource.Resource, error) { func (m *merginator) appendIfNoMatch(index int) (*resource.Resource, error) {
candidate := m.incoming[index] candidate := m.incoming[index]
matchedResources := m.result.GetMatchingResourcesByOriginalId( matchedResources := m.result.GetMatchingResourcesByAnyId(
candidate.OrgId().Equals) candidate.OrgId().Equals)
if len(matchedResources) == 0 { if len(matchedResources) == 0 {
m.result.Append(candidate) m.result.Append(candidate)

View File

@@ -142,24 +142,18 @@ type ResMap interface {
// who's CurId is matched by the argument. // who's CurId is matched by the argument.
GetMatchingResourcesByCurrentId(matches IdMatcher) []*resource.Resource GetMatchingResourcesByCurrentId(matches IdMatcher) []*resource.Resource
// GetMatchingResourcesByOriginalId returns the resources // GetMatchingResourcesByAnyId returns the resources
// who's OriginalId is matched by the argument. // who's current or previous IDs is matched by the argument.
GetMatchingResourcesByOriginalId(matches IdMatcher) []*resource.Resource GetMatchingResourcesByAnyId(matches IdMatcher) []*resource.Resource
// GetByCurrentId is shorthand for calling // GetByCurrentId is shorthand for calling
// GetMatchingResourcesByCurrentId with a matcher requiring // GetMatchingResourcesByCurrentId with a matcher requiring
// an exact match, returning an error on multiple or no matches. // an exact match, returning an error on multiple or no matches.
GetByCurrentId(resid.ResId) (*resource.Resource, error) GetByCurrentId(resid.ResId) (*resource.Resource, error)
// GetByOriginalId is shorthand for calling // GetByPreviousId is shorthand for calling
// GetMatchingResourcesByOriginalId with a matcher requiring // GetMatchingResourcesByAnyId with a matcher requiring
// an exact match, returning an error on multiple or no matches. // an exact match, returning an error on multiple or no matches.
GetByOriginalId(resid.ResId) (*resource.Resource, error)
// GetById is a helper function which first
// attempts GetByOriginalId, then GetByCurrentId,
// returning an error if both fail to find a single
// match.
GetById(resid.ResId) (*resource.Resource, error) GetById(resid.ResId) (*resource.Resource, error)
// GroupedByCurrentNamespace returns a map of namespace // GroupedByCurrentNamespace returns a map of namespace

View File

@@ -155,8 +155,7 @@ func (m *resWrangler) GetIndexOfCurrentId(id resid.ResId) (int, error) {
type IdFromResource func(r *resource.Resource) resid.ResId type IdFromResource func(r *resource.Resource) resid.ResId
func GetOriginalId(r *resource.Resource) resid.ResId { return r.OrgId() } func GetCurrentId(r *resource.Resource) resid.ResId { return r.CurId() }
func GetCurrentId(r *resource.Resource) resid.ResId { return r.CurId() }
// GetMatchingResourcesByCurrentId implements ResMap. // GetMatchingResourcesByCurrentId implements ResMap.
func (m *resWrangler) GetMatchingResourcesByCurrentId( func (m *resWrangler) GetMatchingResourcesByCurrentId(
@@ -164,10 +163,21 @@ func (m *resWrangler) GetMatchingResourcesByCurrentId(
return m.filteredById(matches, GetCurrentId) return m.filteredById(matches, GetCurrentId)
} }
// GetMatchingResourcesByOriginalId implements ResMap. // GetMatchingResourcesByAnyId implements ResMap.
func (m *resWrangler) GetMatchingResourcesByOriginalId( func (m *resWrangler) GetMatchingResourcesByAnyId(
matches IdMatcher) []*resource.Resource { matches IdMatcher) []*resource.Resource {
return m.filteredById(matches, GetOriginalId) var result []*resource.Resource
for _, r := range m.rList {
prevIds := r.PrevIds()
prevIds = append(prevIds, r.CurId())
for _, prevId := range prevIds {
if matches(prevId) {
result = append(result, r)
break
}
}
}
return result
} }
func (m *resWrangler) filteredById( func (m *resWrangler) filteredById(
@@ -187,26 +197,16 @@ func (m *resWrangler) GetByCurrentId(
return demandOneMatch(m.GetMatchingResourcesByCurrentId, id, "Current") return demandOneMatch(m.GetMatchingResourcesByCurrentId, id, "Current")
} }
// GetByOriginalId implements ResMap.
func (m *resWrangler) GetByOriginalId(
id resid.ResId) (*resource.Resource, error) {
return demandOneMatch(m.GetMatchingResourcesByOriginalId, id, "Original")
}
// GetById implements ResMap. // GetById implements ResMap.
func (m *resWrangler) GetById( func (m *resWrangler) GetById(
id resid.ResId) (*resource.Resource, error) { id resid.ResId) (*resource.Resource, error) {
match, err1 := m.GetByOriginalId(id) r, err := demandOneMatch(m.GetMatchingResourcesByAnyId, id, "Id")
if err1 == nil { if err != nil {
return match, nil return nil, fmt.Errorf(
"%s; failed to find unique target for patch %s",
err.Error(), id.GvknString())
} }
match, err2 := m.GetByCurrentId(id) return r, nil
if err2 == nil {
return match, nil
}
return nil, fmt.Errorf(
"%s; %s; failed to find unique target for patch %s",
err1.Error(), err2.Error(), id.GvknString())
} }
type resFinder func(IdMatcher) []*resource.Resource type resFinder func(IdMatcher) []*resource.Resource
@@ -465,10 +465,7 @@ func (m *resWrangler) AbsorbAll(other ResMap) error {
func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error { func (m *resWrangler) appendReplaceOrMerge(res *resource.Resource) error {
id := res.CurId() id := res.CurId()
matches := m.GetMatchingResourcesByOriginalId(id.Equals) matches := m.GetMatchingResourcesByAnyId(id.Equals)
if len(matches) == 0 {
matches = m.GetMatchingResourcesByCurrentId(id.Equals)
}
switch len(matches) { switch len(matches) {
case 0: case 0:
switch res.Behavior() { switch res.Behavior() {
@@ -593,10 +590,8 @@ func (m *resWrangler) ApplySmPatch(
continue continue
} }
patchCopy := patch.DeepCopy() patchCopy := patch.DeepCopy()
patchCopy.SetName(res.GetName()) patchCopy.CopyMergeMetaDataFieldsFrom(patch)
patchCopy.SetNamespace(res.GetNamespace())
patchCopy.SetGvk(res.GetGvk()) patchCopy.SetGvk(res.GetGvk())
patchCopy.SetOriginalName(res.GetOriginalName(), true)
err := res.ApplySmPatch(patchCopy) err := res.ApplySmPatch(patchCopy)
if err != nil { if err != nil {
// Check for an error string from UnmarshalJSON that's indicative // Check for an error string from UnmarshalJSON that's indicative

View File

@@ -331,6 +331,134 @@ func TestGetMatchingResourcesByCurrentId(t *testing.T) {
} }
} }
func TestGetMatchingResourcesByPreviousId(t *testing.T) {
r1 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "new-alice",
"annotations": map[string]interface{}{
"config.kubernetes.io/originalName": "alice",
"config.kubernetes.io/originalNs": "default",
},
},
})
r2 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "new-bob",
"annotations": map[string]interface{}{
"config.kubernetes.io/originalName": "bob,bob2",
"config.kubernetes.io/originalNs": "default,default",
},
},
})
r3 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "new-bob",
"namespace": "new-happy",
"annotations": map[string]interface{}{
"config.kubernetes.io/originalName": "bob",
"config.kubernetes.io/originalNs": "happy",
},
},
})
r4 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": map[string]interface{}{
"name": "charlie",
"namespace": "happy",
"annotations": map[string]interface{}{
"config.kubernetes.io/originalName": "charlie",
"config.kubernetes.io/originalNs": "default",
},
},
})
r5 := rf.FromMap(
map[string]interface{}{
"apiVersion": "v1",
"kind": "Deployment",
"metadata": map[string]interface{}{
"name": "charlie",
"namespace": "happy",
},
})
m := resmaptest_test.NewRmBuilder(t, rf).
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).ResMap()
// nolint:goconst
tests := []struct {
name string
matcher IdMatcher
count int
}{
{
"match everything",
func(resid.ResId) bool { return true },
5,
},
{
"match nothing",
func(resid.ResId) bool { return false },
0,
},
{
"name is alice",
func(x resid.ResId) bool { return x.Name == "alice" },
1,
},
{
"name is charlie",
func(x resid.ResId) bool { return x.Name == "charlie" },
2,
},
{
"name is bob",
func(x resid.ResId) bool { return x.Name == "bob" },
2,
},
{
"happy namespace",
func(x resid.ResId) bool {
return x.Namespace == "happy"
},
3,
},
{
"happy deployment",
func(x resid.ResId) bool {
return x.Namespace == "happy" &&
x.Gvk.Kind == "Deployment"
},
1,
},
{
"happy ConfigMap",
func(x resid.ResId) bool {
return x.Namespace == "happy" &&
x.Gvk.Kind == "ConfigMap"
},
2,
},
}
for _, tst := range tests {
result := m.GetMatchingResourcesByAnyId(tst.matcher)
if len(result) != tst.count {
t.Fatalf("test '%s'; actual: %d, expected: %d",
tst.name, len(result), tst.count)
}
}
}
func TestSubsetThatCouldBeReferencedByResource(t *testing.T) { func TestSubsetThatCouldBeReferencedByResource(t *testing.T) {
r1 := rf.FromMap( r1 := rf.FromMap(
map[string]interface{}{ map[string]interface{}{

View File

@@ -11,6 +11,7 @@ import (
"sigs.k8s.io/kustomize/api/ifc" "sigs.k8s.io/kustomize/api/ifc"
"sigs.k8s.io/kustomize/api/internal/kusterr" "sigs.k8s.io/kustomize/api/internal/kusterr"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/api/types"
) )
@@ -35,17 +36,12 @@ func (rf *Factory) FromMap(m map[string]interface{}) *Resource {
// FromMapWithName returns a new instance with the given "original" name. // FromMapWithName returns a new instance with the given "original" name.
func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource { func (rf *Factory) FromMapWithName(n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).SetOriginalName(n, true) return rf.FromMapWithNamespaceAndName(resid.DefaultNamespace, n, m)
}
// FromMapWithNamespace returns a new instance with the given "original" namespace.
func (rf *Factory) FromMapWithNamespace(n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).SetOriginalNs(n, true)
} }
// FromMapWithNamespaceAndName returns a new instance with the given "original" namespace. // FromMapWithNamespaceAndName returns a new instance with the given "original" namespace.
func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource { func (rf *Factory) FromMapWithNamespaceAndName(ns string, n string, m map[string]interface{}) *Resource {
return rf.makeOne(rf.kf.FromMap(m), nil).SetOriginalNs(ns, true).SetOriginalName(n, true) return rf.makeOne(rf.kf.FromMap(m), nil).setPreviousNamespaceAndName(ns, n)
} }
// FromMapAndOption returns a new instance of Resource with given options. // FromMapAndOption returns a new instance of Resource with given options.
@@ -157,7 +153,7 @@ func (rf *Factory) SliceFromBytesWithNames(names []string, in []byte) ([]*Resour
return nil, fmt.Errorf("number of names doesn't match number of resources") return nil, fmt.Errorf("number of names doesn't match number of resources")
} }
for i, res := range result { for i, res := range result {
res.SetOriginalName(names[i], true) res.setPreviousNamespaceAndName("", names[i])
} }
return result, nil return result, nil
} }

View File

@@ -32,18 +32,29 @@ type Resource struct {
} }
const ( const (
buildAnnotationOriginalName = konfig.ConfigAnnoDomain + "/originalName" buildAnnotationPreviousName = konfig.ConfigAnnoDomain + "/originalName"
buildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes" buildAnnotationPrefixes = konfig.ConfigAnnoDomain + "/prefixes"
buildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes" buildAnnotationSuffixes = konfig.ConfigAnnoDomain + "/suffixes"
buildAnnotationOriginalNamespace = konfig.ConfigAnnoDomain + "/originalNs" buildAnnotationPreviousNamespace = konfig.ConfigAnnoDomain + "/originalNs"
) )
var buildAnnotations = []string{
buildAnnotationPreviousName,
buildAnnotationPrefixes,
buildAnnotationSuffixes,
buildAnnotationPreviousNamespace,
}
func (r *Resource) ResetPrimaryData(incoming *Resource) { func (r *Resource) ResetPrimaryData(incoming *Resource) {
r.kunStr = incoming.Copy() r.kunStr = incoming.Copy()
} }
func (r *Resource) GetAnnotations() map[string]string { func (r *Resource) GetAnnotations() map[string]string {
return r.kunStr.GetAnnotations() annotations := r.kunStr.GetAnnotations()
if annotations == nil {
return make(map[string]string)
}
return annotations
} }
func (r *Resource) Copy() ifc.Kunstructured { func (r *Resource) Copy() ifc.Kunstructured {
@@ -146,8 +157,6 @@ func (r *Resource) UnmarshalJSON(s []byte) error {
type ResCtx interface { type ResCtx interface {
AddNamePrefix(p string) AddNamePrefix(p string)
AddNameSuffix(s string) AddNameSuffix(s string)
GetOutermostNamePrefix() string
GetOutermostNameSuffix() string
GetNamePrefixes() []string GetNamePrefixes() []string
GetNameSuffixes() []string GetNameSuffixes() []string
} }
@@ -252,22 +261,19 @@ func copyStringSlice(s []string) []string {
// Implements ResCtx AddNamePrefix // Implements ResCtx AddNamePrefix
func (r *Resource) AddNamePrefix(p string) { func (r *Resource) AddNamePrefix(p string) {
r.addAdditiveAnnotation(buildAnnotationPrefixes, p) r.appendCsvAnnotation(buildAnnotationPrefixes, p)
} }
// Implements ResCtx AddNameSuffix // Implements ResCtx AddNameSuffix
func (r *Resource) AddNameSuffix(s string) { func (r *Resource) AddNameSuffix(s string) {
r.addAdditiveAnnotation(buildAnnotationSuffixes, s) r.appendCsvAnnotation(buildAnnotationSuffixes, s)
} }
func (r *Resource) addAdditiveAnnotation(name, value string) { func (r *Resource) appendCsvAnnotation(name, value string) {
if value == "" { if value == "" {
return return
} }
annotations := r.GetAnnotations() annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
if existing, ok := annotations[name]; ok { if existing, ok := annotations[name]; ok {
annotations[name] = existing + "," + value annotations[name] = existing + "," + value
} else { } else {
@@ -312,26 +318,20 @@ func SameEndingSubarray(shortest, longest []string) bool {
// Implements ResCtx GetNamePrefixes // Implements ResCtx GetNamePrefixes
func (r *Resource) GetNamePrefixes() []string { func (r *Resource) GetNamePrefixes() []string {
annotations := r.GetAnnotations() return r.getCsvAnnotation(buildAnnotationPrefixes)
if _, ok := annotations[buildAnnotationPrefixes]; !ok {
return nil
}
return strings.Split(annotations[buildAnnotationPrefixes], ",")
} }
// Implements ResCtx GetNameSuffixes // Implements ResCtx GetNameSuffixes
func (r *Resource) GetNameSuffixes() []string { func (r *Resource) GetNameSuffixes() []string {
annotations := r.GetAnnotations() return r.getCsvAnnotation(buildAnnotationSuffixes)
if _, ok := annotations[buildAnnotationSuffixes]; !ok {
return nil
}
return strings.Split(annotations[buildAnnotationSuffixes], ",")
} }
// OutermostPrefixSuffixEquals returns true if both resources func (r *Resource) getCsvAnnotation(name string) []string {
// outer suffix and prefix matches. annotations := r.GetAnnotations()
func (r *Resource) OutermostPrefixSuffixEquals(o ResCtx) bool { if _, ok := annotations[name]; !ok {
return (r.GetOutermostNamePrefix() == o.GetOutermostNamePrefix()) && (r.GetOutermostNameSuffix() == o.GetOutermostNameSuffix()) return nil
}
return strings.Split(annotations[name], ",")
} }
// PrefixesSuffixesEquals is conceptually doing the same task // PrefixesSuffixesEquals is conceptually doing the same task
@@ -349,57 +349,20 @@ func (r *Resource) RemoveBuildAnnotations() {
if len(annotations) == 0 { if len(annotations) == 0 {
return return
} }
delete(annotations, buildAnnotationOriginalName) for _, a := range buildAnnotations {
delete(annotations, buildAnnotationPrefixes) delete(annotations, a)
delete(annotations, buildAnnotationSuffixes)
delete(annotations, buildAnnotationOriginalNamespace)
r.SetAnnotations(annotations)
}
func (r *Resource) GetOriginalName() string {
annotations := r.GetAnnotations()
if name, ok := annotations[buildAnnotationOriginalName]; ok {
return name
}
return r.kunStr.GetName()
}
func (r *Resource) SetOriginalName(n string, overwrite bool) *Resource {
annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
if _, ok := annotations[buildAnnotationOriginalName]; !ok || overwrite {
annotations[buildAnnotationOriginalName] = n
}
r.kunStr.SetAnnotations(annotations)
return r
}
func (r *Resource) GetOriginalNs() string {
annotations := r.GetAnnotations()
if ns, ok := annotations[buildAnnotationOriginalNamespace]; ok {
return ns
}
ns := r.GetNamespace()
if ns == "default" {
return ""
}
return ns
}
func (r *Resource) SetOriginalNs(n string, overwrite bool) *Resource {
if n == "" {
n = "default"
}
annotations := r.GetAnnotations()
if annotations == nil {
annotations = make(map[string]string)
}
if _, ok := annotations[buildAnnotationOriginalNamespace]; !ok || overwrite {
annotations[buildAnnotationOriginalNamespace] = n
} }
r.SetAnnotations(annotations) r.SetAnnotations(annotations)
}
func (r *Resource) setPreviousNamespaceAndName(ns string, n string) *Resource {
// name
r.appendCsvAnnotation(buildAnnotationPreviousName, n)
// namespace
if ns == "" {
ns = resid.DefaultNamespace
}
r.appendCsvAnnotation(buildAnnotationPreviousNamespace, ns)
return r return r
} }
@@ -456,10 +419,36 @@ func (r *Resource) GetNamespace() string {
// OrgId returns the original, immutable ResId for the resource. // OrgId returns the original, immutable ResId for the resource.
// This doesn't have to be unique in a ResMap. // This doesn't have to be unique in a ResMap.
// TODO: compute this once and save it in the resource.
func (r *Resource) OrgId() resid.ResId { func (r *Resource) OrgId() resid.ResId {
return resid.NewResIdWithNamespace( ids := r.PrevIds()
r.GetGvk(), r.GetOriginalName(), r.GetOriginalNs()) if len(ids) > 0 {
return ids[0]
}
return r.CurId()
}
// PrevIds returns a list of ResIds that includes every
// previous ResId the resource has had through all of its
// GVKN transformations, in the order that it had that ID.
// The returned array does not include the resource's current
// ID.
// If there are no previous IDs, this will return nil.
func (r *Resource) PrevIds() []resid.ResId {
var ids []resid.ResId
// names and ns should always be the same length
names := r.getCsvAnnotation(buildAnnotationPreviousName)
ns := r.getCsvAnnotation(buildAnnotationPreviousNamespace)
for i := range names {
ids = append(ids, resid.NewResIdWithNamespace(
r.GetGvk(), names[i], ns[i]))
}
return ids
}
// StorePreviousId stores the resource's current ID via build annotations.
func (r *Resource) StorePreviousId() {
r.setPreviousNamespaceAndName(r.GetNamespace(), r.GetName())
} }
// CurId returns a ResId for the resource using the // CurId returns a ResId for the resource using the

View File

@@ -695,322 +695,165 @@ spec:
} }
} }
func TestSetOriginalNameAndNs(t *testing.T) { func TestResource_StorePreviousId(t *testing.T) {
input := `apiVersion: apps/v1 tests := map[string]struct {
input string
newName string
newNs string
expected string
}{
"default namespace, first previous name": {
input: `apiVersion: apps/v1
kind: Secret kind: Secret
metadata: metadata:
name: newName` name: oldName
`,
factory := provider.NewDefaultDepProvider().GetResourceFactory() newName: "newName",
resources, err := factory.SliceFromBytes([]byte(input)) newNs: "",
if err != nil { expected: `apiVersion: apps/v1
t.Fatal(err)
}
res := resources[0]
res.SetOriginalName("oldName", false)
res.SetOriginalNs("default", false)
expected := `apiVersion: apps/v1
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/originalName: oldName config.kubernetes.io/originalName: oldName
config.kubernetes.io/originalNs: default config.kubernetes.io/originalNs: default
name: newName name: newName
` `,
bytes, err := res.AsYAML() },
if err != nil {
t.Fatal(err) "default namespace, second previous name": {
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
config.kubernetes.io/originalNs: default
name: oldName2
`,
newName: "newName",
newNs: "",
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName,oldName2
config.kubernetes.io/originalNs: default,default
name: newName
`,
},
"non-default namespace": {
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
config.kubernetes.io/originalNs: default
name: oldName2
namespace: oldNamespace
`,
newName: "newName",
newNs: "newNamespace",
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName,oldName2
config.kubernetes.io/originalNs: default,oldNamespace
name: newName
namespace: newNamespace
`,
},
}
factory := provider.NewDefaultDepProvider().GetResourceFactory()
for i := range tests {
test := tests[i]
t.Run(i, func(t *testing.T) {
resources, err := factory.SliceFromBytes([]byte(test.input))
if !assert.NoError(t, err) || len(resources) == 0 {
t.FailNow()
}
r := resources[0]
r.StorePreviousId()
r.SetName(test.newName)
if test.newNs != "" {
r.SetNamespace(test.newNs)
}
bytes, err := r.AsYAML()
if !assert.NoError(t, err) {
t.FailNow()
}
assert.Equal(t, test.expected, string(bytes))
})
} }
assert.Equal(t, expected, string(bytes))
} }
func TestGetOriginalName(t *testing.T) { func TestResource_PrevIds(t *testing.T) {
tests := []struct { tests := map[string]struct {
input string input string
expected string expected []resid.ResId
}{ }{
{ "no previous IDs": {
// no name annotation, return the name
input: `apiVersion: apps/v1 input: `apiVersion: apps/v1
kind: Secret kind: Secret
metadata: metadata:
name: mySecret`, name: name
expected: "mySecret", `,
expected: nil,
}, },
{ "one previous ID": {
// return name from name annotation
input: `apiVersion: apps/v1 input: `apiVersion: apps/v1
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/originalName: oldName config.kubernetes.io/originalName: oldName
name: newName`, config.kubernetes.io/originalNs: default
expected: "oldName",
},
}
for _, test := range tests {
factory := provider.NewDefaultDepProvider().GetResourceFactory()
resources, err := factory.SliceFromBytes([]byte(test.input))
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expected, resources[0].GetOriginalName())
}
}
func TestSetOriginalName(t *testing.T) {
tests := []struct {
input string
originalName string
overwrite bool
expected string
}{
{
// no original name set, overwrite is false
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: newName`,
originalName: "oldName",
overwrite: false,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName name: newName
`, `,
expected: []resid.ResId{
{
Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
Name: "oldName",
Namespace: resid.DefaultNamespace,
},
},
}, },
{ "two ids": {
// no original name set, overwrite is true
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: newName`,
originalName: "oldName",
overwrite: true,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName
`,
},
{
// original name is set, overwrite is false, resource shouldn't change
input: `apiVersion: apps/v1 input: `apiVersion: apps/v1
kind: Secret kind: Secret
metadata: metadata:
annotations: annotations:
config.kubernetes.io/originalName: oldName config.kubernetes.io/originalName: oldName,oldName2
name: newName`, config.kubernetes.io/originalNs: default,oldNamespace
originalName: "newOriginalName",
overwrite: false,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName
`,
},
{
// original name is set, overwrite is true, resource should change
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: oldName
name: newName`,
originalName: "newOriginalName",
overwrite: true,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalName: newOriginalName
name: newName
`,
},
}
for _, test := range tests {
factory := provider.NewDefaultDepProvider().GetResourceFactory()
resources, err := factory.SliceFromBytes([]byte(test.input))
if err != nil {
t.Fatal(err)
}
res := resources[0]
res.SetOriginalName(test.originalName, test.overwrite)
bytes, err := res.AsYAML()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expected, string(bytes))
}
}
func TestGetOriginalNs(t *testing.T) {
tests := []struct {
input string
expected string
}{
{
// no namespace, return default
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: mySecret`,
expected: "",
},
{
// return old namespace
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: mySecret
namespace: myNamespace`,
expected: "oldNamespace",
},
{
// return namespace
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: mySecret
namespace: myNamespace`,
expected: "myNamespace",
},
}
for _, test := range tests {
factory := provider.NewDefaultDepProvider().GetResourceFactory()
resources, err := factory.SliceFromBytes([]byte(test.input))
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expected, resources[0].GetOriginalNs())
}
}
func TestSetOriginalNs(t *testing.T) {
tests := []struct {
input string
originalNs string
overwrite bool
expected string
}{
{
// no original namespace set, overwrite is false
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: newName
namespace: newNamespace`,
originalNs: "oldNamespace",
overwrite: false,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace
`,
},
{
// no original name set, overwrite is true
input: `apiVersion: apps/v1
kind: Secret
metadata:
name: newName
namespace: newNamespace`,
originalNs: "oldNamespace",
overwrite: true,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace
`,
},
{
// original name is set, overwrite is false, resource shouldn't change
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace`,
originalNs: "newOriginalNamespace",
overwrite: false,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace
`,
},
{
// original name is set, overwrite is true, resource should change
input: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: oldNamespace
name: newName
namespace: newNamespace`,
originalNs: "newOriginalNamespace",
overwrite: true,
expected: `apiVersion: apps/v1
kind: Secret
metadata:
annotations:
config.kubernetes.io/originalNs: newOriginalNamespace
name: newName name: newName
namespace: newNamespace namespace: newNamespace
`, `,
expected: []resid.ResId{
{
Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
Name: "oldName",
Namespace: resid.DefaultNamespace,
},
{
Gvk: resid.Gvk{Group: "apps", Version: "v1", Kind: "Secret"},
Name: "oldName2",
Namespace: "oldNamespace",
},
},
}, },
} }
factory := provider.NewDefaultDepProvider().GetResourceFactory()
for _, test := range tests { for i := range tests {
factory := provider.NewDefaultDepProvider().GetResourceFactory() test := tests[i]
resources, err := factory.SliceFromBytes([]byte(test.input)) t.Run(i, func(t *testing.T) {
if err != nil { resources, err := factory.SliceFromBytes([]byte(test.input))
t.Fatal(err) if !assert.NoError(t, err) || len(resources) == 0 {
} t.FailNow()
}
res := resources[0] r := resources[0]
res.SetOriginalNs(test.originalNs, test.overwrite) assert.Equal(t, test.expected, r.PrevIds())
})
bytes, err := res.AsYAML()
if err != nil {
t.Fatal(err)
}
assert.Equal(t, test.expected, string(bytes))
} }
} }

View File

@@ -7,6 +7,7 @@ import (
"testing" "testing"
"sigs.k8s.io/kustomize/api/provider" "sigs.k8s.io/kustomize/api/provider"
"sigs.k8s.io/kustomize/api/resid"
"sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resmap"
"sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/resource"
) )
@@ -48,15 +49,7 @@ func (rm *rmBuilder) AddR(r *resource.Resource) *rmBuilder {
} }
func (rm *rmBuilder) AddWithName(n string, m map[string]interface{}) *rmBuilder { func (rm *rmBuilder) AddWithName(n string, m map[string]interface{}) *rmBuilder {
err := rm.m.Append(rm.rf.FromMapWithName(n, m)) err := rm.m.Append(rm.rf.FromMapWithNamespaceAndName(resid.DefaultNamespace, n, m))
if err != nil {
rm.t.Fatalf("test setup failure: %v", err)
}
return rm
}
func (rm *rmBuilder) AddWithNs(ns string, m map[string]interface{}) *rmBuilder {
err := rm.m.Append(rm.rf.FromMapWithNamespace(ns, m))
if err != nil { if err != nil {
rm.t.Fatalf("test setup failure: %v", err) rm.t.Fatalf("test setup failure: %v", err)
} }

View File

@@ -32,7 +32,7 @@ func (p *plugin) Transform(m resmap.ResMap) error {
if err != nil { if err != nil {
return err return err
} }
res.SetOriginalName(res.GetName(), false) res.StorePreviousId()
res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h)) res.SetName(fmt.Sprintf("%s-%s", res.GetName(), h))
} }
} }

View File

@@ -38,7 +38,7 @@ func (p *plugin) Transform(m resmap.ResMap) error {
// Don't mutate empty objects? // Don't mutate empty objects?
continue continue
} }
r.SetOriginalNs(r.GetNamespace(), false) r.StorePreviousId()
err := r.ApplyFilter(namespace.Filter{ err := r.ApplyFilter(namespace.Filter{
Namespace: p.Namespace, Namespace: p.Namespace,
FsSlice: p.FieldSpecs, FsSlice: p.FieldSpecs,

View File

@@ -108,7 +108,7 @@ func (p *plugin) transformJson6902(m resmap.ResMap, patch jsonpatch.Patch) error
return err return err
} }
for _, res := range resources { for _, res := range resources {
res.SetOriginalName(res.GetName(), false) res.StorePreviousId()
err = res.ApplyFilter(patchjson6902.Filter{ err = res.ApplyFilter(patchjson6902.Filter{
Patch: p.Patch, Patch: p.Patch,
}) })

View File

@@ -73,7 +73,7 @@ func (p *plugin) Transform(m resmap.ResMap) error {
r.AddNamePrefix(p.Prefix) r.AddNamePrefix(p.Prefix)
r.AddNameSuffix(p.Suffix) r.AddNameSuffix(p.Suffix)
if p.Prefix != "" || p.Suffix != "" { if p.Prefix != "" || p.Suffix != "" {
r.SetOriginalName(r.GetName(), false) r.StorePreviousId()
} }
} }
err := r.ApplyFilter(prefixsuffix.Filter{ err := r.ApplyFilter(prefixsuffix.Filter{

View File

@@ -64,6 +64,7 @@ kind: Service
metadata: metadata:
annotations: annotations:
config.kubernetes.io/originalName: apple config.kubernetes.io/originalName: apple
config.kubernetes.io/originalNs: default
config.kubernetes.io/prefixes: baked- config.kubernetes.io/prefixes: baked-
config.kubernetes.io/suffixes: -pie config.kubernetes.io/suffixes: -pie
name: baked-apple-pie name: baked-apple-pie
@@ -86,6 +87,7 @@ kind: ConfigMap
metadata: metadata:
annotations: annotations:
config.kubernetes.io/originalName: cm config.kubernetes.io/originalName: cm
config.kubernetes.io/originalNs: default
config.kubernetes.io/prefixes: baked- config.kubernetes.io/prefixes: baked-
config.kubernetes.io/suffixes: -pie config.kubernetes.io/suffixes: -pie
name: baked-cm-pie name: baked-cm-pie
@@ -136,6 +138,7 @@ kind: Deployment
metadata: metadata:
annotations: annotations:
config.kubernetes.io/originalName: deployment config.kubernetes.io/originalName: deployment
config.kubernetes.io/originalNs: default
config.kubernetes.io/prefixes: test- config.kubernetes.io/prefixes: test-
name: test-deployment name: test-deployment
spec: spec:

View File

@@ -35,9 +35,7 @@ func (p *plugin) Transform(m resmap.ResMap) error {
found := false found := false
for _, fs := range p.FieldSpecs { for _, fs := range p.FieldSpecs {
matcher := p.createMatcher(fs) matcher := p.createMatcher(fs)
matchOriginal := m.GetMatchingResourcesByOriginalId(matcher) resList := m.GetMatchingResourcesByAnyId(matcher)
resList := append(
matchOriginal, m.GetMatchingResourcesByCurrentId(matcher)...)
if len(resList) > 0 { if len(resList) > 0 {
found = true found = true
for _, r := range resList { for _, r := range resList {