Skip to content

Commit

Permalink
komega: add unstructured support to EqualObject
Browse files Browse the repository at this point in the history
  • Loading branch information
schrej committed May 5, 2022
1 parent 25e9b08 commit 7938d06
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 83 deletions.
123 changes: 55 additions & 68 deletions pkg/envtest/komega/equalobject.go
Expand Up @@ -17,7 +17,6 @@ package komega
import (
"fmt"
"reflect"
"strconv"
"strings"

"github.com/google/go-cmp/cmp"
Expand All @@ -33,15 +32,15 @@ var (
// IgnoreAutogeneratedMetadata contains the paths for all the metadata fields that are commonly set by the
// client and APIServer. This is used as a MatchOption for situations when only user-provided metadata is relevant.
IgnoreAutogeneratedMetadata = IgnorePaths{
{"ObjectMeta", "UID"},
{"ObjectMeta", "Generation"},
{"ObjectMeta", "CreationTimestamp"},
{"ObjectMeta", "ResourceVersion"},
{"ObjectMeta", "ManagedFields"},
{"ObjectMeta", "DeletionGracePeriodSeconds"},
{"ObjectMeta", "DeletionTimestamp"},
{"ObjectMeta", "SelfLink"},
{"ObjectMeta", "GenerateName"},
"metadata.uid",
"metadata.generation",
"metadata.creationTimestamp",
"metadata.resourceVersion",
"metadata.managedFields",
"metadata.deletionGracePeriodSeconds",
"metadata.deletionTimestamp",
"metadata.selfLink",
"metadata.generateName",
}
)

Expand Down Expand Up @@ -112,70 +111,59 @@ func (d diffPath) String() string {
// diffReporter is a custom recorder for cmp.Diff which records all paths that are
// different between two objects.
type diffReporter struct {
stack []cmp.PathStep
path []string
jsonPath []string
stack []cmp.PathStep

diffPaths []diffPath
}

func (r *diffReporter) PushStep(s cmp.PathStep) {
r.stack = append(r.stack, s)
if len(r.stack) <= 1 {
return
}
switch s := s.(type) {
case cmp.SliceIndex:
r.path = append(r.path, strconv.Itoa(s.Key()))
r.jsonPath = append(r.jsonPath, strconv.Itoa(s.Key()))
case cmp.MapIndex:
key := fmt.Sprintf("%v", s.Key())
// if strings.ContainsAny(key, ".[]/\\") {
// key = fmt.Sprintf("[%s]", key)
// } else {
// key = "." + key
// }
r.path = append(r.path, key)
r.jsonPath = append(r.jsonPath, key)
case cmp.StructField:
field := r.stack[len(r.stack)-2].Type().Field(s.Index())
jsonName := strings.Split(field.Tag.Get("json"), ",")[0]
r.path = append(r.path, s.String()[1:])
r.jsonPath = append(r.jsonPath, jsonName)
}
}

func (r *diffReporter) Report(res cmp.Result) {
if !res.Equal() {
r.diffPaths = append(r.diffPaths, diffPath{types: r.path, json: r.jsonPath})
r.diffPaths = append(r.diffPaths, r.currPath())
}
}

// func (r *diffReporter) currPath() string {
// p := []string{}
// for _, s := range r.stack[1:] {
// switch s := s.(type) {
// case cmp.StructField, cmp.SliceIndex, cmp.MapIndex:
// p = append(p, s.String())
// }
// }
// return strings.Join(p, "")[1:]
// }
func (r *diffReporter) currPath() diffPath {
p := diffPath{types: []string{""}, json: []string{""}}
i := 0
for si, s := range r.stack[1:] {
switch s := s.(type) {
case cmp.StructField:
p.types = append(p.types, s.String()[1:])
field := r.stack[si].Type().Field(s.Index())
p.json = append(p.json, strings.Split(field.Tag.Get("json"), ",")[0])
i++
case cmp.SliceIndex:
key := fmt.Sprintf("[%d]", s.Key())
p.types[i] += key
p.json[i] += key
case cmp.MapIndex:
key := fmt.Sprintf("%v", s.Key())
if strings.ContainsAny(key, ".[]/\\") {
key = fmt.Sprintf("[%s]", key)
p.types[i] += key
p.json[i] += key
} else {
p.types = append(p.types, key)
p.json = append(p.json, key)
i++
}
}
}
// empty strings were added as the first element, if they're still empty remove them again
if len(p.json) > 0 && len(p.json[0]) == 0 {
p.json = p.json[1:]
p.types = p.types[1:]
}
fmt.Println(p)
return p
}

func (r *diffReporter) PopStep() {
popped := r.stack[len(r.stack)-1]
r.stack = r.stack[:len(r.stack)-1]
if _, ok := popped.(cmp.Indirect); ok {
return
}
if len(r.stack) <= 1 {
return
}
switch popped.(type) {
case cmp.SliceIndex, cmp.MapIndex, cmp.StructField:
r.path = r.path[:len(r.path)-1]
r.jsonPath = r.jsonPath[:len(r.jsonPath)-1]
}
}

// calculateDiff calculates the difference between two objects and returns the
Expand Down Expand Up @@ -210,7 +198,7 @@ func filterDiffPaths(opts EqualObjectOptions, paths []diffPath) []diffPath {

func matchesPath(path []string, prefix []string) bool {
for i, p := range prefix {
if i >= len(path) || p != path[i] {
if i >= len(path) || !strings.HasPrefix(path[i], p) {
return false
}
}
Expand Down Expand Up @@ -249,23 +237,22 @@ func (o *EqualObjectOptions) ApplyOptions(opts []EqualObjectOption) *EqualObject
return o
}

// func parsePath(path string) []string {
// s := strings.Split(path, ".")
// return s
// }

// IgnorePaths instructs the Matcher to ignore given paths when computing a diff.
type IgnorePaths [][]string
type IgnorePaths []string

// ApplyToEqualObjectMatcher applies this configuration to the given MatchOptions.
func (i IgnorePaths) ApplyToEqualObjectMatcher(opts *EqualObjectOptions) {
opts.ignorePaths = append(opts.ignorePaths, i...)
for _, p := range i {
opts.ignorePaths = append(opts.ignorePaths, strings.Split(p, "."))
}
}

// MatchPaths instructs the Matcher to restrict its diff to the given paths. If empty the Matcher will look at all paths.
type MatchPaths [][]string
type MatchPaths []string

// ApplyToEqualObjectMatcher applies this configuration to the given MatchOptions.
func (i MatchPaths) ApplyToEqualObjectMatcher(opts *EqualObjectOptions) {
opts.matchPaths = append(opts.matchPaths, i...)
for _, p := range i {
opts.ignorePaths = append(opts.ignorePaths, strings.Split(p, "."))
}
}
31 changes: 16 additions & 15 deletions pkg/envtest/komega/equalobject_test.go
Expand Up @@ -69,11 +69,11 @@ func TestEqualObjectMatcher(t *testing.T) {
result: true,
opts: []EqualObjectOption{
IgnorePaths{
{"ObjectMeta", "Name"},
{"ObjectMeta", "CreationTimestamp"},
{"ObjectMeta", "Labels", "somelabel"},
{"ObjectMeta", "OwnerReferences", "0", "Name"},
{"Spec", "Template", "ObjectMeta"},
"ObjectMeta.Name",
"ObjectMeta.CreationTimestamp",
"ObjectMeta.Labels.somelabel",
"ObjectMeta.OwnerReferences[0].Name",
"Spec.Template.ObjectMeta",
},
},
},
Expand All @@ -100,11 +100,11 @@ func TestEqualObjectMatcher(t *testing.T) {
result: true,
opts: []EqualObjectOption{
IgnorePaths{
{"metadata", "name"},
{"metadata", "creationTimestamp"},
{"metadata", "labels", "somelabel"},
{"metadata", "ownerReferences", "0", "name"},
{"spec", "template", "metadata"},
"metadata.name",
"metadata.creationTimestamp",
"metadata.labels.somelabel",
"metadata.ownerReferences[0].name",
"spec.template.metadata",
},
},
},
Expand All @@ -125,7 +125,7 @@ func TestEqualObjectMatcher(t *testing.T) {
result: true,
opts: []EqualObjectOption{
MatchPaths{
{"ObjectMeta", "Name"},
"ObjectMeta.Name",
},
},
},
Expand All @@ -134,21 +134,23 @@ func TestEqualObjectMatcher(t *testing.T) {
expected: &unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]interface{}{
"name": "something",
"name": "something",
"namespace": "test",
},
},
},
actual: &unstructured.Unstructured{
Object: map[string]interface{}{
"metadata": map[string]interface{}{
"name": "somethingelse",
"name": "somethingelse",
"namespace": "test",
},
},
},
result: true,
opts: []EqualObjectOption{
IgnorePaths{
{"metadata", "name"},
"metadata.name",
},
},
},
Expand All @@ -159,7 +161,6 @@ func TestEqualObjectMatcher(t *testing.T) {
g := NewWithT(t)
m := EqualObject(c.expected, c.opts...)
g.Expect(m.Match(c.actual)).To(Equal(c.result))
//fmt.Println(m.FailureMessage(&c.actual))
})
}
}

0 comments on commit 7938d06

Please sign in to comment.