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
 | |
| }
 |