Read an adopted or converged node's mode from its state on a re-run of genesis, and refuse a flag that disagrees (hq ADR 0103)
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/novox/mesh-host/internal/declaration"
|
||||||
"github.com/novox/mesh-host/internal/reachable"
|
"github.com/novox/mesh-host/internal/reachable"
|
||||||
"github.com/novox/mesh-host/internal/store"
|
"github.com/novox/mesh-host/internal/store"
|
||||||
)
|
)
|
||||||
@@ -76,6 +77,20 @@ func RefuseAMachineInUse(ctx context.Context, o Options, run Runner, say func(st
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(known.Resources) > 0 {
|
if len(known.Resources) > 0 {
|
||||||
|
// **The machine says how it was raised** (novox/hq ADR 0103). A re-run must not change
|
||||||
|
// the node's mode by a flag forgotten or added: without --adopted the bundle would load
|
||||||
|
// the foundation's dropping filter over the found firewall, and with it on a converged
|
||||||
|
// machine the filter the node relies on would be removed as no longer carried.
|
||||||
|
switch adopted := RecordsAdoption(known); {
|
||||||
|
case adopted && !o.Adopted:
|
||||||
|
return fmt.Errorf("this machine was raised adopted, and genesis was run again without --adopted. " +
|
||||||
|
"Run it again the way it was raised: pass --adopted. Returning it to converged is the " +
|
||||||
|
"controller's act (converge), never genesis's; nothing was changed")
|
||||||
|
case !adopted && o.Adopted:
|
||||||
|
return fmt.Errorf("this machine was raised converged, and genesis was run again with --adopted, " +
|
||||||
|
"which would remove the foundation's filter it relies on. Run it again without --adopted; " +
|
||||||
|
"returning a node to adopted is the controller's act (adopt); nothing was changed")
|
||||||
|
}
|
||||||
// What genesis raised on an earlier run is the mesh's, and it is what the machine now
|
// What genesis raised on an earlier run is the mesh's, and it is what the machine now
|
||||||
// serves; the question was answered the first time.
|
// serves; the question was answered the first time.
|
||||||
say(" in use not asked: this machine carries what an earlier genesis raised")
|
say(" in use not asked: this machine carries what an earlier genesis raised")
|
||||||
@@ -109,3 +124,19 @@ func RefuseAMachineInUse(ctx context.Context, o Options, run Runner, say func(st
|
|||||||
"force and every module is taken on it one at a time. Nothing was changed",
|
"force and every module is taken on it one at a time. Nothing was changed",
|
||||||
strings.Join(named, "\n - "))
|
strings.Join(named, "\n - "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordsAdoption is whether this machine's state says it is an adopted node: it holds something
|
||||||
|
// of the mesh's that only an adopted node has — the guard or an opening, under the adoption prefix
|
||||||
|
// — or something it found and holds. A node the controller converged has neither any more; the
|
||||||
|
// record of the firewall it found outlives the flip, so it is not read as the mode.
|
||||||
|
func RecordsAdoption(known store.State) bool {
|
||||||
|
if len(known.Held) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, r := range known.Resources {
|
||||||
|
if strings.HasPrefix(r.ID, declaration.AdoptionPrefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -120,3 +120,46 @@ func TestAFreshlyInstalledMachineAsMeasuredIsNotInUse(t *testing.T) {
|
|||||||
t.Errorf("a fresh machine read as in use: containers %v, listeners %v", containers, listeners)
|
t.Errorf("a fresh machine read as in use: containers %v, listeners %v", containers, listeners)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defends novox/hq ADR 0103: a machine raised adopted stays adopted if genesis is run again. The
|
||||||
|
// installer reads the mode from what the machine records, and refuses a flag that disagrees.
|
||||||
|
func TestARerunWithoutTheFlagOnAnAdoptedMachineIsRefused(t *testing.T) {
|
||||||
|
o := Options{State: filepath.Join(t.TempDir(), "state.json")}
|
||||||
|
adoptedState := store.State{Resources: []store.Applied{
|
||||||
|
{ID: "store", Type: "container", Target: "mesh-store", Origin: store.OriginCarried},
|
||||||
|
{ID: "adoption.guard", Type: "file", Target: "/etc/mesh/guard.nft", Origin: store.OriginCarried},
|
||||||
|
}}
|
||||||
|
if err := store.Save(o.State, adoptedState); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
m := inUseRunner{ss: inUseSockets}
|
||||||
|
err := RefuseAMachineInUse(context.Background(), o, m.run, quietly)
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "pass --adopted") {
|
||||||
|
t.Fatalf("a re-run without --adopted on an adopted machine was not refused: %v", err)
|
||||||
|
}
|
||||||
|
o.Adopted = true
|
||||||
|
if err := RefuseAMachineInUse(context.Background(), o, m.run, quietly); err != nil {
|
||||||
|
t.Errorf("a re-run with --adopted on an adopted machine was refused: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestARerunWithTheFlagOnAConvergedMachineIsRefused(t *testing.T) {
|
||||||
|
o := Options{State: filepath.Join(t.TempDir(), "state.json"), Adopted: true}
|
||||||
|
converged := store.State{Resources: []store.Applied{
|
||||||
|
{ID: "store", Type: "container", Target: "mesh-store", Origin: store.OriginCarried},
|
||||||
|
{ID: "base-filter", Type: "file", Target: "/etc/nftables.conf", Origin: store.OriginCarried},
|
||||||
|
},
|
||||||
|
// Converged by the controller from adopted: the firewall it found is still recorded.
|
||||||
|
Firewall: &store.FoundFirewall{Kind: "ufw", WasActive: true, DisabledByMesh: true}}
|
||||||
|
if err := store.Save(o.State, converged); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err := RefuseAMachineInUse(context.Background(), o, inUseRunner{ss: inUseSockets}.run, quietly)
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "without --adopted") {
|
||||||
|
t.Fatalf("a re-run with --adopted on a converged machine was not refused: %v", err)
|
||||||
|
}
|
||||||
|
o.Adopted = false
|
||||||
|
if err := RefuseAMachineInUse(context.Background(), o, inUseRunner{ss: inUseSockets}.run, quietly); err != nil {
|
||||||
|
t.Errorf("a converged re-run of a converged machine was refused: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user