mirror of https://github.com/linkerd/linkerd2.git
220 lines
4.5 KiB
Go
220 lines
4.5 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"sigs.k8s.io/yaml"
|
|
)
|
|
|
|
type (
|
|
manifest = map[string]interface{}
|
|
|
|
diff struct {
|
|
path []string
|
|
a interface{}
|
|
b interface{}
|
|
}
|
|
)
|
|
|
|
func splitManifests(manifest string) []string {
|
|
manifests := strings.Split(manifest, "\n---\n")
|
|
filtered := []string{}
|
|
for _, m := range manifests {
|
|
if !isManifestEmpty(m) {
|
|
filtered = append(filtered, m)
|
|
}
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
func isManifestEmpty(manifest string) bool {
|
|
lines := strings.Split(manifest, "\n")
|
|
for _, line := range lines {
|
|
if line == "" || strings.HasPrefix(line, "#") || line == "---" {
|
|
continue
|
|
}
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func manifestKey(m manifest) string {
|
|
kind := m["kind"].(string)
|
|
meta := m["metadata"].(map[string]interface{})
|
|
name := meta["name"].(string)
|
|
return fmt.Sprintf("%s/%s", kind, name)
|
|
}
|
|
|
|
func (d diff) String() string {
|
|
expected, _ := yaml.Marshal(d.a)
|
|
actual, _ := yaml.Marshal(d.b)
|
|
return fmt.Sprintf("Diff at [%s]:\nExpected:\n%s\nActual:\n%s", d.path, string(expected), string(actual))
|
|
}
|
|
|
|
func parseManifestList(in string) map[string]manifest {
|
|
manifestList := splitManifests(in)
|
|
manifestMap := map[string]manifest{}
|
|
for _, m := range manifestList {
|
|
manifest := manifest{}
|
|
yaml.Unmarshal([]byte(m), &manifest)
|
|
manifestMap[manifestKey(manifest)] = manifest
|
|
}
|
|
return manifestMap
|
|
}
|
|
|
|
func diffManifest(a manifest, b manifest, path []string) []diff {
|
|
diffs := []diff{}
|
|
for k, v := range a {
|
|
bv, bvExists := b[k]
|
|
switch val := v.(type) {
|
|
case manifest:
|
|
if !bvExists {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, k),
|
|
a: val,
|
|
b: nil,
|
|
})
|
|
} else {
|
|
bvm, ok := bv.(manifest)
|
|
if !ok {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, k),
|
|
a: val,
|
|
b: bv,
|
|
})
|
|
} else {
|
|
diffs = append(diffs, diffManifest(val, bvm, extend(path, k))...)
|
|
}
|
|
}
|
|
case []interface{}:
|
|
bva, ok := bv.([]interface{})
|
|
if !ok {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, k),
|
|
a: val,
|
|
b: bv,
|
|
})
|
|
} else if len(val) != len(bva) {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, k),
|
|
a: val,
|
|
b: bva,
|
|
})
|
|
} else {
|
|
diffs = append(diffs, diffArray(val, bva, extend(path, k))...)
|
|
}
|
|
default:
|
|
if !bvExists {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, k),
|
|
a: val,
|
|
b: nil,
|
|
})
|
|
} else {
|
|
if !reflect.DeepEqual(val, bv) {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, k),
|
|
a: val,
|
|
b: bv,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for k, v := range b {
|
|
_, avExists := a[k]
|
|
if !avExists {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, k),
|
|
a: nil,
|
|
b: v,
|
|
})
|
|
}
|
|
}
|
|
return diffs
|
|
}
|
|
|
|
func diffArray(a, b []interface{}, path []string) []diff {
|
|
diffs := []diff{}
|
|
for i, v := range a {
|
|
switch aVal := v.(type) {
|
|
case manifest:
|
|
bm, ok := b[i].(manifest)
|
|
if !ok {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, fmt.Sprintf("%d", i)),
|
|
a: aVal,
|
|
b: b[i],
|
|
})
|
|
} else {
|
|
diffs = append(diffs, diffManifest(aVal, bm, extend(path, fmt.Sprintf("%d", i)))...)
|
|
}
|
|
case []interface{}:
|
|
ba, ok := b[i].([]interface{})
|
|
if !ok {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, fmt.Sprintf("%d", i)),
|
|
a: aVal,
|
|
b: b[i],
|
|
})
|
|
} else if len(aVal) != len(ba) {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, fmt.Sprintf("%d", i)),
|
|
a: aVal,
|
|
b: b[i],
|
|
})
|
|
} else {
|
|
diffs = append(diffs, diffArray(aVal, ba, extend(path, fmt.Sprintf("%d", i)))...)
|
|
}
|
|
default:
|
|
if !reflect.DeepEqual(v, b[i]) {
|
|
diffs = append(diffs, diff{
|
|
path: extend(path, fmt.Sprintf("%d", i)),
|
|
a: v,
|
|
b: b[i],
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return diffs
|
|
}
|
|
|
|
func diffManifestLists(a map[string]manifest, b map[string]manifest) map[string][]diff {
|
|
diffs := map[string][]diff{}
|
|
for k, am := range a {
|
|
bm, ok := b[k]
|
|
if !ok {
|
|
diffs[k] = []diff{{
|
|
a: am,
|
|
b: nil,
|
|
path: []string{},
|
|
}}
|
|
} else {
|
|
diffs[k] = diffManifest(am, bm, []string{})
|
|
}
|
|
}
|
|
for k, bm := range b {
|
|
_, ok := a[k]
|
|
if !ok {
|
|
diffs[k] = []diff{{
|
|
a: nil,
|
|
b: bm,
|
|
path: []string{},
|
|
}}
|
|
}
|
|
}
|
|
return diffs
|
|
}
|
|
|
|
// extend returns a new slice which is a copy of slice with next appended to it.
|
|
// The advantage of using extend instead of append is that modifying the
|
|
// returned slice will not modify the original.
|
|
func extend(slice []string, next string) []string {
|
|
new := make([]string, len(slice)+1)
|
|
copy(new, slice)
|
|
new[len(slice)] = next
|
|
return new
|
|
}
|