A machine says which ports it already holds
novox/hq ADR 0038 and 04-ISSUES/028. The substrate is not a module: a node raises it from the bundle it carries before any mesh exists, so the control plane has never heard of the store, the broker, or the control plane's own container. A module assigned afterwards is handed a port one of them holds, and finds out from a container runtime three layers down. The host already recorded which resources it carried and which the mesh sent — that distinction exists so the two never remove each other. It now also records what each one binds, and reports the carried ones. What the declaration binds, not what is open. A machine's open ports are a moving target — something a person started, a connection the kernel handed out — and assigning around those would mean a port that was free when it was asked for and taken when it was used. What a resource declares is stable, and it is the half the mesh can be responsible for. Only the carried ones are reported. What the mesh put here it already knows, and reporting it back would make the machine an authority on the mesh's own bookkeeping.
This commit is contained in:
+29
-1
@@ -15,6 +15,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"text/tabwriter"
|
||||
@@ -733,7 +734,7 @@ func applyAndKeep(ctx context.Context, opts options, raw []byte, signed *store.D
|
||||
saveErr.Error()}
|
||||
}
|
||||
|
||||
report := link.Report{}
|
||||
report := link.Report{Carried: carriedPorts(updated)}
|
||||
for _, change := range outcome.Outcomes {
|
||||
report.Applied = append(report.Applied, change.ID)
|
||||
}
|
||||
@@ -798,3 +799,30 @@ func sealOpener(statePath string) apply.Unseal {
|
||||
return key.Unseal(sealed)
|
||||
}
|
||||
}
|
||||
|
||||
// carriedPorts is every machine port held by what this host raised from its own bundle.
|
||||
//
|
||||
// **What the mesh must assign around** (novox/hq ADR 0038). The substrate is not a module: a node
|
||||
// raises it before any mesh exists, so the control plane has never heard of the store or the
|
||||
// broker. Told this, it can put a module somewhere else; not told, it hands out a port one of them
|
||||
// holds and finds out from a container runtime.
|
||||
//
|
||||
// Only what was carried. What the mesh itself put here it already knows about, and reporting it
|
||||
// back would make the machine an authority on the mesh's own bookkeeping.
|
||||
func carriedPorts(state store.State) []int {
|
||||
seen := map[int]bool{}
|
||||
var out []int
|
||||
for _, applied := range state.Resources {
|
||||
if applied.Origin == store.OriginDeclared {
|
||||
continue
|
||||
}
|
||||
for _, port := range applied.Holds {
|
||||
if !seen[port] {
|
||||
seen[port] = true
|
||||
out = append(out, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Ints(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -204,6 +204,7 @@ func Apply(
|
||||
ID: resource.Identity(), Type: string(resource.Kind()),
|
||||
Target: outcome.Target, AppliedAt: time.Now().UTC(),
|
||||
Wrote: outcome.wrote,
|
||||
Holds: holds(resource),
|
||||
})
|
||||
report.Outcomes = append(report.Outcomes, outcome)
|
||||
if outcome.Action != "unchanged" {
|
||||
@@ -1063,3 +1064,31 @@ func digestOf(content string) string {
|
||||
sum := sha256.Sum256([]byte(content))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
// holds is the machine's own ports a resource occupies.
|
||||
//
|
||||
// **What the declaration binds, not what is open.** A machine's open ports are a moving target —
|
||||
// something a person started, a connection the kernel handed out — and assigning around them would
|
||||
// mean a port that was free when it was asked for and taken when it was used. What a resource
|
||||
// declares is stable, and it is the half the mesh can be responsible for.
|
||||
func holds(resource declaration.Resource) []int {
|
||||
container, ok := resource.(*declaration.Container)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
var out []int
|
||||
for _, mapping := range container.Ports {
|
||||
// "8080:80", or "127.0.0.1:8080:80" when an address was named. The machine's port is the
|
||||
// one before the last colon; the last is inside the container and is not the machine's.
|
||||
parts := strings.Split(mapping, ":")
|
||||
if len(parts) < 2 {
|
||||
continue
|
||||
}
|
||||
port, err := strconv.Atoi(strings.TrimSpace(parts[len(parts)-2]))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, port)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -56,4 +56,15 @@ type Report struct {
|
||||
|
||||
// Refused is set when the declaration was rejected whole rather than applied in part.
|
||||
Refused string `json:"refused,omitempty"`
|
||||
|
||||
// Carried are the machine's ports held by what this host raised from its own bundle.
|
||||
//
|
||||
// **So the mesh can assign around what it did not put here** (novox/hq ADR 0038). A node
|
||||
// raises its substrate before any mesh exists, so the control plane has never heard of the
|
||||
// store or the broker — and would hand a module a port one of them holds, discovering it only
|
||||
// when a container runtime refused to start.
|
||||
//
|
||||
// A node *states* and the mesh writes, which is the whole shape of this message: this is the
|
||||
// machine saying what is true of it, not asking for anything.
|
||||
Carried []int `json:"carried,omitempty"`
|
||||
}
|
||||
|
||||
@@ -43,6 +43,18 @@ type Applied struct {
|
||||
// Empty means carried, for state written before this field existed: everything a host had
|
||||
// applied at that point came from its bundle.
|
||||
Origin string `json:"origin,omitempty"`
|
||||
|
||||
// Holds are the machine's own ports this resource occupies.
|
||||
//
|
||||
// **So the mesh can assign around what it did not put here** (novox/hq ADR 0038). A node
|
||||
// raises its substrate from the bundle before any mesh exists, so the control plane has never
|
||||
// heard of the store, the broker or the control plane's own container — and a module assigned
|
||||
// afterwards would be given a port one of them already holds, and would be told so by a
|
||||
// container runtime rather than by anything that could have prevented it.
|
||||
//
|
||||
// Recorded per resource rather than counted per machine, because what a machine happens to
|
||||
// have open right now is a moving target, and what its declaration binds is not.
|
||||
Holds []int `json:"holds,omitempty"`
|
||||
// Target is what was changed — a path, a unit — so removal knows what to undo without
|
||||
// re-reading a declaration that may no longer exist.
|
||||
Target string `json:"target"`
|
||||
|
||||
Reference in New Issue
Block a user