Give a service's held state its own field and key, so main compiles again
mesh/merge-gate pass: builds mesh-host → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered

#64/#65 and #66 each added Applied.Declared: a directory's declared owner and
mode (string) and a service's held state (*FoundUnit), both under the JSON key
"declared". The directory's is already on nodes, written by d28ca95, so it
keeps the name and key; the service's becomes DeclaredUnit, "declared_unit".
#66 never deployed, so no node holds a record under the new key.
This commit is contained in:
jochen
2026-10-09 03:03:58 +02:00
parent 690966d847
commit c2bae14994
4 changed files with 67 additions and 31 deletions
+25 -25
View File
@@ -728,25 +728,25 @@ func ApplyMindingWindows(
Origin: origin, Origin: origin,
ID: resource.Identity(), Type: string(resource.Kind()), ID: resource.Identity(), Type: string(resource.Kind()),
Target: outcome.Target, AppliedAt: time.Now().UTC(), Target: outcome.Target, AppliedAt: time.Now().UTC(),
Wrote: outcome.wrote, Wrote: outcome.wrote,
Into: outcome.into, Into: outcome.into,
Kept: kept, Kept: kept,
KeptMode: keptMode, KeptMode: keptMode,
KeptOwner: keptOwner, KeptOwner: keptOwner,
Reads: outcome.reads, Reads: outcome.reads,
Stateless: outcome.stateless, Stateless: outcome.stateless,
Scope: outcome.scope, Scope: outcome.scope,
User: outcome.user, User: outcome.user,
Found: outcome.found, Found: outcome.found,
Declared: outcome.declared, DeclaredUnit: outcome.declared,
Shell: outcome.shell, Shell: outcome.shell,
Linger: outcome.linger, Linger: outcome.linger,
Groups: outcome.groups, Groups: outcome.groups,
Unpacked: outcome.unpacked, Unpacked: outcome.unpacked,
AsFound: outcome.asFound, AsFound: outcome.asFound,
Declared: outcome.asFoundDeclared, Declared: outcome.asFoundDeclared,
FoundAt: outcome.asFoundAt, FoundAt: outcome.asFoundAt,
Holds: holds(resource), Holds: holds(resource),
}) })
if outcome.found != nil { if outcome.found != nil {
known.DropFound(resource.Identity()) known.DropFound(resource.Identity())
@@ -2847,9 +2847,9 @@ func giveUnitBack(ctx context.Context, sys system.System, a store.Applied, run R
// mesh's doing outliving the mesh — a pool whose distribution scrub timer a module stopped would have // mesh's doing outliving the mesh — a pool whose distribution scrub timer a module stopped would have
// no schedule at all once the module went. A unit stopped for any other reason is not known to have // no schedule at all once the module went. A unit stopped for any other reason is not known to have
// been stopped by the mesh, and is left as before. // been stopped by the mesh, and is left as before.
if !made && a.Declared != nil && (a.Declared.Unit == "" || a.Declared.Unit == a.Target) { if !made && a.DeclaredUnit != nil && (a.DeclaredUnit.Unit == "" || a.DeclaredUnit.Unit == a.Target) {
start := a.Found.State == "running" && a.Declared.State == "stopped" start := a.Found.State == "running" && a.DeclaredUnit.State == "stopped"
enable := a.Found.Boot == "enabled" && a.Declared.Boot == "disabled" enable := a.Found.Boot == "enabled" && a.DeclaredUnit.Boot == "disabled"
if start || enable { if start || enable {
return startUnitBack(ctx, sys, a, run, start, enable) return startUnitBack(ctx, sys, a, run, start, enable)
} }
@@ -2953,9 +2953,9 @@ func startUnitBack(ctx context.Context, sys system.System, a store.Applied, run
errRemovalWaits, a.Target, err) errRemovalWaits, a.Target, err)
} }
} }
if state != a.Declared.State || (a.Declared.Boot != "" && boot != a.Declared.Boot) { if state != a.DeclaredUnit.State || (a.DeclaredUnit.Boot != "" && boot != a.DeclaredUnit.Boot) {
return "forgotten", fmt.Sprintf("it is %s and %s at boot, not the %s and %s the mesh held it to: somebody "+ return "forgotten", fmt.Sprintf("it is %s and %s at boot, not the %s and %s the mesh held it to: somebody "+
"changed it since, so it is left as it is", state, boot, a.Declared.State, orUnset(a.Declared.Boot)), nil "changed it since, so it is left as it is", state, boot, a.DeclaredUnit.State, orUnset(a.DeclaredUnit.Boot)), nil
} }
var did []string var did []string
if enable { if enable {
@@ -3027,7 +3027,7 @@ func foundAs(ctx context.Context, sys system.System, r *declaration.Service, run
// Given back as if undeclared, never as the mesh's own: whether the mesh wrote the old // Given back as if undeclared, never as the mesh's own: whether the mesh wrote the old
// unit's file is known to the removal of orphans, and that file's own record goes with it. // unit's file is known to the removal of orphans, and that file's own record goes with it.
action, detail, err := removeService(ctx, sys, action, detail, err := removeService(ctx, sys,
store.Applied{Type: string(declaration.TypeService), Target: unit, Found: f, Declared: previous.Declared, store.Applied{Type: string(declaration.TypeService), Target: unit, Found: f, DeclaredUnit: previous.DeclaredUnit,
Scope: previous.Scope, User: previous.User}, run, false) Scope: previous.Scope, User: previous.User}, run, false)
switch { switch {
case errors.Is(err, errRemovalWaits): case errors.Is(err, errRemovalWaits):
+4 -4
View File
@@ -125,14 +125,14 @@ func Plan(d *declaration.Declaration, known store.State, origin string) []Step {
case f == nil: case f == nil:
step.Verb, step.Why = "forget", "no longer declared; recorded before the host kept what it "+ step.Verb, step.Why = "forget", "no longer declared; recorded before the host kept what it "+
"found, so it is left as it is" "found, so it is left as it is"
case orphan.Declared != nil && ((f.State == "running" && orphan.Declared.State == "stopped") || case orphan.DeclaredUnit != nil && ((f.State == "running" && orphan.DeclaredUnit.State == "stopped") ||
(f.Boot == "enabled" && orphan.Declared.Boot == "disabled")): (f.Boot == "enabled" && orphan.DeclaredUnit.Boot == "disabled")):
// startUnitBack's words (novox/hq ADR 0269): what the mesh's declaration stopped goes back. // startUnitBack's words (novox/hq ADR 0269): what the mesh's declaration stopped goes back.
var back []string var back []string
if f.State == "running" && orphan.Declared.State == "stopped" { if f.State == "running" && orphan.DeclaredUnit.State == "stopped" {
back = append(back, "started") back = append(back, "started")
} }
if f.Boot == "enabled" && orphan.Declared.Boot == "disabled" { if f.Boot == "enabled" && orphan.DeclaredUnit.Boot == "disabled" {
back = append(back, "enabled at boot") back = append(back, "enabled at boot")
} }
// A preview: whether it is still as the mesh held it is read when the removal runs, and a // A preview: whether it is still as the mesh held it is read when the removal runs, and a
+35
View File
@@ -0,0 +1,35 @@
package store
import (
"encoding/json"
"testing"
)
// A directory used as found and a service's held state are two records that both say "declared"
// (novox/hq issue 339, ADR 0269). The directory's was on nodes first, under the key "declared", and a
// record a node already holds must keep reading back; the service's has a key of its own.
func TestADirectorysDeclaredAndAUnitsDeclaredKeepTheirOwnKeys(t *testing.T) {
onDisk := []byte(`{"id":"notes.data","type":"directory","as_found":true,"declared":"root, mode 755",` +
`"declared_unit":{"unit":"a.timer","state":"stopped","boot":"disabled"}}`)
var a Applied
if err := json.Unmarshal(onDisk, &a); err != nil {
t.Fatal(err)
}
if a.Declared != "root, mode 755" {
t.Errorf("the directory's declared owner and mode read back as %q", a.Declared)
}
if a.DeclaredUnit == nil || a.DeclaredUnit.State != "stopped" || a.DeclaredUnit.Boot != "disabled" {
t.Errorf("the unit's held state read back as %+v", a.DeclaredUnit)
}
body, err := json.Marshal(a)
if err != nil {
t.Fatal(err)
}
var keys map[string]json.RawMessage
if err := json.Unmarshal(body, &keys); err != nil {
t.Fatal(err)
}
if string(keys["declared"]) != `"root, mode 755"` || keys["declared_unit"] == nil {
t.Errorf("written as %s", body)
}
}
+3 -2
View File
@@ -99,12 +99,13 @@ type Applied struct {
// when its declaration goes; one the mesh started is stopped again. Absent on a record written // when its declaration goes; one the mesh started is stopped again. Absent on a record written
// before the host kept it, and then the unit is left exactly as it is. // before the host kept it, and then the unit is left exactly as it is.
Found *FoundUnit `json:"found,omitempty"` Found *FoundUnit `json:"found,omitempty"`
// Declared is, for a service with a state, what the mesh last held its unit to: running or stopped, // DeclaredUnit is, for a service with a state, what the mesh last held its unit to: running or stopped,
// and enabled or disabled where the declaration said. With Found, it tells a unit the mesh's own // and enabled or disabled where the declaration said. With Found, it tells a unit the mesh's own
// declaration stopped and disabled from one somebody stopped since, so removal gives back running // declaration stopped and disabled from one somebody stopped since, so removal gives back running
// and enabled only the first (novox/hq ADR 0269). Absent on a record from before the host kept it, // and enabled only the first (novox/hq ADR 0269). Absent on a record from before the host kept it,
// and then nothing is started on the way out. // and then nothing is started on the way out.
Declared *FoundUnit `json:"declared,omitempty"` // Its key is not "declared": that one is a directory's (AsFound, below) and was on disk first.
DeclaredUnit *FoundUnit `json:"declared_unit,omitempty"`
// Into is set for a file written into rather than over (novox/hq ADR 0102): the format, what // Into is set for a file written into rather than over (novox/hq ADR 0102): the format, what
// each of the mesh's keys held before it set them, which of them were absent, and whether the // each of the mesh's keys held before it set them, which of them were absent, and whether the