Skip to content

Commit

Permalink
Elide uninteresting fields in object diffs.
Browse files Browse the repository at this point in the history
Signed-off-by: Marcin Owsiany <porridge@redhat.com>
  • Loading branch information
porridge committed Nov 15, 2023
1 parent 46a545d commit 748f7a0
Show file tree
Hide file tree
Showing 5 changed files with 743 additions and 3 deletions.
3 changes: 2 additions & 1 deletion pkg/test/step.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,8 @@ func (s *Step) CheckResource(expected runtime.Object, namespace string) []error
tmpTestErrors := []error{}

if err := testutils.IsSubset(expectedObj, actual.UnstructuredContent()); err != nil {
diff, diffErr := testutils.PrettyDiff(expected, &actual)
diff, diffErr := testutils.PrettyDiff(
&unstructured.Unstructured{Object: expectedObj}, &actual)
if diffErr == nil {
tmpTestErrors = append(tmpTestErrors, fmt.Errorf(diff))
} else {
Expand Down
46 changes: 44 additions & 2 deletions pkg/test/utils/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,16 +358,58 @@ func Namespaced(dClient discovery.DiscoveryInterface, obj runtime.Object, namesp
return m.GetName(), namespace, nil
}

func pruneLargeAdditions(expected *unstructured.Unstructured, actual *unstructured.Unstructured) runtime.Object {
pruned := actual.DeepCopy()
prune(expected.Object, pruned.Object)
return pruned
}

func prune(expected map[string]interface{}, actual map[string]interface{}) {
const maxLines = 10
toRemove := []string{}
for k, v := range actual {
if _, inExpected := expected[k]; inExpected {
expectedMap, isExpectedMap := expected[k].(map[string]interface{})
actualMap, isActualMap := actual[k].(map[string]interface{})
if isActualMap && isExpectedMap {
prune(expectedMap, actualMap)
}
continue
}
numLines, err := countLines(k, v)
if err != nil || numLines < maxLines {
continue
}
toRemove = append(toRemove, k)
}
for _, s := range toRemove {
actual[s] = fmt.Sprintf("[... elided field over %d lines long ...]", maxLines)
}
}

func countLines(k string, v interface{}) (int, error) {
buf := strings.Builder{}
dummyObj := &unstructured.Unstructured{
Object: map[string]interface{}{k: v}}
err := MarshalObject(dummyObj, &buf)
if err != nil {
return 0, fmt.Errorf("cannot marshal field %s to compute its length in lines: %w", k, err)
}
return strings.Count(buf.String(), "\n"), nil
}

// PrettyDiff creates a unified diff highlighting the differences between two Kubernetes resources
func PrettyDiff(expected runtime.Object, actual runtime.Object) (string, error) {
func PrettyDiff(expected *unstructured.Unstructured, actual *unstructured.Unstructured) (string, error) {
actualPruned := pruneLargeAdditions(expected, actual)

expectedBuf := &bytes.Buffer{}
actualBuf := &bytes.Buffer{}

if err := MarshalObject(expected, expectedBuf); err != nil {
return "", err
}

if err := MarshalObject(actual, actualBuf); err != nil {
if err := MarshalObject(actualPruned, actualBuf); err != nil {
return "", err
}

Expand Down
52 changes: 52 additions & 0 deletions pkg/test/utils/kubernetes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,3 +515,55 @@ func TestRunScript(t *testing.T) {
})
}
}

func TestPrettyDiff(t *testing.T) {
actual, err := LoadYAMLFromFile("test_data/prettydiff-actual.yaml")
assert.NoError(t, err)
assert.Len(t, actual, 1)
expected, err := LoadYAMLFromFile("test_data/prettydiff-expected.yaml")
assert.NoError(t, err)
assert.Len(t, expected, 1)

result, err := PrettyDiff(expected[0].(*unstructured.Unstructured), actual[0].(*unstructured.Unstructured))
assert.NoError(t, err)
assert.Equal(t, `--- Deployment:/central
+++ Deployment:kuttl-test-thorough-hermit/central
@@ -1,7 +1,35 @@
apiVersion: apps/v1
kind: Deployment
metadata:
+ annotations:
+ email: support@stackrox.com
+ meta.helm.sh/release-name: stackrox-central-services
+ meta.helm.sh/release-namespace: kuttl-test-thorough-hermit
+ owner: stackrox
+ labels:
+ app: central
+ app.kubernetes.io/component: central
+ app.kubernetes.io/instance: stackrox-central-services
+ app.kubernetes.io/managed-by: Helm
+ app.kubernetes.io/name: stackrox
+ app.kubernetes.io/part-of: stackrox-central-services
+ app.kubernetes.io/version: 4.3.x-160-g465d734c11
+ helm.sh/chart: stackrox-central-services-400.3.0-160-g465d734c11
+ managedFields: '[... elided field over 10 lines long ...]'
name: central
+ namespace: kuttl-test-thorough-hermit
+ ownerReferences:
+ - apiVersion: platform.stackrox.io/v1alpha1
+ blockOwnerDeletion: true
+ controller: true
+ kind: Central
+ name: stackrox-central-services
+ uid: ff834d91-0853-42b3-9460-7ebf1c659f8a
+spec: '[... elided field over 10 lines long ...]'
status:
- availableReplicas: 1
+ conditions: '[... elided field over 10 lines long ...]'
+ observedGeneration: 2
+ replicas: 1
+ unavailableReplicas: 1
+ updatedReplicas: 1
`, result)
}
Loading

0 comments on commit 748f7a0

Please sign in to comment.