mesh/merge-gate pass: builds dnsmasq, docker, netcheck → ace, g14, novox, shanks; no bus step; 2 wait(s) for a person; every machine composes with the chan…
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery superseded: a newer head of the same pull request
- The dnsmasq watch restarts only a unit that runs, without waiting for its start; a unit stopped, starting or failed is held and said (C2). - dnsmasq_resolve asks only an address the resolver listens on (C3). - Tests for the watch loop keeping its budget, a watch that wrote nothing or stopped looking, and a unit not running (C4). - A restart is remembered as said only once emitted, restarts made before the bundle started are said, and whether one helped is said (C5). - docker_events reads the runtime's whole buffer: complete when it is not full or reaches past the window, never a quiet machine read as lost (C6). - netcheck: refused read with errors.Is; tests for addresses and ports, and for a container named as docker knows it.
97 lines
3.7 KiB
Go
97 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func archiveOf(t *testing.T, h tar.Header, body string) string {
|
|
t.Helper()
|
|
var buf bytes.Buffer
|
|
w := tar.NewWriter(&buf)
|
|
h.Size = int64(len(body))
|
|
if h.Typeflag == tar.TypeSymlink {
|
|
h.Size = 0
|
|
}
|
|
if err := w.WriteHeader(&h); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if h.Typeflag != tar.TypeSymlink {
|
|
if _, err := w.Write([]byte(body)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
w.Close()
|
|
return buf.String()
|
|
}
|
|
|
|
// Issue 348: route-proxy asked 10.10.0.1 alone while the machine listed both mesh resolvers; its copy
|
|
// of the file could not be read by anything on the mesh.
|
|
func TestAContainersResolverFileIsReadThroughTheArchiveAndComparedWithTheMachines(t *testing.T) {
|
|
inside := "# Generated by Docker Engine.\nnameserver 10.10.0.1\nsearch novox.internal\noptions timeout:1 attempts:2\n"
|
|
f := (&fake{}).on("docker cp route-proxy:/etc/resolv.conf -",
|
|
Ran{Stdout: archiveOf(t, tar.Header{Name: "resolv.conf", Typeflag: tar.TypeReg, Mode: 0o644}, inside)})
|
|
c := client(f, 1000)
|
|
c.ReadFile = func(p string) ([]byte, error) {
|
|
if p != HostResolvConf {
|
|
t.Fatalf("read %s", p)
|
|
}
|
|
return []byte("nameserver 10.10.0.1\nnameserver 10.10.0.2\noptions timeout:1 attempts:2 edns0\n"), nil
|
|
}
|
|
got, err := c.ResolvConfOf(context.Background(), "route-proxy")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rc := got["resolv_conf"].(ResolvConf)
|
|
if strings.Join(rc.Nameservers, ",") != "10.10.0.1" || strings.Join(rc.Search, ",") != "novox.internal" ||
|
|
strings.Join(rc.Options, ",") != "timeout:1,attempts:2" || rc.Raw != inside {
|
|
t.Fatalf("parsed: %+v", rc)
|
|
}
|
|
if got["same_as_machine"] != false || got["note"] == nil {
|
|
t.Fatalf("a copy listing fewer resolvers than the machine is not said: %v", got)
|
|
}
|
|
if f.ran("docker exec") {
|
|
t.Fatal("read by exec, not through the archive")
|
|
}
|
|
}
|
|
|
|
func TestAResolverFileThatCannotBeReadIsSaidPlainly(t *testing.T) {
|
|
c := client((&fake{}).on("docker cp", Ran{Status: 1, Stderr: "Error response from daemon: No such container: nope\n"}), 1000)
|
|
if _, err := c.ResolvConfOf(context.Background(), "nope"); err == nil || !strings.Contains(err.Error(), "could not be read") {
|
|
t.Fatalf("a missing container: %v", err)
|
|
}
|
|
link := archiveOf(t, tar.Header{Name: "resolv.conf", Typeflag: tar.TypeSymlink, Linkname: "/run/systemd/resolve/stub-resolv.conf"}, "")
|
|
c = client((&fake{}).on("docker cp", Ran{Stdout: link}), 1000)
|
|
if _, err := c.ResolvConfOf(context.Background(), "x"); err == nil || !strings.Contains(err.Error(), "link to") {
|
|
t.Fatalf("a link inside: %v", err)
|
|
}
|
|
c = client((&fake{}).on("docker cp", Ran{Stdout: "not a tar"}), 1000)
|
|
if _, err := c.ResolvConfOf(context.Background(), "x"); err == nil {
|
|
t.Fatal("garbage was read as a file")
|
|
}
|
|
ok := archiveOf(t, tar.Header{Name: "resolv.conf", Typeflag: tar.TypeReg, Mode: 0o644}, "nameserver 10.10.0.1\n")
|
|
c = client((&fake{}).on("docker cp", Ran{Stdout: ok}), 1000)
|
|
c.ReadFile = func(string) ([]byte, error) { return nil, errors.New("permission denied") }
|
|
got, err := c.ResolvConfOf(context.Background(), "x")
|
|
if err != nil || !strings.Contains(got["machine"].(string), "could not be read") || got["same_as_machine"] != nil {
|
|
t.Fatalf("an unreadable machine file: %v, %v", got, err)
|
|
}
|
|
}
|
|
|
|
// The container is named as docker knows it, never as something docker would read as an option.
|
|
func TestAResolverFileIsAskedOnlyOfAContainerRef(t *testing.T) {
|
|
f := &fake{}
|
|
for _, ref := range []string{"--privileged", "a b", "", "x;rm"} {
|
|
if _, err := client(f, 1000).ResolvConfOf(context.Background(), ref); err == nil {
|
|
t.Errorf("%q was asked of the runtime", ref)
|
|
}
|
|
}
|
|
if len(f.calls) != 0 {
|
|
t.Fatalf("the runtime was asked: %+v", f.calls)
|
|
}
|
|
}
|