The profile names the network manager that is running, and travels in every report (hq ADR 0161) #61
+13
-5
@@ -818,10 +818,7 @@ func enrol(ctx context.Context, opts options) error {
|
||||
// control plane cannot decide what a node should run without it, so it travels with the
|
||||
// request instead of being asked for in a second round trip.
|
||||
detected := profile.Detect(ctx, profile.Default(nil), opts.timeout)
|
||||
reported := map[string]any{}
|
||||
if raw, err := json.Marshal(detected); err == nil {
|
||||
_ = json.Unmarshal(raw, &reported)
|
||||
}
|
||||
reported := profileAsReported(detected)
|
||||
|
||||
// Signed with the identity just generated, so the mesh can tell this machine from anyone else
|
||||
// who knows its public key (novox/hq issue 083).
|
||||
@@ -1418,7 +1415,8 @@ func applyAndKeep(ctx context.Context, opts options, raw []byte, signed *store.D
|
||||
sched.Sync(declared, held)
|
||||
}
|
||||
|
||||
report := link.Report{Carried: carriedPorts(updated), Declared: digestOf(raw), Host: runningVersion()}
|
||||
report := link.Report{Carried: carriedPorts(updated), Declared: digestOf(raw), Host: runningVersion(),
|
||||
Profile: profileAsReported(profile.Detect(ctx, profile.Default(nil), opts.timeout))}
|
||||
// Which of this machine's links face outside, for the filter the mesh writes around them
|
||||
// (novox/hq ADR 0140). Reported whatever the node's mode: a converged node's filter needs it,
|
||||
// and an adopted one becomes converged without a further round trip. A machine that cannot read
|
||||
@@ -1630,3 +1628,13 @@ func refuseOlder(kept store.Declared, keptErr error, sequence int64) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// profileAsReported is the profile as the mesh reads it — the same bytes enrolment sends, so a
|
||||
// report's profile and an enrolment's are one shape on the controller's side (novox/hq ADR 0161).
|
||||
func profileAsReported(detected profile.Profile) map[string]any {
|
||||
reported := map[string]any{}
|
||||
if raw, err := json.Marshal(detected); err == nil {
|
||||
_ = json.Unmarshal(raw, &reported)
|
||||
}
|
||||
return reported
|
||||
}
|
||||
|
||||
@@ -110,6 +110,11 @@ type Report struct {
|
||||
// and the mesh's up in its place, and where the found configuration's original was kept.
|
||||
Tunnel *CarriedTunnel `json:"tunnel,omitempty"`
|
||||
|
||||
// Profile is what this machine can do, detected again by the apply that reports (novox/hq
|
||||
// ADR 0161) — the same shape enrolment sends — so a capability gained or lost since enrolment,
|
||||
// a network manager switched, reaches the mesh at the next push rather than never.
|
||||
Profile map[string]any `json:"profile,omitempty"`
|
||||
|
||||
// Host is the version of the host that produced this report (novox/hq ADR 0141).
|
||||
//
|
||||
// Without it nothing can say a machine is behind, so "every machine current with its source"
|
||||
|
||||
@@ -20,6 +20,14 @@ const (
|
||||
// state: whether one IS running. Assignment needs the first.
|
||||
CapSeat = "seat"
|
||||
CapPrivileged = "privileged"
|
||||
|
||||
// The network manager this machine runs, one capability per dialect (novox/hq ADR 0161): the
|
||||
// uplink seat's holder declares its own, so the holder for a manager the machine does not run
|
||||
// is refused the way any missing capability is, naming it. Active, not installed — a machine
|
||||
// may have two of these on disk and runs one.
|
||||
CapUplinkNetworkManager = "uplink-networkmanager"
|
||||
CapUplinkSystemdNetworkd = "uplink-systemd-networkd"
|
||||
CapUplinkDhcpcd = "uplink-dhcpcd"
|
||||
)
|
||||
|
||||
// commandCapability is the shape most detectors take: run something, and treat a working
|
||||
@@ -202,6 +210,21 @@ func Default(runner Runner) []Detector {
|
||||
why: "asks the kernel for interfaces — needs the module, not just the tool",
|
||||
runner: runner,
|
||||
},
|
||||
commandCapability{
|
||||
name: CapUplinkNetworkManager, command: "systemctl", args: []string{"is-active", "NetworkManager.service"},
|
||||
why: "asks the init whether NetworkManager is running — the dialect the uplink seat's holder must speak",
|
||||
runner: runner,
|
||||
},
|
||||
commandCapability{
|
||||
name: CapUplinkSystemdNetworkd, command: "systemctl", args: []string{"is-active", "systemd-networkd.service"},
|
||||
why: "asks the init whether systemd-networkd is running — the dialect the uplink seat's holder must speak",
|
||||
runner: runner,
|
||||
},
|
||||
commandCapability{
|
||||
name: CapUplinkDhcpcd, command: "systemctl", args: []string{"is-active", "dhcpcd.service"},
|
||||
why: "asks the init whether dhcpcd is running — the dialect the uplink seat's holder must speak",
|
||||
runner: runner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package profile
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// The uplink seat's holder must be the dialect the machine runs (novox/hq ADR 0161): the profile
|
||||
// names the network manager found active, one capability per manager, and nothing for one that is
|
||||
// merely installed.
|
||||
func TestTheProfileNamesTheNetworkManagerThatIsRunning(t *testing.T) {
|
||||
runner := func(_ context.Context, name string, args ...string) (string, error) {
|
||||
if name == "systemctl" && len(args) == 2 && args[0] == "is-active" {
|
||||
if args[1] == "NetworkManager.service" {
|
||||
return "active\n", nil
|
||||
}
|
||||
return "inactive\n", errors.New("exit status 3")
|
||||
}
|
||||
return "", errors.New("not here")
|
||||
}
|
||||
var have []Detector
|
||||
for _, d := range Default(Runner(runner)) {
|
||||
switch d.Name() {
|
||||
case CapUplinkNetworkManager, CapUplinkSystemdNetworkd, CapUplinkDhcpcd:
|
||||
have = append(have, d)
|
||||
}
|
||||
}
|
||||
if len(have) != 3 {
|
||||
t.Fatalf("expected a detector per manager, found %d", len(have))
|
||||
}
|
||||
for _, d := range have {
|
||||
v := d.Detect(context.Background())
|
||||
want := d.Name() == CapUplinkNetworkManager
|
||||
if v.Present != want {
|
||||
t.Errorf("%s: present=%v, want %v (%s)", d.Name(), v.Present, want, v.Detail)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user