Unify trunk on main: initialization → main #3
+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