mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-13 01:50:55 +00:00
Simplify code base.
- In ResMap, drop concept of internal Id to Resource map. The ResMap is now (just) a list, allowing only very particular edits. - Resources should now be maintained in the order loaded. A later PR can adjust tests to remove the internal legacy sorting, and confirm order-out is predictable from order-in. The PR would suppress the sort in tests, and reorder the output to make all tests pass again, and confirm that the new order matched depth-first input traversal. The FromMap fixture function was removed from all test inputs to establish a predictable input order. - Resources now have two 'Ids', OriginalId and CurrentId. The former is fixed as GVK-name-namespace at load time, the latter changes during transformations. The latter can be used to narrow name references when the former maps to multiple resources. We allow bases to be loaded more than once in a build (a diamond pattern), so the OriginalId is not unique across the resources set. The CurrentId is (and must be) unique, but is constantly mutating. Failing to make this distinction clear, and attempting to maintain a mapping from a single mutating Id to a resource was making the code too complex. - Drop prefix/suffix from ResId - the ResId is now immutable. A later PR can remove the distinction with ItemId. - This PR increases coverage of ResMap is since this is a large refactor. Higher level tests didn't need much change outside reordering of results at the resource level.
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"sigs.k8s.io/kustomize/internal/kusterr"
|
||||
"sigs.k8s.io/kustomize/pkg/ifc"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
)
|
||||
@@ -63,29 +62,6 @@ func (rmF *Factory) NewResMapFromBytes(b []byte) (ResMap, error) {
|
||||
return newResMapFromResourceSlice(resources)
|
||||
}
|
||||
|
||||
// Deprecated.
|
||||
// FromMap returns a ResMap with arbitrary internal ordering,
|
||||
// panicing on error. For tests only.
|
||||
// See also ErrorIfNotEqualSets.
|
||||
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
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// NewResMapFromConfigMapArgs returns a Resource slice given
|
||||
// a configmap metadata slice from kustomization file.
|
||||
func (rmF *Factory) NewResMapFromConfigMapArgs(
|
||||
|
||||
@@ -69,7 +69,7 @@ metadata:
|
||||
if m.Size() != 3 {
|
||||
t.Fatalf("result should contain 3, but got %d", m.Size())
|
||||
}
|
||||
if err := expected.ErrorIfNotEqualSets(m); err != nil {
|
||||
if err := expected.ErrorIfNotEqualLists(m); err != nil {
|
||||
t.Fatalf("actual doesn't match expected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,8 +30,8 @@ var _ sort.Interface = IdSlice{}
|
||||
func (a IdSlice) Len() int { return len(a) }
|
||||
func (a IdSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a IdSlice) Less(i, j int) bool {
|
||||
if !a[i].Gvk().Equals(a[j].Gvk()) {
|
||||
return a[i].Gvk().IsLessThan(a[j].Gvk())
|
||||
if !a[i].Gvk.Equals(a[j].Gvk) {
|
||||
return a[i].Gvk.IsLessThan(a[j].Gvk)
|
||||
}
|
||||
return a[i].String() < a[j].String()
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ package resmap
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
"sigs.k8s.io/kustomize/pkg/resource"
|
||||
"sigs.k8s.io/kustomize/pkg/types"
|
||||
@@ -17,53 +17,17 @@ import (
|
||||
)
|
||||
|
||||
// ResMap is an interface describing operations on the
|
||||
// core kustomize data structure.
|
||||
// core kustomize data structure, a list of Resources.
|
||||
//
|
||||
// TODO: delete the commentary below when/if the issues
|
||||
// discussed are addressed.
|
||||
// Every Resource has two ResIds: OriginalId and CurId.
|
||||
//
|
||||
// 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.
|
||||
// A ResId is a tuple of {Namespace, Group, Version, Kind, Name}.
|
||||
//
|
||||
// The old (bare map) ResMap had pervasive problems:
|
||||
// In a ResMap, no two resources may have the same CurId,
|
||||
// but they may have the same OriginalId. The latter can happen
|
||||
// when mixing two or more different overlays apply different
|
||||
// transformations to a common base.
|
||||
//
|
||||
// * 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
|
||||
@@ -73,17 +37,12 @@ type ResMap interface {
|
||||
// as appended.
|
||||
Resources() []*resource.Resource
|
||||
|
||||
// Append adds a Resource, automatically computing its
|
||||
// associated Id.
|
||||
// Error on Id collision.
|
||||
// Append adds a Resource.
|
||||
// Error on OrgId collision.
|
||||
Append(*resource.Resource) error
|
||||
|
||||
// AppendWithId adds a Resource with the given Id.
|
||||
// Error on Id collision.
|
||||
AppendWithId(resid.ResId, *resource.Resource) error
|
||||
|
||||
// AppendAll appends another ResMap to self,
|
||||
// failing on any Id collision.
|
||||
// failing on any OrgId collision.
|
||||
AppendAll(ResMap) error
|
||||
|
||||
// AbsorbAll appends, replaces or merges the contents
|
||||
@@ -99,49 +58,56 @@ type ResMap interface {
|
||||
// self, then its behavior _cannot_ be merge or replace.
|
||||
AbsorbAll(ResMap) error
|
||||
|
||||
// AsMap returns (ResId, *Resource) pairs in
|
||||
// arbitrary order via a generated 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
|
||||
|
||||
// AsYaml returns the yaml form of resources.
|
||||
AsYaml() ([]byte, error)
|
||||
|
||||
// Gets the resource with the given Id, else nil.
|
||||
GetById(resid.ResId) *resource.Resource
|
||||
// GetByIndex returns a resource at the given index,
|
||||
// nil if out of range.
|
||||
GetByIndex(int) *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
|
||||
// GetIndexOfCurrentId returns the index of the resource
|
||||
// with the given CurId.
|
||||
// Returns error if there is more than one match.
|
||||
// Returns (-1, nil) if there is no match.
|
||||
GetIndexOfCurrentId(id resid.ResId) (int, error)
|
||||
|
||||
// AllIds returns all known Ids.
|
||||
// Result order is arbitrary.
|
||||
// GetMatchingResourcesByCurrentId returns the resources
|
||||
// who's CurId is matched by the argument.
|
||||
GetMatchingResourcesByCurrentId(matches IdMatcher) []*resource.Resource
|
||||
|
||||
// GetMatchingResourcesByOriginalId returns the resources
|
||||
// who's OriginalId is matched by the argument.
|
||||
GetMatchingResourcesByOriginalId(matches IdMatcher) []*resource.Resource
|
||||
|
||||
// GetByCurrentId is shorthand for calling
|
||||
// GetMatchingResourcesByCurrentId with a matcher requiring
|
||||
// an exact match, returning an error on multiple or no matches.
|
||||
GetByCurrentId(resid.ResId) (*resource.Resource, error)
|
||||
|
||||
// GetByOriginalId is shorthand for calling
|
||||
// GetMatchingResourcesByOriginalId with a matcher requiring
|
||||
// an exact match, returning an error on multiple or no matches.
|
||||
GetByOriginalId(resid.ResId) (*resource.Resource, error)
|
||||
|
||||
// Deprecated.
|
||||
// Same as GetByOriginalId.
|
||||
GetById(resid.ResId) (*resource.Resource, error)
|
||||
|
||||
// AllIds returns all CurrentIds.
|
||||
AllIds() []resid.ResId
|
||||
|
||||
// GetMatchingIds returns a slice of Ids that
|
||||
// satisfy the given matcher function.
|
||||
// Result order is arbitrary.
|
||||
GetMatchingIds(IdMatcher) []resid.ResId
|
||||
// Replace replaces the resource with the matching CurId.
|
||||
// Error if there's no match or more than one match.
|
||||
// Returns the index where the replacement happened.
|
||||
Replace(*resource.Resource) (int, error)
|
||||
|
||||
// Remove removes the Id and the resource it points to.
|
||||
// Remove removes the resource whose CurId matches the argument.
|
||||
// Error if not found.
|
||||
Remove(resid.ResId) error
|
||||
|
||||
// Clear removes all resources and Ids.
|
||||
Clear()
|
||||
|
||||
// SubsetThatCouldBeReferencedById returns a ResMap subset
|
||||
// of self with resources that could be referenced by the
|
||||
// resource represented by the argument Id.
|
||||
// This is a filter; it excludes things that cannot be
|
||||
// referenced by the Id's resource, e.g. objects in other
|
||||
// namespaces. Cluster wide objects are never excluded.
|
||||
SubsetThatCouldBeReferencedById(resid.ResId) ResMap
|
||||
|
||||
// SubsetThatCouldBeReferencedByResource returns a ResMap subset
|
||||
// of self with resources that could be referenced by the
|
||||
// resource argument.
|
||||
@@ -158,8 +124,8 @@ type ResMap interface {
|
||||
ShallowCopy() ResMap
|
||||
|
||||
// ErrorIfNotEqualSets returns an error if the
|
||||
// argument doesn't have the same Ids and resource
|
||||
// data as self. Ordering is _not_ taken into account,
|
||||
// argument doesn't have the same resources 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
|
||||
@@ -174,7 +140,7 @@ type ResMap interface {
|
||||
// data as self, in the same order.
|
||||
// Meta information is ignored; this is similar
|
||||
// to comparing the AsYaml() strings, but allows
|
||||
// for printing pointers, etc.
|
||||
// for more informed errors on not equals.
|
||||
ErrorIfNotEqualLists(ResMap) error
|
||||
|
||||
// Debug prints the ResMap.
|
||||
@@ -190,14 +156,6 @@ type resWrangler struct {
|
||||
// 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 {
|
||||
@@ -209,14 +167,10 @@ func newOne() *resWrangler {
|
||||
// Clear implements ResMap.
|
||||
func (m *resWrangler) Clear() {
|
||||
m.rList = nil
|
||||
m.rIndex = make(map[resid.ResId]int)
|
||||
}
|
||||
|
||||
// Size implements ResMap.
|
||||
func (m *resWrangler) Size() int {
|
||||
if len(m.rList) != len(m.rIndex) {
|
||||
panic("class size invariant violation")
|
||||
}
|
||||
return len(m.rList)
|
||||
}
|
||||
|
||||
@@ -236,94 +190,51 @@ func (m *resWrangler) Resources() []*resource.Resource {
|
||||
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)
|
||||
id := res.CurId()
|
||||
if r := m.GetMatchingResourcesByCurrentId(id.Equals); len(r) > 0 {
|
||||
return fmt.Errorf(
|
||||
"may not add resource with an already registered id: %s", id)
|
||||
}
|
||||
m.rList = append(m.rList, res)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
for _, r := range m.rList {
|
||||
if r.CurId() != adios {
|
||||
tmp.Append(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)
|
||||
// Replace implements ResMap.
|
||||
func (m *resWrangler) Replace(res *resource.Resource) (int, error) {
|
||||
id := res.CurId()
|
||||
i, err := m.GetIndexOfCurrentId(id)
|
||||
if err != nil {
|
||||
return -1, errors.Wrap(err, "in Replace")
|
||||
}
|
||||
i := m.indexOfResource(res)
|
||||
if i >= 0 {
|
||||
return fmt.Errorf(
|
||||
"attempt to add res %s that is already held",
|
||||
res)
|
||||
if i < 0 {
|
||||
return -1, fmt.Errorf("cannot find resource with id %s to replace", id)
|
||||
}
|
||||
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
|
||||
m.rList[i] = res
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// 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++
|
||||
for i, r := range m.rList {
|
||||
ids[i] = r.CurId()
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -338,7 +249,7 @@ func (m *resWrangler) Debug(title string) {
|
||||
} else {
|
||||
fmt.Println("---")
|
||||
}
|
||||
fmt.Printf("# %d %s\n", i, m.debugIdMappingToIndex(i))
|
||||
fmt.Printf("# %d %s\n", i, r.OrgId())
|
||||
blob, err := yaml.Marshal(r.Map())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -347,45 +258,91 @@ func (m *resWrangler) Debug(title string) {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
// GetMatchingIds implements ResMap.
|
||||
func (m *resWrangler) GetMatchingIds(matches IdMatcher) []resid.ResId {
|
||||
var result []resid.ResId
|
||||
for id := range m.rIndex {
|
||||
if matches(id) {
|
||||
result = append(result, id)
|
||||
// GetByIndex implements ResMap.
|
||||
func (m *resWrangler) GetByIndex(i int) *resource.Resource {
|
||||
if i < 0 || i >= m.Size() {
|
||||
return nil
|
||||
}
|
||||
return m.rList[i]
|
||||
}
|
||||
|
||||
// GetIndexOfCurrentId implements ResMap.
|
||||
func (m *resWrangler) GetIndexOfCurrentId(id resid.ResId) (int, error) {
|
||||
count := 0
|
||||
result := -1
|
||||
for i, r := range m.rList {
|
||||
if id.Equals(r.CurId()) {
|
||||
count++
|
||||
result = i
|
||||
}
|
||||
}
|
||||
if count > 1 {
|
||||
return -1, fmt.Errorf("id matched %d resources", count)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
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() }
|
||||
|
||||
// GetMatchingResourcesByCurrentId implements ResMap.
|
||||
func (m *resWrangler) GetMatchingResourcesByCurrentId(
|
||||
matches IdMatcher) []*resource.Resource {
|
||||
return m.filteredById(matches, GetCurrentId)
|
||||
}
|
||||
|
||||
// GetMatchingResourcesByOriginalId implements ResMap.
|
||||
func (m *resWrangler) GetMatchingResourcesByOriginalId(
|
||||
matches IdMatcher) []*resource.Resource {
|
||||
return m.filteredById(matches, GetOriginalId)
|
||||
}
|
||||
|
||||
func (m *resWrangler) filteredById(
|
||||
matches IdMatcher, idGetter IdFromResource) []*resource.Resource {
|
||||
var result []*resource.Resource
|
||||
for _, r := range m.rList {
|
||||
if matches(idGetter(r)) {
|
||||
result = append(result, r)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// GetByCurrentId implements ResMap.
|
||||
func (m *resWrangler) GetByCurrentId(
|
||||
id resid.ResId) (*resource.Resource, error) {
|
||||
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")
|
||||
}
|
||||
|
||||
type resFinder func(IdMatcher) []*resource.Resource
|
||||
|
||||
func demandOneMatch(
|
||||
f resFinder, id resid.ResId, s string) (*resource.Resource, error) {
|
||||
r := f(id.Equals)
|
||||
if len(r) == 1 {
|
||||
return r[0], nil
|
||||
}
|
||||
if len(r) > 1 {
|
||||
return nil, fmt.Errorf("multiple matches for %sId %s", s, id)
|
||||
}
|
||||
return nil, fmt.Errorf("no matches for %sId %s", s, id)
|
||||
}
|
||||
|
||||
// GetById implements ResMap.
|
||||
func (m *resWrangler) GetById(id resid.ResId) (*resource.Resource, error) {
|
||||
return m.GetByCurrentId(id)
|
||||
}
|
||||
|
||||
// AsYaml implements ResMap.
|
||||
func (m *resWrangler) AsYaml() ([]byte, error) {
|
||||
firstObj := true
|
||||
@@ -421,17 +378,28 @@ func (m *resWrangler) ErrorIfNotEqualSets(other ResMap) error {
|
||||
"lists have different number of entries: %#v doesn't equal %#v",
|
||||
m.rList, m2.rList)
|
||||
}
|
||||
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)
|
||||
seen := make(map[int]bool)
|
||||
for _, r1 := range m.rList {
|
||||
id := r1.CurId()
|
||||
others := m2.GetMatchingResourcesByCurrentId(id.Equals)
|
||||
if len(others) < 0 {
|
||||
return fmt.Errorf(
|
||||
"id in self missing from other; id: %s", id)
|
||||
}
|
||||
if len(others) > 1 {
|
||||
return fmt.Errorf(
|
||||
"id in self matches %d in other; id: %s", len(others), id)
|
||||
}
|
||||
r2 := others[0]
|
||||
if !r1.KunstructEqual(r2) {
|
||||
return fmt.Errorf(
|
||||
"kuns equal mismatch: \n -- %s,\n -- %s\n\n--\n%#v\n------\n%#v\n",
|
||||
"kunstruct not equal: \n -- %s,\n -- %s\n\n--\n%#v\n------\n%#v\n",
|
||||
r1, r2, r1, r2)
|
||||
}
|
||||
seen[m2.indexOfResource(r2)] = true
|
||||
}
|
||||
if len(seen) != m.Size() {
|
||||
return fmt.Errorf("counting problem %d != %d", len(seen), m.Size())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -449,10 +417,10 @@ func (m *resWrangler) ErrorIfNotEqualLists(other ResMap) error {
|
||||
}
|
||||
for i, r1 := range m.rList {
|
||||
r2 := m2.rList[i]
|
||||
if !r1.KunstructEqual(r2) {
|
||||
if !r1.Equals(r2) {
|
||||
return fmt.Errorf(
|
||||
"Item i=%d differs:\n n1 = %s\n n2 = %s\n",
|
||||
i, r1.Id(), r2.Id())
|
||||
"Item i=%d differs:\n n1 = %s\n n2 = %s\n o1 = %s\n o2 = %s\n",
|
||||
i, r1.OrgId(), r2.OrgId(), r1, r2)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -479,34 +447,9 @@ func (m *resWrangler) DeepCopy() ResMap {
|
||||
// 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("corrupt index map")
|
||||
}
|
||||
result.rIndex[id] = i
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SubsetThatCouldBeReferencedById implements ResMap.
|
||||
func (m *resWrangler) SubsetThatCouldBeReferencedById(inputId resid.ResId) ResMap {
|
||||
if inputId.Gvk().IsClusterKind() {
|
||||
return m
|
||||
}
|
||||
result := New()
|
||||
for id, i := range m.rIndex {
|
||||
if id.Gvk().IsClusterKind() || id.Namespace() == inputId.Namespace() &&
|
||||
id.HasSameLeftmostPrefix(inputId) &&
|
||||
id.HasSameRightmostSuffix(inputId) {
|
||||
err := result.AppendWithId(id, m.rList[i])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -514,13 +457,13 @@ func (m *resWrangler) SubsetThatCouldBeReferencedById(inputId resid.ResId) ResMa
|
||||
// SubsetThatCouldBeReferencedByResource implements ResMap.
|
||||
func (m *resWrangler) SubsetThatCouldBeReferencedByResource(
|
||||
inputRes *resource.Resource) ResMap {
|
||||
inputId := inputRes.Id()
|
||||
if inputId.Gvk().IsClusterKind() {
|
||||
inputId := inputRes.OrgId()
|
||||
if inputId.IsClusterKind() {
|
||||
return m
|
||||
}
|
||||
result := New()
|
||||
for _, r := range m.Resources() {
|
||||
if r.Id().Gvk().IsClusterKind() || inputRes.InSameFuzzyNamespace(r) {
|
||||
if r.OrgId().IsClusterKind() || inputRes.InSameFuzzyNamespace(r) {
|
||||
err := result.Append(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -535,17 +478,8 @@ func (m *resWrangler) AppendAll(other ResMap) error {
|
||||
if other == nil {
|
||||
return nil
|
||||
}
|
||||
w2, ok := other.(*resWrangler)
|
||||
if !ok {
|
||||
panic("bad cast")
|
||||
}
|
||||
for i, res := range w2.Resources() {
|
||||
id, err := w2.idMappingToIndex(i)
|
||||
if err != nil {
|
||||
panic("map is irrecoverably corrupted; " + err.Error())
|
||||
}
|
||||
err = m.AppendWithId(id, res)
|
||||
if err != nil {
|
||||
for _, res := range other.Resources() {
|
||||
if err := m.Append(res); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -557,16 +491,8 @@ func (m *resWrangler) AbsorbAll(other ResMap) error {
|
||||
if other == nil {
|
||||
return nil
|
||||
}
|
||||
w2, ok := other.(*resWrangler)
|
||||
if !ok {
|
||||
panic("bad cast")
|
||||
}
|
||||
for i, r := range w2.Resources() {
|
||||
id, err := w2.idMappingToIndex(i)
|
||||
if err != nil {
|
||||
panic("map is irrecoverably corrupted; " + err.Error())
|
||||
}
|
||||
err = m.appendReplaceOrMerge(id, r)
|
||||
for _, r := range other.Resources() {
|
||||
err := m.appendReplaceOrMerge(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -575,48 +501,52 @@ func (m *resWrangler) AbsorbAll(other ResMap) error {
|
||||
}
|
||||
|
||||
func (m *resWrangler) appendReplaceOrMerge(
|
||||
idForRes resid.ResId, res *resource.Resource) error {
|
||||
matchedId := m.GetMatchingIds(idForRes.GvknEquals)
|
||||
switch len(matchedId) {
|
||||
res *resource.Resource) error {
|
||||
id := res.CurId()
|
||||
// Maybe also try by current id if nothing matches?
|
||||
matches := m.GetMatchingResourcesByOriginalId(id.GvknEquals)
|
||||
switch len(matches) {
|
||||
case 0:
|
||||
switch res.Behavior() {
|
||||
case types.BehaviorMerge, types.BehaviorReplace:
|
||||
return fmt.Errorf(
|
||||
"id %#v does not exist; cannot merge or replace", idForRes)
|
||||
"id %#v does not exist; cannot merge or replace", id)
|
||||
default:
|
||||
// presumably types.BehaviorCreate
|
||||
err := m.AppendWithId(idForRes, res)
|
||||
err := m.Append(res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
mId := matchedId[0]
|
||||
old := m.GetById(mId)
|
||||
old := matches[0]
|
||||
if old == nil {
|
||||
return fmt.Errorf("id lookup failure")
|
||||
}
|
||||
index := m.indexOfResource(old)
|
||||
if index < 0 {
|
||||
return fmt.Errorf("indexing problem")
|
||||
}
|
||||
switch res.Behavior() {
|
||||
case types.BehaviorReplace:
|
||||
res.Replace(old)
|
||||
err := m.ReplaceResource(mId, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case types.BehaviorMerge:
|
||||
res.Merge(old)
|
||||
err := m.ReplaceResource(mId, res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"id %#v exists; must merge or replace", idForRes)
|
||||
"id %#v exists; must merge or replace", id)
|
||||
}
|
||||
i, err := m.Replace(res)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i != index {
|
||||
return fmt.Errorf("unexpected index in replacement")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"found multiple objects %v that could accept merge of %v",
|
||||
matchedId, idForRes)
|
||||
matches, id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"sigs.k8s.io/kustomize/k8sdeps/kunstruct"
|
||||
"sigs.k8s.io/kustomize/pkg/gvk"
|
||||
"sigs.k8s.io/kustomize/pkg/resid"
|
||||
. "sigs.k8s.io/kustomize/pkg/resmap"
|
||||
"sigs.k8s.io/kustomize/pkg/resmaptest"
|
||||
@@ -55,10 +54,10 @@ func TestAppendRemove(t *testing.T) {
|
||||
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())
|
||||
doRemove(t, w1, makeCm(1).OrgId())
|
||||
doRemove(t, w1, makeCm(3).OrgId())
|
||||
doRemove(t, w1, makeCm(5).OrgId())
|
||||
doRemove(t, w1, makeCm(7).OrgId())
|
||||
|
||||
w2 := New()
|
||||
doAppend(t, w2, makeCm(2))
|
||||
@@ -79,7 +78,7 @@ func TestAppendRemove(t *testing.T) {
|
||||
func TestRemove(t *testing.T) {
|
||||
w := New()
|
||||
r := makeCm(1)
|
||||
err := w.Remove(r.Id())
|
||||
err := w.Remove(r.OrgId())
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
@@ -87,20 +86,20 @@ func TestRemove(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
err = w.Remove(r.Id())
|
||||
err = w.Remove(r.OrgId())
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
err = w.Remove(r.Id())
|
||||
err = w.Remove(r.OrgId())
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReplaceResource(t *testing.T) {
|
||||
func TestReplace(t *testing.T) {
|
||||
cm5 := makeCm(5)
|
||||
cm700 := makeCm(700)
|
||||
cm888 := makeCm(888)
|
||||
otherCm5 := makeCm(5)
|
||||
|
||||
w := New()
|
||||
doAppend(t, w, makeCm(1))
|
||||
@@ -112,40 +111,27 @@ func TestReplaceResource(t *testing.T) {
|
||||
doAppend(t, w, makeCm(7))
|
||||
|
||||
oldSize := w.Size()
|
||||
err := w.ReplaceResource(cm5.Id(), cm700)
|
||||
_, err := w.Replace(otherCm5)
|
||||
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 r, err := w.GetByCurrentId(cm5.OrgId()); err != nil || r != otherCm5 {
|
||||
t.Fatalf("unexpected result r=%s, err=%v", r.CurId(), err)
|
||||
}
|
||||
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 {
|
||||
if err := w.Remove(cm5.OrgId()); 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)
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,205 +170,249 @@ metadata:
|
||||
}
|
||||
}
|
||||
|
||||
func TestDemandOneGvknMatchForId(t *testing.T) {
|
||||
rm1 := FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm1", "prefix1", "ns1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
}),
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix1", "ns1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
}),
|
||||
})
|
||||
func TestGetMatchingResourcesByCurrentId(t *testing.T) {
|
||||
r1 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "alice",
|
||||
},
|
||||
})
|
||||
r2 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "bob",
|
||||
},
|
||||
})
|
||||
r3 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "bob",
|
||||
"namespace": "happy",
|
||||
},
|
||||
})
|
||||
r4 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "charlie",
|
||||
"namespace": "happy",
|
||||
},
|
||||
})
|
||||
r5 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "charlie",
|
||||
"namespace": "happy",
|
||||
},
|
||||
})
|
||||
|
||||
result := rm1.GetMatchingIds(
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix1", "ns1").GvknEquals)
|
||||
m := resmaptest_test.NewRmBuilder(t, rf).
|
||||
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).ResMap()
|
||||
|
||||
result := m.GetMatchingResourcesByCurrentId(
|
||||
resid.NewResId(cmap, "alice").GvknEquals)
|
||||
if len(result) != 1 {
|
||||
t.Fatalf("Expected single map entry but got %v", result)
|
||||
}
|
||||
result = m.GetMatchingResourcesByCurrentId(
|
||||
resid.NewResId(cmap, "bob").GvknEquals)
|
||||
if len(result) != 2 {
|
||||
t.Fatalf("Expected two, got %v", result)
|
||||
}
|
||||
result = m.GetMatchingResourcesByCurrentId(
|
||||
resid.NewResIdWithNamespace(cmap, "bob", "system").GvknEquals)
|
||||
if len(result) != 2 {
|
||||
t.Fatalf("Expected two but got %v", result)
|
||||
}
|
||||
result = m.GetMatchingResourcesByCurrentId(
|
||||
resid.NewResIdWithNamespace(cmap, "bob", "happy").Equals)
|
||||
if len(result) != 1 {
|
||||
t.Fatalf("Expected single map entry but got %v", result)
|
||||
}
|
||||
result = m.GetMatchingResourcesByCurrentId(
|
||||
resid.NewResId(cmap, "charlie").GvknEquals)
|
||||
if len(result) != 1 {
|
||||
t.Fatalf("Expected single map entry but got %v", result)
|
||||
}
|
||||
|
||||
// confirm that ns and prefix are not included in match
|
||||
result = rm1.GetMatchingIds(
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm2", "prefix", "ns").GvknEquals)
|
||||
if len(result) != 1 {
|
||||
t.Fatalf("Expected single map entry but got %v", result)
|
||||
// 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,
|
||||
},
|
||||
}
|
||||
|
||||
// confirm that name is matched correctly
|
||||
result = rm1.GetMatchingIds(
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "cm3", "prefix1", "ns1").GvknEquals)
|
||||
if len(result) > 0 {
|
||||
t.Fatalf("Expected no map entries but got %v", result)
|
||||
}
|
||||
|
||||
cmap2 := gvk.Gvk{Version: "v2", Kind: "ConfigMap"}
|
||||
|
||||
// confirm that gvk is matched correctly
|
||||
result = rm1.GetMatchingIds(
|
||||
resid.NewResIdWithPrefixNamespace(cmap2, "cm2", "prefix1", "ns1").GvknEquals)
|
||||
if len(result) > 0 {
|
||||
t.Fatalf("Expected no map entries but got %v", result)
|
||||
for _, tst := range tests {
|
||||
result := m.GetMatchingResourcesByCurrentId(tst.matcher)
|
||||
if len(result) != tst.count {
|
||||
t.Fatalf("test '%s'; actual: %d, expected: %d",
|
||||
tst.name, len(result), tst.count)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterBy(t *testing.T) {
|
||||
func TestSubsetThatCouldBeReferencedByResource(t *testing.T) {
|
||||
r1 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "alice",
|
||||
},
|
||||
})
|
||||
r2 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "bob",
|
||||
},
|
||||
})
|
||||
r3 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "bob",
|
||||
"namespace": "happy",
|
||||
},
|
||||
})
|
||||
r4 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "charlie",
|
||||
"namespace": "happy",
|
||||
},
|
||||
})
|
||||
r5 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "charlie",
|
||||
"namespace": "happy",
|
||||
},
|
||||
})
|
||||
r5.AddNamePrefix("little-")
|
||||
r6 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "domino",
|
||||
"namespace": "happy",
|
||||
},
|
||||
})
|
||||
r6.AddNamePrefix("little-")
|
||||
r7 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ClusterRoleBinding",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "meh",
|
||||
},
|
||||
})
|
||||
|
||||
tests := map[string]struct {
|
||||
resMap ResMap
|
||||
filter resid.ResId
|
||||
filter *resource.Resource
|
||||
expected ResMap
|
||||
}{
|
||||
"different namespace": {
|
||||
resMap: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix", "namespace1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix", "namespace2"),
|
||||
expected: New(),
|
||||
"default namespace 1": {
|
||||
filter: r2,
|
||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||
AddR(r1).AddR(r2).AddR(r7).ResMap(),
|
||||
},
|
||||
"different prefix": {
|
||||
resMap: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix1", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix2", "suffix", "namespace"),
|
||||
expected: New(),
|
||||
"default namespace 2": {
|
||||
filter: r1,
|
||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||
AddR(r1).AddR(r2).AddR(r7).ResMap(),
|
||||
},
|
||||
"different suffix": {
|
||||
resMap: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix1", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map", "prefix", "suffix2", "namespace"),
|
||||
expected: New(),
|
||||
"happy namespace no prefix": {
|
||||
filter: r3,
|
||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||
AddR(r3).AddR(r4).AddR(r7).ResMap(),
|
||||
},
|
||||
"same namespace, same prefix": {
|
||||
resMap: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "config-map1", "prefix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map1",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
filter: resid.NewResIdWithPrefixNamespace(cmap, "config-map2", "prefix", "namespace"),
|
||||
expected: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "config-map1", "prefix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map1",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
"happy namespace with prefix": {
|
||||
filter: r5,
|
||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||
AddR(r5).AddR(r6).AddR(r7).ResMap(),
|
||||
},
|
||||
"same namespace, same suffix": {
|
||||
resMap: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithSuffixNamespace(cmap, "config-map1", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map1",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
filter: resid.NewResIdWithSuffixNamespace(cmap, "config-map2", "suffix", "namespace"),
|
||||
expected: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithSuffixNamespace(cmap, "config-map1", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map1",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
"same namespace, same prefix, same suffix": {
|
||||
resMap: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map1", "prefix", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
filter: resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map2", "prefix", "suffix", "namespace"),
|
||||
expected: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixSuffixNamespace(cmap, "config-map1", "prefix", "suffix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
"filter by cluster-level Gvk": {
|
||||
resMap: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "config-map", "prefix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
filter: resid.NewResId(gvk.Gvk{Kind: "ClusterRoleBinding"}, "cluster-role-binding"),
|
||||
expected: FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResIdWithPrefixNamespace(cmap, "config-map", "prefix", "namespace"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "config-map",
|
||||
},
|
||||
}),
|
||||
}),
|
||||
"cluster level": {
|
||||
filter: r7,
|
||||
expected: resmaptest_test.NewRmBuilder(t, rf).
|
||||
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap(),
|
||||
},
|
||||
}
|
||||
|
||||
m := resmaptest_test.NewRmBuilder(t, rf).
|
||||
AddR(r1).AddR(r2).AddR(r3).AddR(r4).AddR(r5).AddR(r6).AddR(r7).ResMap()
|
||||
for name, test := range tests {
|
||||
test := test
|
||||
t.Run(name, func(t *testing.T) {
|
||||
got := test.resMap.SubsetThatCouldBeReferencedById(test.filter)
|
||||
err := test.expected.ErrorIfNotEqualSets(got)
|
||||
got := m.SubsetThatCouldBeReferencedByResource(test.filter)
|
||||
err := test.expected.ErrorIfNotEqualLists(got)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected %v but got back %v", test.expected, got)
|
||||
test.expected.Debug("expected")
|
||||
got.Debug("actual")
|
||||
t.Fatalf("Expected match")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -410,163 +440,145 @@ func TestDeepCopy(t *testing.T) {
|
||||
if &rm1 == &rm2 {
|
||||
t.Fatal("DeepCopy returned a reference to itself instead of a copy")
|
||||
}
|
||||
err := rm1.ErrorIfNotEqualSets(rm1)
|
||||
err := rm1.ErrorIfNotEqualLists(rm1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMatchingIds(t *testing.T) {
|
||||
|
||||
m := FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResId(
|
||||
gvk.Gvk{Kind: "vegetable"},
|
||||
"bedlam"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "whatever1",
|
||||
},
|
||||
}),
|
||||
resid.NewResId(
|
||||
gvk.Gvk{Group: "g1", Version: "v1", Kind: "vegetable"},
|
||||
"domino"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "whatever2",
|
||||
},
|
||||
}),
|
||||
resid.NewResIdWithPrefixNamespace(
|
||||
gvk.Gvk{Kind: "vegetable"},
|
||||
"peter", "p", "happy"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "whatever3",
|
||||
},
|
||||
}),
|
||||
resid.NewResIdWithPrefixNamespace(
|
||||
gvk.Gvk{Version: "v1", Kind: "fruit"},
|
||||
"shatterstar", "p", "happy"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "whatever4",
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
matcher IdMatcher
|
||||
count int
|
||||
}{
|
||||
{
|
||||
"match everything",
|
||||
func(resid.ResId) bool { return true },
|
||||
4,
|
||||
},
|
||||
{
|
||||
"match nothing",
|
||||
func(resid.ResId) bool { return false },
|
||||
0,
|
||||
},
|
||||
{
|
||||
"name is peter",
|
||||
func(x resid.ResId) bool { return x.Name() == "peter" },
|
||||
1,
|
||||
},
|
||||
{
|
||||
"happy vegetable",
|
||||
func(x resid.ResId) bool {
|
||||
return x.Namespace() == "happy" &&
|
||||
x.Gvk().Kind == "vegetable"
|
||||
func TestErrorIfNotEqualSets(t *testing.T) {
|
||||
r1 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
1,
|
||||
},
|
||||
})
|
||||
r2 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
})
|
||||
r3 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
"namespace": "system",
|
||||
},
|
||||
})
|
||||
|
||||
m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
||||
if err := m1.ErrorIfNotEqualSets(m1); err != nil {
|
||||
t.Fatalf("object should equal itself %v", err)
|
||||
}
|
||||
for _, tst := range tests {
|
||||
result := m.GetMatchingIds(tst.matcher)
|
||||
if len(result) != tst.count {
|
||||
t.Fatalf("test '%s'; actual: %d, expected: %d",
|
||||
tst.name, len(result), tst.count)
|
||||
}
|
||||
|
||||
m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).ResMap()
|
||||
if err := m1.ErrorIfNotEqualSets(m2); err == nil {
|
||||
t.Fatalf("%v should not equal %v %v", m1, m2, err)
|
||||
}
|
||||
|
||||
m3 := resmaptest_test.NewRmBuilder(t, rf).Add(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
}}).ResMap()
|
||||
if err := m2.ErrorIfNotEqualSets(m3); err != nil {
|
||||
t.Fatalf("%v should equal %v %v", m2, m3, err)
|
||||
}
|
||||
|
||||
m4 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
||||
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||
}
|
||||
|
||||
m4 = resmaptest_test.NewRmBuilder(t, rf).AddR(r3).AddR(r1).AddR(r2).ResMap()
|
||||
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||
}
|
||||
|
||||
m4 = m1.ShallowCopy()
|
||||
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||
}
|
||||
m4 = m1.DeepCopy()
|
||||
if err := m1.ErrorIfNotEqualSets(m4); err != nil {
|
||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorIfNotEqual(t *testing.T) {
|
||||
rm1 := resmaptest_test.NewRmBuilder(t, rf).Add(
|
||||
func TestErrorIfNotEqualLists(t *testing.T) {
|
||||
r1 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
}).Add(map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
}).ResMap()
|
||||
})
|
||||
r2 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
},
|
||||
})
|
||||
r3 := rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm2",
|
||||
"namespace": "system",
|
||||
},
|
||||
})
|
||||
|
||||
err := rm1.ErrorIfNotEqualSets(rm1)
|
||||
if err != nil {
|
||||
t.Fatalf("%v should equal itself %v", rm1, err)
|
||||
m1 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
||||
if err := m1.ErrorIfNotEqualLists(m1); err != nil {
|
||||
t.Fatalf("object should equal itself %v", err)
|
||||
}
|
||||
|
||||
rm2 := resmaptest_test.NewRmBuilder(t, rf).Add(
|
||||
m2 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).ResMap()
|
||||
if err := m1.ErrorIfNotEqualLists(m2); err == nil {
|
||||
t.Fatalf("%v should not equal %v %v", m1, m2, err)
|
||||
}
|
||||
|
||||
m3 := resmaptest_test.NewRmBuilder(t, rf).Add(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
}).ResMap()
|
||||
|
||||
// test the different number of keys path
|
||||
err = rm1.ErrorIfNotEqualSets(rm2)
|
||||
if err == nil {
|
||||
t.Fatalf("%v should not equal %v %v", rm1, rm2, err)
|
||||
}}).ResMap()
|
||||
if err := m2.ErrorIfNotEqualLists(m3); err != nil {
|
||||
t.Fatalf("%v should equal %v %v", m2, m3, err)
|
||||
}
|
||||
|
||||
rm3 := FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResId(cmap, "cm2"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm1",
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
// test the different key values path
|
||||
err = rm2.ErrorIfNotEqualSets(rm3)
|
||||
if err == nil {
|
||||
t.Fatalf("%v should not equal %v %v", rm1, rm2, err)
|
||||
m4 := resmaptest_test.NewRmBuilder(t, rf).AddR(r1).AddR(r2).AddR(r3).ResMap()
|
||||
if err := m1.ErrorIfNotEqualLists(m4); err != nil {
|
||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||
}
|
||||
|
||||
rm4 := FromMap(map[resid.ResId]*resource.Resource{
|
||||
resid.NewResId(cmap, "cm1"): rf.FromMap(
|
||||
map[string]interface{}{
|
||||
"apiVersion": "v1",
|
||||
"kind": "ConfigMap",
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "cm3",
|
||||
},
|
||||
}),
|
||||
})
|
||||
m4 = resmaptest_test.NewRmBuilder(t, rf).AddR(r3).AddR(r1).AddR(r2).ResMap()
|
||||
if err := m1.ErrorIfNotEqualLists(m4); err == nil {
|
||||
t.Fatalf("expected inequality between %v and %v, %v", m1, m4, err)
|
||||
}
|
||||
|
||||
// test the deepcopy path
|
||||
err = rm2.ErrorIfNotEqualSets(rm4)
|
||||
if err == nil {
|
||||
t.Fatalf("%v should not equal %v %v", rm1, rm2, err)
|
||||
m4 = m1.ShallowCopy()
|
||||
if err := m1.ErrorIfNotEqualLists(m4); err != nil {
|
||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||
}
|
||||
m4 = m1.DeepCopy()
|
||||
if err := m1.ErrorIfNotEqualLists(m4); err != nil {
|
||||
t.Fatalf("expected equality between %v and %v, %v", m1, m4, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -601,7 +613,7 @@ func TestAppendAll(t *testing.T) {
|
||||
if err := input1.AppendAll(input2); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := expected.ErrorIfNotEqualSets(input1); err != nil {
|
||||
if err := expected.ErrorIfNotEqualLists(input1); err != nil {
|
||||
input1.Debug("1")
|
||||
expected.Debug("ex")
|
||||
t.Fatalf("%#v doesn't equal expected %#v", input1, expected)
|
||||
@@ -609,7 +621,7 @@ func TestAppendAll(t *testing.T) {
|
||||
if err := input1.AppendAll(nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := expected.ErrorIfNotEqualSets(input1); err != nil {
|
||||
if err := expected.ErrorIfNotEqualLists(input1); err != nil {
|
||||
t.Fatalf("%#v doesn't equal expected %#v", input1, expected)
|
||||
}
|
||||
}
|
||||
@@ -671,14 +683,14 @@ func TestAbsorbAll(t *testing.T) {
|
||||
if err := w.AbsorbAll(makeMap2(types.BehaviorMerge)); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := expected.ErrorIfNotEqualSets(w); err != nil {
|
||||
if err := expected.ErrorIfNotEqualLists(w); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w = makeMap1()
|
||||
if err := w.AbsorbAll(nil); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := w.ErrorIfNotEqualSets(makeMap1()); err != nil {
|
||||
if err := w.ErrorIfNotEqualLists(makeMap1()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w = makeMap1()
|
||||
@@ -686,7 +698,7 @@ func TestAbsorbAll(t *testing.T) {
|
||||
if err := w.AbsorbAll(w2); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if err := w2.ErrorIfNotEqualSets(w); err != nil {
|
||||
if err := w2.ErrorIfNotEqualLists(w); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w = makeMap1()
|
||||
|
||||
Reference in New Issue
Block a user