Merge pull request 'A merge that shares nothing with the assembling batch, and whose group waits for no one, is cut at once (hq issue 525, ADR 0304 §1, ADR 0306 §2-3)' (#250) from feat/525-a-lone-merge-skips-the-window into main
This commit was merged in pull request #250.
This commit is contained in:
Vendored
+1
-1
@@ -8,7 +8,7 @@ private addresses. That is acceptable while mesh-controller is private; if it ev
|
||||
a second place to clean besides the catalogue itself.
|
||||
|
||||
mesh-catalog b9de001833b1b61b297182b8e3bcdae1cbdeace9 modules/*/module.json, modules/nats/Dockerfile
|
||||
mesh-host bd5cc6980419c1bd4824be6d58dfebd2381a9de3 examples/foundation-first-node-nats.lock
|
||||
mesh-host a76baa4b47f07a8b2bab065603414530bd02b53a examples/foundation-first-node-nats.lock
|
||||
mesh-sdk 33917f91ac3cc213b879b32f3c70e2aa70395f98 conformance/events/module-event.json
|
||||
|
||||
To move them, from this repository's root, with the two repositories checked out beside it:
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
"type": "file",
|
||||
"path": "/var/lib/mesh-bus-conf/accounts.conf",
|
||||
"mode": "0600",
|
||||
"content": "// The first user list, carried by the installer because at genesis there is no mesh to\n// compose one. A bootstrap credential, rotated with the store's and replaced by the\n// controller's own composition from its first start onward.\naccounts {\n MESH {\n jetstream: enabled\n users = [\n { user: \"controller\", password: \"$2a$10$AHqJgOifIVbU41KmATiMhuXFs8xa7Wl2HuN4UVBCXdN2jIQzjqApy\", permissions: {\n publish: { allow: [\"$JS.ACK.CONTROL.controller.>\", \"$JS.ACK.EVENTS.controller.>\", \"$JS.ACK.DEAD_LETTER_NOTICES.controller.>\", \"$JS.API.>\", \"$KV.mesh-controller_asked.>\", \"$KV.mesh-controller_calls.>\", \"$KV.mesh-controller_hand-acts.>\", \"$KV.mesh-controller_conditions.>\", \"$KV.mesh-controller_condition-history.>\", \"$KV.mesh-controller_lease.>\", \"$KV.SEAT_MESH_BUILD_MACHINE_cancelled.>\", \"$KV.SEAT_NODE_BUILD_AGENT_cancelled.>\", \"_INBOX.enrol.>\", \"mesh.assignment.>\", \"mesh.mod.*.tool.>\", \"mesh.node.>\", \"mesh.seat.mesh-build-machine.accept.>\", \"mesh.seat.node-build-agent.accept.>\", \"mesh.seat.mesh-build-machine.tool.>\", \"mesh.seat.node-build-agent.tool.>\", \"mesh.seat.mesh-controller.event.applied\", \"mesh.seat.mesh-controller.event.built-before\", \"mesh.seat.mesh-controller.event.refused\", \"mesh.seat.mesh-controller.event.rolled-back\", \"mesh.seat.mesh-controller.event.checked\", \"mesh.seat.mesh-controller.event.plan-moved\", \"mesh.seat.mesh-controller.event.condition-raised\", \"mesh.seat.mesh-controller.event.condition-changed\", \"mesh.seat.mesh-controller.event.condition-cleared\", \"mesh.seat.mesh-controller.event.doctor-heartbeat\", \"mesh.seat.mesh-controller.event.secret-replaced\", \"mesh.seat.mesh-controller.event.healer-acted\", \"$SRV.INFO\", \"mesh.seat.node-intrusion-prevention.tool.banned.*\", \"mesh.seat.node-backup.tool.backed-up.*\", \"mesh.seat.node-backup.tool.now.*\", \"mesh.seat.mesh-delivery.tool.stalled\", \"mesh.seat.mesh-delivery.tool.close\", \"mesh.seat.node-launcher.tool.secret.*\", \"mesh.seat.mesh-delivery.tool.release\", \"mesh.seat.mesh-delivery.tool.stop\", \"mesh.seat.mesh-delivery.tool.times\", \"mesh.seat.node-service-manager.tool.restart.*\", \"mesh.seat.mesh-controller.tool.plans\", \"mesh.events.dead.>\", \"mesh.again.>\"] }\n subscribe: { allow: [\"$JS.API.>\", \"$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.>\", \"$JS.EVENT.ADVISORY.CONSUMER.DELETED.>\", \"_DELIVER.controller\", \"_DELIVER.controller.>\", \"_INBOX.controller.>\", \"mesh.control.>\", \"mesh.mod.*.event.provisioner.failing\", \"mesh.mod.*.event.provisioner.recovered\", \"mesh.mod.*.event.provisioner.retirement\", \"mesh.mod.gitea.event.pull.merged\", \"mesh.mod.gitea.event.pull.updated\", \"mesh.mod.mesh-catalog.event.catching-up\", \"mesh.mod.mesh-catalog.event.upgraded\", \"mesh.seat.mesh-build-machine.event.built\", \"mesh.seat.node-build-agent.event.built\", \"mesh.seat.operator-channel.event.decided.mesh-controller\", \"mesh.seat.mesh-controller.tool.>\", \"$SRV.PING\", \"$SRV.INFO\", \"$SRV.PING.mesh-controller\", \"$SRV.PING.mesh-controller.>\", \"$SRV.INFO.mesh-controller\", \"$SRV.INFO.mesh-controller.>\", \"$SRV.STATS\", \"$SRV.STATS.mesh-controller\", \"$SRV.STATS.mesh-controller.>\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n}\n"
|
||||
"content": "// The first user list, carried by the installer because at genesis there is no mesh to\n// compose one. A bootstrap credential, rotated with the store's and replaced by the\n// controller's own composition from its first start onward.\naccounts {\n MESH {\n jetstream: enabled\n users = [\n { user: \"controller\", password: \"$2a$10$AHqJgOifIVbU41KmATiMhuXFs8xa7Wl2HuN4UVBCXdN2jIQzjqApy\", permissions: {\n publish: { allow: [\"$JS.ACK.CONTROL.controller.>\", \"$JS.ACK.EVENTS.controller.>\", \"$JS.ACK.DEAD_LETTER_NOTICES.controller.>\", \"$JS.API.>\", \"$KV.mesh-controller_asked.>\", \"$KV.mesh-controller_calls.>\", \"$KV.mesh-controller_hand-acts.>\", \"$KV.mesh-controller_conditions.>\", \"$KV.mesh-controller_condition-history.>\", \"$KV.mesh-controller_lease.>\", \"$KV.SEAT_MESH_BUILD_MACHINE_cancelled.>\", \"$KV.SEAT_NODE_BUILD_AGENT_cancelled.>\", \"_INBOX.enrol.>\", \"mesh.assignment.>\", \"mesh.mod.*.tool.>\", \"mesh.node.>\", \"mesh.seat.mesh-build-machine.accept.>\", \"mesh.seat.node-build-agent.accept.>\", \"mesh.seat.mesh-build-machine.tool.>\", \"mesh.seat.node-build-agent.tool.>\", \"mesh.seat.mesh-controller.event.applied\", \"mesh.seat.mesh-controller.event.built-before\", \"mesh.seat.mesh-controller.event.refused\", \"mesh.seat.mesh-controller.event.rolled-back\", \"mesh.seat.mesh-controller.event.checked\", \"mesh.seat.mesh-controller.event.plan-moved\", \"mesh.seat.mesh-controller.event.condition-raised\", \"mesh.seat.mesh-controller.event.condition-changed\", \"mesh.seat.mesh-controller.event.condition-cleared\", \"mesh.seat.mesh-controller.event.doctor-heartbeat\", \"mesh.seat.mesh-controller.event.secret-replaced\", \"mesh.seat.mesh-controller.event.healer-acted\", \"$SRV.INFO\", \"mesh.seat.node-intrusion-prevention.tool.banned.*\", \"mesh.seat.node-backup.tool.backed-up.*\", \"mesh.seat.node-backup.tool.now.*\", \"mesh.seat.mesh-delivery.tool.stalled\", \"mesh.seat.mesh-delivery.tool.close\", \"mesh.seat.node-launcher.tool.secret.*\", \"mesh.seat.mesh-delivery.tool.release\", \"mesh.seat.mesh-delivery.tool.stop\", \"mesh.seat.mesh-delivery.tool.times\", \"mesh.seat.mesh-delivery.tool.groups\", \"mesh.seat.node-service-manager.tool.restart.*\", \"mesh.seat.mesh-controller.tool.plans\", \"mesh.events.dead.>\", \"mesh.again.>\"] }\n subscribe: { allow: [\"$JS.API.>\", \"$JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.>\", \"$JS.EVENT.ADVISORY.CONSUMER.DELETED.>\", \"_DELIVER.controller\", \"_DELIVER.controller.>\", \"_INBOX.controller.>\", \"mesh.control.>\", \"mesh.mod.*.event.provisioner.failing\", \"mesh.mod.*.event.provisioner.recovered\", \"mesh.mod.*.event.provisioner.retirement\", \"mesh.mod.gitea.event.pull.merged\", \"mesh.mod.gitea.event.pull.updated\", \"mesh.mod.mesh-catalog.event.catching-up\", \"mesh.mod.mesh-catalog.event.upgraded\", \"mesh.seat.mesh-build-machine.event.built\", \"mesh.seat.node-build-agent.event.built\", \"mesh.seat.operator-channel.event.decided.mesh-controller\", \"mesh.seat.mesh-controller.tool.>\", \"$SRV.PING\", \"$SRV.INFO\", \"$SRV.PING.mesh-controller\", \"$SRV.PING.mesh-controller.>\", \"$SRV.INFO.mesh-controller\", \"$SRV.INFO.mesh-controller.>\", \"$SRV.STATS\", \"$SRV.STATS.mesh-controller\", \"$SRV.STATS.mesh-controller.>\"] }\n allow_responses: { max: 1, ttl: \"1m\" }\n } }\n ]\n }\n}\n"
|
||||
},
|
||||
{
|
||||
"id": "broker",
|
||||
|
||||
@@ -31,6 +31,13 @@ package apply
|
||||
// 5. **A mount source that is a refused directory or a refused access is refused with it**: the container
|
||||
// would otherwise bind the very path the engine would not place, and the runtime creates a missing one
|
||||
// as root.
|
||||
// 6. **A file is judged as a directory is** (novox/hq issue 495): never one of the machine's own roots, nor
|
||||
// in a tree nothing is placed in (rule 2), and never below a directory or an access refused here. A file
|
||||
// or an archive below a refused path is refused with it, whichever module declares it and in whatever
|
||||
// order. Rules 3 and 4 are a directory's owner and do not reach a file: a file below /etc is the mesh's
|
||||
// ordinary configuration, and one below a home is judged by home_links.go and agent_home_guard.go.
|
||||
// A refused directory also gates the rest of its module, as a step that did not complete does
|
||||
// (apply.go's `gated`): what a module declares after its directory was written for that directory.
|
||||
//
|
||||
// Each is a failed resource with its reason in the node's report; nothing is touched. Paths are judged as
|
||||
// declared and again with every link in them resolved, so a link at /srv/x pointing at /etc places nothing.
|
||||
@@ -400,6 +407,7 @@ const (
|
||||
placingDirectory placing = iota
|
||||
placingAccess
|
||||
placingMount
|
||||
placingFile
|
||||
)
|
||||
|
||||
// refusePath says why a path is not placed or mounted; nil when it may be. module is the resource's module,
|
||||
@@ -466,7 +474,7 @@ func refuseOne(path string, kind placing, module, owner string) *PlacementRefuse
|
||||
for _, part := range strings.Split(path, "/") {
|
||||
ssh = ssh || part == ".ssh"
|
||||
}
|
||||
if ssh && kind != placingDirectory {
|
||||
if ssh && (kind == placingAccess || kind == placingMount) {
|
||||
return &PlacementRefusedError{Path: path, Why: "it is an account's .ssh, which holds its keys and who may " +
|
||||
"log in as it, and is never an access or a mount"}
|
||||
}
|
||||
@@ -502,8 +510,12 @@ func moduleOfID(id string) string {
|
||||
return module
|
||||
}
|
||||
|
||||
// refusedPlaces judges every directory and access of a declaration before anything is applied, and answers
|
||||
// each refusal by path: what is refused is refused again as a container's mount source.
|
||||
// refusedPlaces judges every directory, access, file and archive of a declaration before anything is applied,
|
||||
// and answers each refusal by path: what is refused is refused again as a container's mount source.
|
||||
//
|
||||
// Directories and accesses first, so a file or an archive below one of them is refused with it whatever order
|
||||
// they are declared in (novox/hq issue 495): the engine refused Docker's data root as a directory and then wrote
|
||||
// a file into it, because nothing judged the file's path.
|
||||
func refusedPlaces(resources []declaration.Resource) map[string]error {
|
||||
out := map[string]error{}
|
||||
for _, r := range resources {
|
||||
@@ -520,9 +532,60 @@ func refusedPlaces(resources []declaration.Resource) map[string]error {
|
||||
out[filepath.Clean(r.Target())] = err
|
||||
}
|
||||
}
|
||||
trees := make(map[string]error, len(out))
|
||||
for path, err := range out {
|
||||
trees[path] = err
|
||||
}
|
||||
for _, r := range resources {
|
||||
var err error
|
||||
switch res := r.(type) {
|
||||
case *declaration.File:
|
||||
if err = belowRefused(res.Path, trees); err == nil {
|
||||
err = refusePath(res.Path, placingFile, moduleOfID(res.ID), "")
|
||||
}
|
||||
case *declaration.Archive:
|
||||
// Only below a refused path: where an archive may unpack is otherwise the archive's own judgement
|
||||
// (archive.go), and the engine's own module unpacks its versions into its own tree.
|
||||
err = belowRefused(res.Path, trees)
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
out[filepath.Clean(r.Target())] = err
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// belowRefused says why a path below a refused directory or access is refused with it; nil when it is below
|
||||
// none. Judged as declared and with its links resolved, as refusePath judges.
|
||||
func belowRefused(path string, refused map[string]error) error {
|
||||
clean := filepath.Clean(path)
|
||||
if !filepath.IsAbs(clean) {
|
||||
return nil
|
||||
}
|
||||
for _, p := range []string{clean, resolved(clean)} {
|
||||
// The deepest refused tree it is below, so the same declaration always gives the same reason.
|
||||
var tree string
|
||||
for t := range refused {
|
||||
if below(p, t) && len(t) > len(tree) {
|
||||
tree = t
|
||||
}
|
||||
}
|
||||
if tree == "" {
|
||||
continue
|
||||
}
|
||||
why := refused[tree]
|
||||
reason := why.Error()
|
||||
var refusal *PlacementRefusedError
|
||||
if errors.As(why, &refusal) {
|
||||
reason = refusal.Why
|
||||
}
|
||||
return &PlacementRefusedError{Path: clean, Why: "it is below " + tree + ", which is refused: " + reason}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// refuseMounts says why a container's mounts are not made; nil when they may be.
|
||||
func refuseMounts(c *declaration.Container, refused map[string]error) error {
|
||||
for _, v := range c.Volumes {
|
||||
|
||||
Reference in New Issue
Block a user