A container may log to the journal (hq ADR 0179)
A jail reads a log; a container's output went to a file of the runtime's own under a path that changes on recreate, so no jail could read a container's service. logging: journald runs the container with the journal as its driver, named in the spec so moving it recreates it; any other place is refused.
This commit is contained in:
@@ -1607,6 +1607,10 @@ func containerSpecReading(r *declaration.Container, declares, reads map[string]s
|
||||
for _, c := range r.Capabilities {
|
||||
b.WriteString("cap " + c + "\n")
|
||||
}
|
||||
// And where it logs (ADR 0179): the runtime cannot move a running container's output.
|
||||
if r.Logging != "" {
|
||||
b.WriteString("log " + r.Logging + "\n")
|
||||
}
|
||||
// The cadence is part of what was declared, so a changed schedule is a changed spec — the marker
|
||||
// moves and the install is reported "updated" and re-established. Added only when present, so no
|
||||
// ordinary container's or run-once step's digest moves for a field it does not set.
|
||||
@@ -1804,6 +1808,11 @@ func applyContainer(ctx context.Context, r *declaration.Container, run Runner,
|
||||
for _, c := range r.Capabilities {
|
||||
args = append(args, "--cap-add", c)
|
||||
}
|
||||
if r.Logging != "" {
|
||||
// The journal keeps the container's name on every line (CONTAINER_NAME), which is what a
|
||||
// jail matches on (novox/hq ADR 0179); `docker logs` keeps working against the journal.
|
||||
args = append(args, "--log-driver", r.Logging)
|
||||
}
|
||||
for _, d := range r.Dns {
|
||||
args = append(args, "--dns", d)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package apply
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/novox/mesh-host/internal/declaration"
|
||||
"github.com/novox/mesh-host/internal/store"
|
||||
)
|
||||
|
||||
// A container declared to log to the journal is run with the journal as its log driver, and the
|
||||
// place it logs is part of its spec, so moving it recreates the container (novox/hq ADR 0179).
|
||||
func TestAContainerLoggingToTheJournalIsRunThatWayAndRecreatedWhenMoved(t *testing.T) {
|
||||
pinned := "postgres@sha256:" + strings.Repeat("a", 64)
|
||||
var ran []string
|
||||
run := func(_ context.Context, cmd string, args ...string) (string, error) {
|
||||
if cmd == "docker" && len(args) > 0 && args[0] == "run" {
|
||||
ran = args
|
||||
return "deadbeef\n", nil
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
d := parseTrusted(t, `{"declaration":1,"resources":[
|
||||
{"id":"front","type":"container","name":"front","image":"`+pinned+`","logging":"journald"}
|
||||
]}`)
|
||||
_, _, _ = Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, run, nil, nil)
|
||||
sent := false
|
||||
for i, a := range ran {
|
||||
if a == "--log-driver" && i+1 < len(ran) && ran[i+1] == "journald" {
|
||||
sent = true
|
||||
}
|
||||
}
|
||||
if !sent {
|
||||
t.Fatalf("the container's output was not sent to the journal: %v", ran)
|
||||
}
|
||||
with := d.Resources[0].(*declaration.Container)
|
||||
without := *with
|
||||
without.Logging = ""
|
||||
if containerSpec(with, inputs{}) == containerSpec(&without, inputs{}) {
|
||||
t.Fatal("where a container logs is not part of its spec, so moving it would not recreate it")
|
||||
}
|
||||
}
|
||||
@@ -952,6 +952,14 @@ type Container struct {
|
||||
// container; a privileged container stays undeclarable.
|
||||
Capabilities []string `json:"capabilities,omitempty"`
|
||||
|
||||
// Logging names where the runtime sends this container's output: "journald" sends it to the
|
||||
// machine's journal, under the container's name, where what reads the machine's logs — its
|
||||
// intrusion prevention first of all (novox/hq ADR 0179) — can read it the way it reads the
|
||||
// machine's own services. Empty keeps the runtime's default, which is a file of the runtime's
|
||||
// own that nothing but the runtime reads. Part of the spec: a container that logs elsewhere
|
||||
// is a different container, and the runtime cannot change a running one's driver.
|
||||
Logging string `json:"logging,omitempty"`
|
||||
|
||||
// Networks are networks this container also joins once created, by name — a found network a
|
||||
// per-machine setting keeps for a taken container (novox/hq ADR 0163, rule 4), so a
|
||||
// neighbour that resolves it there keeps resolving it until the neighbour is taken too.
|
||||
@@ -1057,6 +1065,10 @@ func (c *Container) validate(where string, _ bool) []string {
|
||||
"capability's name (CAP_NET_ADMIN or NET_ADMIN)")
|
||||
}
|
||||
}
|
||||
if c.Logging != "" && c.Logging != "journald" {
|
||||
problems = append(problems, where+": logging is "+strconv.Quote(c.Logging)+", and the only place a "+
|
||||
"container's output can be sent besides the runtime's own file is \"journald\"")
|
||||
}
|
||||
for _, n := range c.Networks {
|
||||
problems = append(problems, (&Network{Name: n}).validate(where+": networks", false)...)
|
||||
if n == c.Network {
|
||||
|
||||
@@ -524,3 +524,24 @@ func TestACapabilityIsNamedOrRefused(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A container may send its output to the machine's journal, and nowhere else but the runtime's own
|
||||
// file (novox/hq ADR 0179): what reads the machine's logs then reads the container's too.
|
||||
func TestAContainerMayLogToTheJournalAndNowhereElse(t *testing.T) {
|
||||
image := "postgres@sha256:" + strings.Repeat("a", 64)
|
||||
d, err := Parse([]byte(`{"declaration":1,"resources":[
|
||||
{"id":"front","type":"container","name":"front","image":"` + image + `","logging":"journald"}
|
||||
]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := d.Resources[0].(*Container).Logging; got != "journald" {
|
||||
t.Fatalf("logging read as %q", got)
|
||||
}
|
||||
for _, bad := range []string{`"syslog"`, `"none"`, `"json-file"`} {
|
||||
if _, err := Parse([]byte(`{"declaration":1,"resources":[
|
||||
{"id":"front","type":"container","name":"front","image":"` + image + `","logging":` + bad + `}]}`)); err == nil {
|
||||
t.Errorf("%s was accepted as a place to log", bad)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user