Unify trunk on main: initialization → main #3
@@ -812,6 +812,13 @@ func applyContainer(ctx context.Context, r *declaration.Container, run Runner) (
|
||||
for _, v := range r.Volumes {
|
||||
args = append(args, "--volume", v)
|
||||
}
|
||||
for _, n := range r.Nameservers {
|
||||
// Per container rather than by changing the machine's resolver configuration. That file
|
||||
// belongs to something else on most machines, and a host that edited it would be fighting
|
||||
// whatever owns it on every boot — the fault this host exists to avoid, in the one place
|
||||
// it would be hardest to see.
|
||||
args = append(args, "--dns", n)
|
||||
}
|
||||
args = append(args, r.Image)
|
||||
args = append(args, r.Args...)
|
||||
|
||||
|
||||
@@ -1224,3 +1224,75 @@ func TestAFailedActionStopsWhatFollows(t *testing.T) {
|
||||
t.Errorf("the message does not say the rest was not tried: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// A container is told which resolver to use, because it does not inherit the machine's names.
|
||||
//
|
||||
// A container gets its own `/etc/hosts` holding its own hostname, and a runtime rewrites
|
||||
// `resolv.conf` — so every internal name the mesh wrote for the machine is invisible to what the
|
||||
// machine is running. That was hit for real: a database client on one node could not resolve
|
||||
// another node, on a mesh where both names were correct and present on both machines.
|
||||
func TestAContainerIsToldWhichResolverToUse(t *testing.T) {
|
||||
var ran []string
|
||||
run := func(_ context.Context, name string, args ...string) (string, error) {
|
||||
if name != "docker" {
|
||||
return "", errors.New("not installed")
|
||||
}
|
||||
switch args[0] {
|
||||
case "info":
|
||||
return "29.0.0\n", nil
|
||||
case "inspect":
|
||||
return "false\t\n", errors.New("no such container")
|
||||
case "run":
|
||||
ran = args
|
||||
return "deadbeef\n", nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
d := parseTrusted(t, `{"declaration":1,"resources":[
|
||||
{"id":"app","type":"container","name":"app","image":"`+pinned+`",
|
||||
"nameservers":["10.42.0.1"]}
|
||||
]}`)
|
||||
_, _, _ = Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil)
|
||||
|
||||
var told bool
|
||||
for i, a := range ran {
|
||||
if a == "--dns" && i+1 < len(ran) && ran[i+1] == "10.42.0.1" {
|
||||
told = true
|
||||
}
|
||||
}
|
||||
if !told {
|
||||
t.Fatalf("the container was not told where to resolve names: %v", ran)
|
||||
}
|
||||
}
|
||||
|
||||
// And a container that was told nothing is run exactly as before: most containers resolve
|
||||
// whatever the machine resolves, and passing an empty flag would be a change of behaviour
|
||||
// dressed as a default.
|
||||
func TestAContainerToldNothingIsRunAsBefore(t *testing.T) {
|
||||
var ran []string
|
||||
run := func(_ context.Context, name string, args ...string) (string, error) {
|
||||
if name != "docker" {
|
||||
return "", errors.New("not installed")
|
||||
}
|
||||
switch args[0] {
|
||||
case "info":
|
||||
return "29.0.0\n", nil
|
||||
case "inspect":
|
||||
return "false\t\n", errors.New("no such container")
|
||||
case "run":
|
||||
ran = args
|
||||
return "deadbeef\n", nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
d := parseTrusted(t, `{"declaration":1,"resources":[
|
||||
{"id":"app","type":"container","name":"app","image":"`+pinned+`"}
|
||||
]}`)
|
||||
_, _, _ = Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil)
|
||||
|
||||
for _, a := range ran {
|
||||
if a == "--dns" {
|
||||
t.Fatalf("a container that was told nothing was given a resolver anyway: %v", ran)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,6 +328,19 @@ type Container struct {
|
||||
Ports []string `json:"ports,omitempty"`
|
||||
Volumes []string `json:"volumes,omitempty"`
|
||||
Args []string `json:"args,omitempty"`
|
||||
// Nameservers this container resolves through.
|
||||
//
|
||||
// **Because a container does not inherit the machine's names.** It gets its own `/etc/hosts`
|
||||
// holding its own hostname, and a runtime rewrites `resolv.conf` — so every internal name the
|
||||
// mesh wrote for this machine is invisible to the thing the machine is running. That was hit
|
||||
// for real: a database client on one node could not resolve another node, on a mesh where
|
||||
// both names were correct and present.
|
||||
//
|
||||
// Set by the mesh rather than by a module: which resolver a machine has is a fact about the
|
||||
// machine, and a module that named one would be a module that only runs where somebody put
|
||||
// that resolver.
|
||||
Nameservers []string `json:"nameservers,omitempty"`
|
||||
|
||||
// Network is the container's network, passed to the runtime unchanged.
|
||||
//
|
||||
// Needed because the control plane must reach the store and the broker on the machine it was
|
||||
|
||||
Reference in New Issue
Block a user