Hold a found directory, a found service's unit, a container that would mount found data, and a step run in a held container on an adopted node (hq ADR 0103)

This commit is contained in:
2026-09-22 18:14:37 +02:00
parent 35ecf68393
commit 824cb60cbb
6 changed files with 562 additions and 119 deletions
+10 -5
View File
@@ -57,11 +57,16 @@ func TestAnAdoptionNamingAnUnknownIDIsRefused(t *testing.T) {
}
}
func TestAnAdoptionMayOnlyHoldFilesAndContainers(t *testing.T) {
refusal := refusalFor(t, `{"adoption":{"taken":[],"untaken":{"hello-web":["hello-web.data"]}},
"declaration":1,`+adoptedResources+`}`)
if !strings.Contains(strings.Join(refusal.Problems, "\n"), "only a file or a container") {
t.Errorf("a directory was accepted as holdable: %v", refusal.Problems)
func TestAnAdoptionMayNameAResourceOfAnyKind(t *testing.T) {
// A directory, a service or an action can reach what was found as surely as a file can, so
// the controller lists every resource of an untaken module (novox/hq ADR 0103).
d, err := Parse([]byte(`{"adoption":{"taken":[],"untaken":{"hello-web":["hello-web.data"]}},
"declaration":1,` + adoptedResources + `}`))
if err != nil {
t.Fatalf("a directory of an untaken module was refused: %v", err)
}
if module, ok := d.Adoption.UntakenModuleOf("hello-web.data"); !ok || module != "hello-web" {
t.Errorf("the directory is not its module's: %q %v", module, ok)
}
}
+7 -11
View File
@@ -956,10 +956,12 @@ type Declaration struct {
// is adopted; it is told, in every declaration, so a host restarted from the declaration it kept
// is in the same mode it was in before.
//
// Untaken names, per module assigned here and not yet taken, the ids of its file and container
// resources — the only shapes a predecessor can already have on the machine. The host cannot
// split a resource id into its module, because module names may contain dots, so the controller
// says which ids belong to which module rather than leaving the host to guess.
// Untaken names, per module assigned here and not yet taken, the ids of its resources. Any kind
// may be listed: a file, a directory, a service's unit or a container can already be on the
// machine, and an action run inside a held container reaches what was found (novox/hq ADR 0103);
// the host decides per kind what can be held. The host cannot split a resource id into its
// module, because module names may contain dots, so the controller says which ids belong to which
// module rather than leaving the host to guess.
type Adoption struct {
Taken []string `json:"taken"`
Untaken map[string][]string `json:"untaken,omitempty"`
@@ -1026,15 +1028,9 @@ func checkAdoption(a *Adoption, resources []Resource, allowActions bool) []strin
continue
}
owner[id] = module
kind, declared := kinds[id]
switch {
case !declared:
if _, declared := kinds[id]; !declared {
problems = append(problems, fmt.Sprintf(
"adoption: %q of the untaken module %q is not in this declaration", id, module))
case kind != TypeFile && kind != TypeContainer:
problems = append(problems, fmt.Sprintf(
"adoption: %q of the untaken module %q is a %s, and only a file or a "+
"container can be found on a machine", id, module, kind))
}
}
}