Skip to content
This repository has been archived by the owner on Nov 17, 2021. It is now read-only.

Commit

Permalink
add last-applied strategy for diff
Browse files Browse the repository at this point in the history
  • Loading branch information
kgaurav2 committed May 9, 2021
1 parent ddc4eaf commit 31781f2
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 9 deletions.
2 changes: 1 addition & 1 deletion cmd/diff.go
Expand Up @@ -27,7 +27,7 @@ const (
)

func init() {
diffCmd.PersistentFlags().String(flagDiffStrategy, "all", "Diff strategy, all or subset.")
diffCmd.PersistentFlags().String(flagDiffStrategy, "all", "Diff strategy, all, subset or last-applied")
diffCmd.PersistentFlags().Bool(flagOmitSecrets, false, "hide secret details when showing diff")
RootCmd.AddCommand(diffCmd)
}
Expand Down
21 changes: 15 additions & 6 deletions pkg/kubecfg/diff.go
Expand Up @@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"io"
v1 "k8s.io/api/core/v1"
"os"
"regexp"
"sort"
Expand Down Expand Up @@ -88,12 +89,7 @@ func (c DiffCmd) Run(ctx context.Context, apiObjects []*unstructured.Unstructure
continue
}

liveObjObject := liveObj.Object
if c.DiffStrategy == "subset" {
liveObjObject = removeMapFields(obj.Object, liveObjObject)
}

liveObjText, _ := json.MarshalIndent(liveObjObject, "", " ")
liveObjText, _ := json.MarshalIndent(c.getLiveObjObject(obj, liveObj), "", " ")
objText, _ := json.MarshalIndent(obj.Object, "", " ")

liveObjTextLines, objTextLines, lines := dmp.DiffLinesToChars(string(liveObjText), string(objText))
Expand All @@ -119,6 +115,19 @@ func (c DiffCmd) Run(ctx context.Context, apiObjects []*unstructured.Unstructure
return nil
}

func (c DiffCmd) getLiveObjObject(obj *unstructured.Unstructured, liveObj *unstructured.Unstructured) map[string]interface{} {
var liveObjObject map[string]interface{}
if c.DiffStrategy == "subset" {
liveObjObject = removeMapFields(obj.Object, liveObj.Object)
} else if c.DiffStrategy == "last-applied" {
lastAppliedText := liveObj.GetAnnotations()[v1.LastAppliedConfigAnnotation]
json.Unmarshal([]byte(lastAppliedText), &liveObjObject)
} else {
liveObjObject = liveObj.Object
}
return liveObjObject
}

// Formats the supplied Diff as a unified-diff-like text with infinite context and optionally colorizes it.
func (c DiffCmd) formatDiff(diffs []diffmatchpatch.Diff, color bool, omitchanges bool) string {
var buff bytes.Buffer
Expand Down
77 changes: 75 additions & 2 deletions pkg/kubecfg/diff_test.go
Expand Up @@ -16,11 +16,84 @@
package kubecfg

import (
"testing"

"encoding/json"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"testing"
)

func TestLastAppliedStrategy(t *testing.T) {

var liveMap map[string]interface{}
var expectedMap map[string]interface{}

c := DiffCmd{}
c.DiffStrategy = "last-applied"

liveText :=
`{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"name": "foo",
"namespace": "default",
"resourceVersion": "288527730",
"selfLink": "/api/v1/namespaces/default/services/foo",
"uid": "687c86fe-12ec-45d1-a4e8-61db473e2d01",
"creationTimestamp": "2021-04-11T13:38:06Z",
"annotations": {
"testKey": "testValue",
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{\"testKey\": \"testValue\"},\"name\":\"foo\",\"namespace\":\"default\"}, \"spec\":{\"ports\":[{\"name\":\"http\",\"port\":8080}],\"selector\":{\"app\":\"foo\"}}}"
}
},
"spec": {
"sessionAffinity": "None",
"type": "ClusterIP",
"selector": {
"app": "foo"
},
"ports": [
{
"name": "http",
"port": 8080,
"protocol": "TCP",
"targetPort": 8080
}
]
},
"status": {
"loadBalancer": {}
}
}`
json.Unmarshal([]byte(liveText), &liveMap)

expectedText :=
`{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"name": "foo",
"namespace": "default",
"annotations": {
"testKey": "testValue"
}
},
"spec": {
"selector": {
"app": "foo"
},
"ports": [
{
"name": "http",
"port": 8080
}
]
}
}`
json.Unmarshal([]byte(expectedText), &expectedMap)
require.Equal(t, expectedMap, c.getLiveObjObject(nil, &unstructured.Unstructured{Object: liveMap}))
}

func TestRemoveListFields(t *testing.T) {
for _, tc := range []struct {
config, live, expected []interface{}
Expand Down

0 comments on commit 31781f2

Please sign in to comment.