package catalogue import ( "strings" "testing" ) // Keyed by the internal name, as the control plane hands them (issue 079). var threeMachines = map[string]string{"homer.internal": "10.42.0.1", "marge.internal": "10.42.0.2", "bart.internal": ""} // **`*.homer.internal` is homer. That is the whole rule.** func TestEveryMachineIsAWildcardUnderItsOwnName(t *testing.T) { out := nodeZones(Resolution{Node: "homer"}, threeMachines, "") for _, want := range []string{ "address=/homer.internal/10.42.0.1", "address=/marge.internal/10.42.0.2", } { if !strings.Contains(out, want) { t.Fatalf("missing %q:\n%s", want, out) } } } // A machine the mesh has a record for and cannot place is left out of both. // // **Not an oversight — the alternative is worse.** A name written with no address resolves to // nothing, and a connection to that hangs. Leaving it out fails at once and says the name is // unknown, which is a thing somebody can act on. func TestAMachineWithNoAddressIsNotNamed(t *testing.T) { for _, out := range []string{ nodeNames(Resolution{Node: "homer"}, threeMachines, ""), nodeZones(Resolution{Node: "homer"}, threeMachines, ""), } { if strings.Contains(out, "bart") { t.Fatalf("a machine with no address was named, so its name resolves to nothing:\n%s", out) } } } // A machine's own mesh name points at its address on the private network, not at loopback — or a // service binding the name it was given is unreachable from everywhere else. func TestAMachinesOwnNameIsItsMeshAddress(t *testing.T) { out := nodeNames(Resolution{Node: "homer"}, threeMachines, "") var line string for _, l := range strings.Split(out, "\n") { if strings.Contains(l, "homer.internal") { line = l } } if !strings.HasPrefix(line, "10.42.0.1") { t.Fatalf("a machine's own mesh name is not its mesh address: %q", line) } // And the loopback floor is still there, or things with nothing to do with the mesh break. if !strings.Contains(out, "127.0.0.1\tlocalhost") { t.Fatalf("the loopback floor was removed:\n%s", out) } } // A module says where it wants a fact, and is given a file. func TestAModuleIsGivenTheFactsItAskedFor(t *testing.T) { m := Manifest{Module: "dnsmasq", Facts: map[string]string{FactNodeZones: "/etc/mesh/zones.conf"}} given, err := FactsInto(m, Resolution{Node: "homer"}, threeMachines, "") if err != nil { t.Fatal(err) } if len(given) != 1 { t.Fatalf("expected one file, got %d", len(given)) } if given[0]["path"] != "/etc/mesh/zones.conf" || given[0]["type"] != "file" { t.Fatalf("not written where it was asked for: %v", given[0]) } if !strings.Contains(given[0]["content"].(string), "homer.internal") { t.Fatalf("the file does not hold the fact: %v", given[0]["content"]) } } // **Asking for a fact the mesh does not have is refused here, not on a machine.** A daemon that // starts, reads a file nobody wrote, and answers no queries is a much worse way to find out. func TestAskingForAFactTheMeshDoesNotHaveIsRefused(t *testing.T) { m := Manifest{Module: "dnsmasq", Facts: map[string]string{"the-weather": "/etc/weather"}} _, err := FactsInto(m, Resolution{}, nil, "") if err == nil { t.Fatal("a module asked for something nobody computes and was given nothing, silently") } for _, known := range []string{FactNodeNames, FactNodeZones} { if !strings.Contains(err.Error(), known) { t.Fatalf("the refusal does not say what would have worked: %v", err) } } } // And a relative path is refused, or a module decides where the mesh writes on a machine. func TestAFactMustBeAskedForAtAnAbsolutePath(t *testing.T) { m := Manifest{Module: "dnsmasq", Facts: map[string]string{FactNodeNames: "etc/hosts"}} if _, err := FactsInto(m, Resolution{}, nil, ""); err == nil { t.Fatal("a relative path was accepted") } } // **The names the control plane hands a resolution are already internal names** — `homer.internal`, // the same map every container gets as its hosts. Appending the suffix again wrote // `homer.internal.internal` into every hosts file and every resolver's zones, and the large mesh // bed's name test was the first to read it back. Either key gives the same files. func TestNamesKeyedByInternalNameAreNotSuffixedTwice(t *testing.T) { internal := map[string]string{"homer.internal": "10.42.0.1", "marge.internal": "10.42.0.2"} bare := map[string]string{"homer": "10.42.0.1", "marge": "10.42.0.2"} if a, b := nodeZones(Resolution{Node: "homer"}, internal, ""), nodeZones(Resolution{Node: "homer"}, bare, ""); a != b { t.Fatalf("the zones differ by how the names were keyed:\n%s\n---\n%s", a, b) } if a, b := nodeNames(Resolution{Node: "homer"}, internal, ""), nodeNames(Resolution{Node: "homer"}, bare, ""); a != b { t.Fatalf("the hosts differ by how the names were keyed:\n%s\n---\n%s", a, b) } zones := nodeZones(Resolution{Node: "homer"}, internal, "") if strings.Contains(zones, "internal.internal") || !strings.Contains(zones, "address=/homer.internal/10.42.0.1") { t.Fatalf("the zones carry a doubled suffix or miss the name:\n%s", zones) } hosts := nodeNames(Resolution{Node: "homer"}, internal, "") if !strings.Contains(hosts, "10.42.0.1\thomer.internal\thomer\t# this machine") { t.Fatalf("the hosts line for the machine itself is not name, bare name and the mark:\n%s", hosts) } } // The suffix the control plane composed the names with is the one the facts write — an operator // who chose another does not get `.internal` appended to it. func TestTheFactsWriteTheSuffixTheNamesWereComposedWith(t *testing.T) { names := map[string]string{"homer.lan": "10.42.0.1"} zones := nodeZones(Resolution{Node: "homer"}, names, "lan") if !strings.Contains(zones, "address=/homer.lan/10.42.0.1") || strings.Contains(zones, "internal") { t.Fatalf("the zones do not carry the operator's suffix as given:\n%s", zones) } hosts := nodeNames(Resolution{Node: "homer"}, names, "lan") if !strings.Contains(hosts, "10.42.0.1\thomer.lan\thomer\t# this machine") { t.Fatalf("the hosts line does not carry the operator's suffix as given:\n%s", hosts) } }