diff --git a/cmd/mesh-controller/compose_equal_test.go b/cmd/mesh-controller/compose_equal_test.go index 2b782992..5ac92d7c 100644 --- a/cmd/mesh-controller/compose_equal_test.go +++ b/cmd/mesh-controller/compose_equal_test.go @@ -180,20 +180,103 @@ type made struct { OwnSecrets []string } -// madeDiffer says how what two sends made differs. -func madeDiffer(comparison string, old, sdk made) []string { +// madeDiffer says how what two sends made differs, by entry: for each kind, the entries only the controller made, the ones only +// mesh-sdk made, and the ones both made with a different value. Never a whole list. The entries of a machine in refused (a +// machine whose send is refused on either side, which sends nothing) are left out of both. +func madeDiffer(comparison string, old, sdk made, refused map[string]bool) []string { var out []string for _, f := range []struct { name string old, sdk []string + split func(string) (key, value string) + nodes func(string) []string }{ - {"the ports allocated", old.Ports, sdk.Ports}, - {"the pair credentials made", old.Pairs, sdk.Pairs}, - {"the own secrets made", old.OwnSecrets, sdk.OwnSecrets}, + {"the ports allocated", old.Ports, sdk.Ports, splitAt("->"), firstWord}, + {"the pair credentials made", old.Pairs, sdk.Pairs, func(l string) (string, string) { return l, "" }, pairNodes}, + {"the own secrets made", old.OwnSecrets, sdk.OwnSecrets, splitAtLast(" ("), firstWord}, } { - if !sameStrings(f.old, f.sdk) { - out = append(out, fmt.Sprintf("%s: %s differ: the controller's %s, mesh-sdk's %s", comparison, f.name, - strings.Join(f.old, ", "), strings.Join(f.sdk, ", "))) + left, right := entriesOf(f.old, f.split, f.nodes, refused), entriesOf(f.sdk, f.split, f.nodes, refused) + var onlyOld, onlySDK, different []string + for key, v := range left { + if w, both := right[key]; !both { + onlyOld = append(onlyOld, key) + } else if w != v { + different = append(different, fmt.Sprintf("%s (the controller's %s, mesh-sdk's %s)", key, v, w)) + } + } + for key := range right { + if _, both := left[key]; !both { + onlySDK = append(onlySDK, key) + } + } + sort.Strings(onlyOld) + sort.Strings(onlySDK) + sort.Strings(different) + var said []string + if len(onlyOld) > 0 { + said = append(said, "made only by the controller: "+strings.Join(onlyOld, ", ")) + } + if len(onlySDK) > 0 { + said = append(said, "made only by mesh-sdk: "+strings.Join(onlySDK, ", ")) + } + if len(different) > 0 { + said = append(said, "made by both with a different value: "+strings.Join(different, ", ")) + } + if len(said) > 0 { + out = append(out, fmt.Sprintf("%s: %s differ: %s", comparison, f.name, strings.Join(said, "; "))) + } + } + return out +} + +// entriesOf is the entries of a list by key, those of a machine in skip left out. +func entriesOf(lines []string, split func(string) (string, string), nodes func(string) []string, skip map[string]bool) map[string]string { + out := map[string]string{} + for _, l := range lines { + skipped := false + for _, n := range nodes(l) { + skipped = skipped || skip[n] + } + if skipped { + continue + } + k, v := split(l) + out[k] = v + } + return out +} + +func splitAt(sep string) func(string) (string, string) { + return func(l string) (string, string) { + k, v, _ := strings.Cut(l, sep) + return k, v + } +} + +func splitAtLast(sep string) func(string) (string, string) { + return func(l string) (string, string) { + if i := strings.LastIndex(l, sep); i >= 0 { + return l[:i], l[i+1:] + } + return l, "" + } +} + +func firstWord(l string) []string { + f := strings.Fields(l) + if len(f) == 0 { + return nil + } + return f[:1] +} + +// pairNodes are the consumer and the provider of a pair credential line ("name module on consumer from provider local=..."). +func pairNodes(l string) []string { + f := strings.Fields(l) + var out []string + for i := 0; i+1 < len(f); i++ { + if f[i] == "on" || f[i] == "from" { + out = append(out, f[i+1]) } } return out @@ -238,9 +321,21 @@ func TestTheComparisonNamesTheMachineWhoseCompositionDiffers(t *testing.T) { !strings.Contains(got[0], "the refusal differs") { t.Fatalf("a machine one path refuses and the other composes is not named: %v", got) } - // And what a send made. - if got := madeDiffer("the send", made{Ports: []string{"anchor files:80->8080"}}, made{}); len(got) != 1 || - !strings.Contains(got[0], "the ports allocated differ") { - t.Fatalf("a port allocated on one path alone is not named: %v", got) + // And what a send made: only the entries that differ, per machine, never the whole lists. + long := made{Ports: []string{"anchor files:80->8080", "anchor web:81->8081", "laptop db:5432->5432"}, + OwnSecrets: []string{"anchor files/admin (made)", "laptop db/root (made)"}} + other2 := made{Ports: []string{"anchor files:80->8080", "anchor web:81->9091", "laptop db:5432->5432", "laptop cache:6379->6379"}, + OwnSecrets: []string{"anchor files/admin (made)", "laptop db/root (accepted)"}} + got = madeDiffer("the send", long, other2, nil) + if len(got) != 2 || !strings.Contains(got[0], "made only by mesh-sdk: laptop cache:6379") || + !strings.Contains(got[0], "anchor web:81 (the controller's 8081, mesh-sdk's 9091)") || + strings.Contains(got[0], "anchor files:80") || strings.Contains(got[0], "laptop db:5432") || + !strings.Contains(got[1], "laptop db/root (the controller's (made), mesh-sdk's (accepted))") || + strings.Contains(got[1], "anchor files/admin") { + t.Fatalf("the send's differences are not only the differing entries: %v", got) + } + // A machine refused on either side is left out of both. + if got := madeDiffer("the send", long, other2, map[string]bool{"anchor": true, "laptop": true}); len(got) != 0 { + t.Fatalf("a refused machine is compared: %v", got) } }