Unverified Commit 9c0daebf authored by Jesse Suen's avatar Jesse Suen
Browse files

Fix diff falsely reporting OutOfSync due to namespace/annotation defaulting

parent f2a0ca56
master parameterized-cmps-tests refresh-docs release-0.10 release-0.11 release-0.12 release-0.4 release-0.5 release-0.6 release-0.7 release-0.8 release-0.9 release-1.0 release-1.1 release-1.2 release-1.3 release-1.4 release-1.5 release-1.6 release-1.7 release-1.8 release-2.0 release-2.1 release-2.2 release-2.3 release-2.4 snyk-fix-22fec3fb11f6fa24b60d05409e980aeb snyk-fix-ae7425b39b7a4d1c374881d04cb13933 snyk-upgrade-187747164f83e48c13336135ff81d0f0 snyk-upgrade-2243046f87be3d7f9b10180955281f9c snyk-upgrade-4c01df0c7326715d3fa31e2b5f273fe5 snyk-upgrade-505a87f7759c3f10d2e1d6934aca94c4 snyk-upgrade-96df2baba13d46d6ce52a422eadf9c67 snyk-upgrade-b22f1a942b1f1490c2be0ead223e7a72 snyk-upgrade-da82436a199275a8077335c09f156f48 snyk-upgrade-ebd75ffe2591f805dde7aac7e06c9c9a snyk-upgrade-fa9072ca01170f7868a58605077488c1 v2.4.0-rc1 v2.3.3 v2.3.2 v2.3.1 v2.3.0 v2.3.0-rc5 v2.3.0-rc4 v2.3.0-rc2 v2.3.0-rc1 v2.2.8 v2.2.7 v2.2.6 v2.2.5 v2.2.4 v2.2.3 v2.2.2 v2.2.1 v2.2.0 v2.2.0-rc1 v2.1.14 v2.1.13 v2.1.12 v2.1.11 v2.1.10 v2.1.9 v2.1.8 v2.1.7 v2.1.6 v2.1.5 v2.1.4 v2.1.3 v2.1.2 v2.1.2-hf1 v2.1.1 v2.1.0 v2.1.0-rc3 v2.1.0-rc2 v2.1.0-rc1 v2.0.5 v2.0.4 v2.0.3 v2.0.2 v2.0.1 v2.0.0 v2.0.0-rc4 v2.0.0-rc3 v2.0.0-rc2 v2.0.0-rc1 v1.8.7 v1.8.6 v1.8.5 v1.8.4 v1.8.3 v1.8.2 v1.8.1 v1.8.0 v1.8.0-rc2 v1.8.0-rc1 v1.7.14 v1.7.13 v1.7.12 v1.7.11 v1.7.10 v1.7.9 v1.7.8 v1.7.7 v1.7.6 v1.7.5 v1.7.4 v1.7.3 v1.7.2 v1.7.1 v1.7.0 v1.7.0-rc1 v1.6.2 v1.6.1 v1.6.0 v1.6.0-rc2 v1.6.0-rc1 v1.5.8 v1.5.7 v1.5.6 v1.5.5 v1.5.4 v1.5.3 v1.5.2 v1.5.1 v1.5.0 v1.5.0-rc3 v1.5.0-rc2 v1.5.0-rc1 v1.4.3 v1.4.2 v1.4.1 v1.4.0 v1.4.0-rc1 v1.3.6 v1.3.5 v1.3.4 v1.3.3 v1.3.2 v1.3.1 v1.3.0 v1.3.0-rc5 v1.3.0-rc4 v1.3.0-rc3 v1.3.0-rc2 v1.3.0-rc1 v1.2.5 v1.2.4 v1.2.3 v1.2.2 v1.2.1 v1.2.0 v1.2.0-rc2 v1.2.0-rc1 v1.1.2 v1.1.1 v1.1.0 v1.1.0-rc8 v1.1.0-rc7 v1.1.0-rc6 v1.1.0-rc5 v1.1.0-rc4 v1.1.0-rc3 v1.1.0-rc2 v1.1.0-rc1 v1.0.2 v1.0.1 v1.0.0 v1.0.0-rc3 v1.0.0-rc2 v1.0.0-rc1 v0.12.3 v0.12.2 v0.12.1 v0.12.0 v0.12.0-rc6 v0.12.0-rc5 v0.12.0-rc4 v0.12.0-rc3 v0.12.0-rc2 v0.12.0-rc1 v0.11.2 v0.11.1 v0.11.0 v0.11.0-rc6 v0.11.0-rc5 v0.11.0-rc4 v0.11.0-rc3 v0.11.0-rc2 v0.11.0-rc1 v0.10.6 v0.10.5 v0.10.4 v0.10.3 v0.10.2 v0.10.1 v0.10.0 v0.9.2 v0.9.1 v0.9.0 v0.8.2 v0.8.1 v0.8.0 v0.7.2 v0.7.1 v0.7.0 v0.6.2 v0.6.1 v0.6.0 v0.5.4 v0.5.3 v0.5.2 v0.5.1 v0.5.0 v0.4.7 v0.4.6 v0.4.5 v0.4.4 v0.4.3 v0.4.2 v0.4.1 v0.4.0 stable
Showing with 96 additions and 6 deletions
+96 -6
......@@ -56,6 +56,7 @@ func TwoWayDiff(config, live *unstructured.Unstructured) *DiffResult {
// ThreeWayDiff performs a diff with the understanding of how to incorporate the
// last-applied-configuration annotation in the diff.
func ThreeWayDiff(orig, config, live *unstructured.Unstructured) *DiffResult {
orig = removeNamespaceAnnotation(orig)
// remove extra fields in the live, that were not in the original object
liveObj := RemoveMapFields(orig.Object, live.Object)
// now we have a pruned live object
......@@ -84,6 +85,22 @@ func ThreeWayDiff(orig, config, live *unstructured.Unstructured) *DiffResult {
return &dr
}
func removeNamespaceAnnotation(orig *unstructured.Unstructured) *unstructured.Unstructured {
orig = orig.DeepCopy()
// remove the namespace an annotation from the
if metadataIf, ok := orig.Object["metadata"]; ok {
metadata := metadataIf.(map[string]interface{})
delete(metadata, "namespace")
if annotationsIf, ok := metadata["annotations"]; ok {
annotation := annotationsIf.(map[string]interface{})
if len(annotation) == 0 {
delete(metadata, "annotations")
}
}
}
return orig
}
func threeWayMergePatch(orig, config, live *unstructured.Unstructured) ([]byte, error) {
origBytes, err := json.Marshal(orig.Object)
if err != nil {
......
......@@ -15,12 +15,23 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
var (
formatOpts = formatter.AsciiFormatterConfig{
Coloring: terminal.IsTerminal(int(os.Stdout.Fd())),
}
)
func TestDiff(t *testing.T) {
leftDep := test.DemoDeployment()
leftUn := kube.MustToUnstructured(leftDep)
diffRes := Diff(leftUn, leftUn)
assert.False(t, diffRes.Diff.Modified())
ascii, err := diffRes.ASCIIFormat(leftUn, formatOpts)
assert.Nil(t, err)
if ascii != "" {
log.Println(ascii)
}
}
func TestDiffWithNils(t *testing.T) {
......@@ -86,6 +97,7 @@ func TestDiffArrayModification(t *testing.T) {
func TestThreeWayDiff(t *testing.T) {
// 1. get config and live to be the same. Both have a foo annotation.
configDep := test.DemoDeployment()
configDep.ObjectMeta.Namespace = ""
configDep.Annotations = map[string]string{
"foo": "bar",
}
......@@ -93,11 +105,15 @@ func TestThreeWayDiff(t *testing.T) {
// 2. add a extra field to the live. this simulates kubernetes adding default values in the
// object. We should not consider defaulted values as a difference
liveDep.Annotations["some-default-val"] = "default"
liveDep.SetNamespace("default")
configUn := kube.MustToUnstructured(configDep)
liveUn := kube.MustToUnstructured(liveDep)
res := Diff(configUn, liveUn)
assert.False(t, res.Modified)
if !assert.False(t, res.Modified) {
ascii, err := res.ASCIIFormat(configUn, formatOpts)
assert.Nil(t, err)
log.Println(ascii)
}
// 3. Add a last-applied-configuration annotation in the live. There should still not be any
// difference
......@@ -107,7 +123,11 @@ func TestThreeWayDiff(t *testing.T) {
configUn = kube.MustToUnstructured(configDep)
liveUn = kube.MustToUnstructured(liveDep)
res = Diff(configUn, liveUn)
assert.False(t, res.Modified)
if !assert.False(t, res.Modified) {
ascii, err := res.ASCIIFormat(configUn, formatOpts)
assert.Nil(t, err)
log.Println(ascii)
}
// 4. Remove the foo annotation from config and perform the diff again. We should detect a
// difference since three-way diff detects the removal of a managed field
......@@ -125,9 +145,6 @@ func TestThreeWayDiff(t *testing.T) {
configUn = kube.MustToUnstructured(configDep)
liveUn = kube.MustToUnstructured(liveDep)
res = Diff(configUn, liveUn)
formatOpts := formatter.AsciiFormatterConfig{
Coloring: terminal.IsTerminal(int(os.Stdout.Fd())),
}
ascii, err := res.ASCIIFormat(configUn, formatOpts)
assert.Nil(t, err)
if ascii != "" {
......@@ -135,3 +152,59 @@ func TestThreeWayDiff(t *testing.T) {
}
assert.False(t, res.Modified)
}
var demoConfig = `
{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": {
"labels": {
"applications.argoproj.io/app-name": "argocd-demo"
},
"name": "application-controller"
}
}
`
var demoLive = `
{
"apiVersion": "v1",
"kind": "ServiceAccount",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"ServiceAccount\",\"metadata\":{\"annotations\":{},\"labels\":{\"applications.argoproj.io/app-name\":\"argocd-demo\"},\"name\":\"application-controller\",\"namespace\":\"argocd-demo\"}}\n"
},
"creationTimestamp": "2018-04-16T22:08:57Z",
"labels": {
"applications.argoproj.io/app-name": "argocd-demo"
},
"name": "application-controller",
"namespace": "argocd-demo",
"resourceVersion": "7584502",
"selfLink": "/api/v1/namespaces/argocd-demo/serviceaccounts/application-controller",
"uid": "c22bb2b4-41c2-11e8-978a-028445d52ec8"
},
"secrets": [
{
"name": "application-controller-token-kfxct"
}
]
}
`
// Tests a real world example
func TestDiffActualExample(t *testing.T) {
var configUn, liveUn unstructured.Unstructured
err := json.Unmarshal([]byte(demoConfig), &configUn.Object)
assert.Nil(t, err)
err = json.Unmarshal([]byte(demoLive), &liveUn.Object)
assert.Nil(t, err)
dr := Diff(&configUn, &liveUn)
assert.False(t, dr.Modified)
ascii, err := dr.ASCIIFormat(&configUn, formatOpts)
assert.Nil(t, err)
if ascii != "" {
log.Println(ascii)
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment