...

Source file src/github.com/linkerd/linkerd2/cli/cmd/test_helper.go

Documentation: github.com/linkerd/linkerd2/cli/cmd

     1  package cmd
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  
     8  	"sigs.k8s.io/yaml"
     9  )
    10  
    11  type (
    12  	manifest = map[string]interface{}
    13  
    14  	diff struct {
    15  		path []string
    16  		a    interface{}
    17  		b    interface{}
    18  	}
    19  )
    20  
    21  func splitManifests(manifest string) []string {
    22  	manifests := strings.Split(manifest, "\n---\n")
    23  	filtered := []string{}
    24  	for _, m := range manifests {
    25  		if !isManifestEmpty(m) {
    26  			filtered = append(filtered, m)
    27  		}
    28  	}
    29  	return filtered
    30  }
    31  
    32  func isManifestEmpty(manifest string) bool {
    33  	lines := strings.Split(manifest, "\n")
    34  	for _, line := range lines {
    35  		if line == "" || strings.HasPrefix(line, "#") || line == "---" {
    36  			continue
    37  		}
    38  		return false
    39  	}
    40  	return true
    41  }
    42  
    43  func manifestKey(m manifest) string {
    44  	kind := m["kind"].(string)
    45  	meta := m["metadata"].(map[string]interface{})
    46  	name := meta["name"].(string)
    47  	return fmt.Sprintf("%s/%s", kind, name)
    48  }
    49  
    50  func (d diff) String() string {
    51  	expected, _ := yaml.Marshal(d.a)
    52  	actual, _ := yaml.Marshal(d.b)
    53  	return fmt.Sprintf("Diff at [%s]:\nExpected:\n%s\nActual:\n%s", d.path, string(expected), string(actual))
    54  }
    55  
    56  func parseManifestList(in string) map[string]manifest {
    57  	manifestList := splitManifests(in)
    58  	manifestMap := map[string]manifest{}
    59  	for _, m := range manifestList {
    60  		manifest := manifest{}
    61  		yaml.Unmarshal([]byte(m), &manifest)
    62  		manifestMap[manifestKey(manifest)] = manifest
    63  	}
    64  	return manifestMap
    65  }
    66  
    67  func diffManifest(a manifest, b manifest, path []string) []diff {
    68  	diffs := []diff{}
    69  	for k, v := range a {
    70  		bv, bvExists := b[k]
    71  		switch val := v.(type) {
    72  		case manifest:
    73  			if !bvExists {
    74  				diffs = append(diffs, diff{
    75  					path: extend(path, k),
    76  					a:    val,
    77  					b:    nil,
    78  				})
    79  			} else {
    80  				bvm, ok := bv.(manifest)
    81  				if !ok {
    82  					diffs = append(diffs, diff{
    83  						path: extend(path, k),
    84  						a:    val,
    85  						b:    bv,
    86  					})
    87  				} else {
    88  					diffs = append(diffs, diffManifest(val, bvm, extend(path, k))...)
    89  				}
    90  			}
    91  		case []interface{}:
    92  			bva, ok := bv.([]interface{})
    93  			if !ok {
    94  				diffs = append(diffs, diff{
    95  					path: extend(path, k),
    96  					a:    val,
    97  					b:    bv,
    98  				})
    99  			} else if len(val) != len(bva) {
   100  				diffs = append(diffs, diff{
   101  					path: extend(path, k),
   102  					a:    val,
   103  					b:    bva,
   104  				})
   105  			} else {
   106  				diffs = append(diffs, diffArray(val, bva, extend(path, k))...)
   107  			}
   108  		default:
   109  			if !bvExists {
   110  				diffs = append(diffs, diff{
   111  					path: extend(path, k),
   112  					a:    val,
   113  					b:    nil,
   114  				})
   115  			} else if !reflect.DeepEqual(val, bv) {
   116  				diffs = append(diffs, diff{
   117  					path: extend(path, k),
   118  					a:    val,
   119  					b:    bv,
   120  				})
   121  			}
   122  		}
   123  	}
   124  	for k, v := range b {
   125  		_, avExists := a[k]
   126  		if !avExists {
   127  			diffs = append(diffs, diff{
   128  				path: extend(path, k),
   129  				a:    nil,
   130  				b:    v,
   131  			})
   132  		}
   133  	}
   134  	return diffs
   135  }
   136  
   137  func diffArray(a, b []interface{}, path []string) []diff {
   138  	diffs := []diff{}
   139  	for i, v := range a {
   140  		switch aVal := v.(type) {
   141  		case manifest:
   142  			bm, ok := b[i].(manifest)
   143  			if !ok {
   144  				diffs = append(diffs, diff{
   145  					path: extend(path, fmt.Sprintf("%d", i)),
   146  					a:    aVal,
   147  					b:    b[i],
   148  				})
   149  			} else {
   150  				diffs = append(diffs, diffManifest(aVal, bm, extend(path, fmt.Sprintf("%d", i)))...)
   151  			}
   152  		case []interface{}:
   153  			ba, ok := b[i].([]interface{})
   154  			if !ok {
   155  				diffs = append(diffs, diff{
   156  					path: extend(path, fmt.Sprintf("%d", i)),
   157  					a:    aVal,
   158  					b:    b[i],
   159  				})
   160  			} else if len(aVal) != len(ba) {
   161  				diffs = append(diffs, diff{
   162  					path: extend(path, fmt.Sprintf("%d", i)),
   163  					a:    aVal,
   164  					b:    b[i],
   165  				})
   166  			} else {
   167  				diffs = append(diffs, diffArray(aVal, ba, extend(path, fmt.Sprintf("%d", i)))...)
   168  			}
   169  		default:
   170  			if !reflect.DeepEqual(v, b[i]) {
   171  				diffs = append(diffs, diff{
   172  					path: extend(path, fmt.Sprintf("%d", i)),
   173  					a:    v,
   174  					b:    b[i],
   175  				})
   176  			}
   177  		}
   178  	}
   179  	return diffs
   180  }
   181  
   182  func diffManifestLists(a map[string]manifest, b map[string]manifest) map[string][]diff {
   183  	diffs := map[string][]diff{}
   184  	for k, am := range a {
   185  		bm, ok := b[k]
   186  		if !ok {
   187  			diffs[k] = []diff{{
   188  				a:    am,
   189  				b:    nil,
   190  				path: []string{},
   191  			}}
   192  		} else {
   193  			diffs[k] = diffManifest(am, bm, []string{})
   194  		}
   195  	}
   196  	for k, bm := range b {
   197  		_, ok := a[k]
   198  		if !ok {
   199  			diffs[k] = []diff{{
   200  				a:    nil,
   201  				b:    bm,
   202  				path: []string{},
   203  			}}
   204  		}
   205  	}
   206  	return diffs
   207  }
   208  
   209  // extend returns a new slice which is a copy of slice with next appended to it.
   210  // The advantage of using extend instead of append is that modifying the
   211  // returned slice will not modify the original.
   212  func extend(slice []string, next string) []string {
   213  	new := make([]string, len(slice)+1)
   214  	copy(new, slice)
   215  	new[len(slice)] = next
   216  	return new
   217  }
   218  

View as plain text