The controller moves from a container to a process on the one machine that runs it (novox/hq issue 213). Every orphan is removed before anything is applied, so the container would go first and nothing would answer the mesh's verbs while the process was fetched, unpacked and started — and never again, if it did not start. - a process may say what it `replaces`: resources the declaration no longer declares. Such an orphan is kept through the up-front sweep and removed right after the process applied and is up: active and running at two looks ten seconds apart, the same main process, no restart in between (stricter than ADR 0184's second look, which reads a unit waiting to restart as running). If the process failed, was skipped behind its module's step, or is not running, the orphan stays running and recorded, reported kept, and the next apply hands it over. Refused: naming something still declared, itself, an empty id, one thing named by two processes, and `replaces` on a step or a schedule. - beyond #85's oneshot unit for a step: a step written ./name runs its own bundle's binary (tested), and is started, never enabled. - a run-once process that fails gates its module, as a run-once container already did, so a version whose preparation failed is not started. - an unchanged run-once process is not run again, and an unchanged scheduled one is kept up by its timer: both were "a daemon that had stopped" and were started on every apply.
64 lines
2.6 KiB
Go
64 lines
2.6 KiB
Go
package declaration
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// novox/hq issue 213: a process says what it takes the place of, so the host can keep the old one
|
|
// answering until the new one does. What it may name is narrow, and each refusal is said here.
|
|
|
|
const aReplacingProcess = `{"id":"m.controller","type":"process","name":"m","source":"https://store.invalid/m",
|
|
"digest":"sha256:` + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + `","run":["./m","serve"]%s}`
|
|
|
|
func replacing(extra string) string {
|
|
return `{"declaration":1,"resources":[` + strings.Replace(aReplacingProcess, "%s", extra, 1) + `]}`
|
|
}
|
|
|
|
func TestAProcessMayNameWhatItReplaces(t *testing.T) {
|
|
d, err := Parse([]byte(replacing(`,"replaces":["m.server"]`)))
|
|
if err != nil {
|
|
t.Fatalf("a process replacing an undeclared container was refused: %v", err)
|
|
}
|
|
if p := d.Resources[0].(*Process); len(p.Replaces) != 1 || p.Replaces[0] != "m.server" {
|
|
t.Fatalf("replaces was not read: %+v", p)
|
|
}
|
|
}
|
|
|
|
// What is replaced is what the declaration no longer says. A resource still declared is applied,
|
|
// and handing it over as well would remove something the same declaration asks to keep.
|
|
func TestAProcessMayNotReplaceSomethingStillDeclared(t *testing.T) {
|
|
raw := `{"declaration":1,"resources":[
|
|
{"id":"m.server","type":"directory","path":"/tmp/x"},
|
|
` + strings.Replace(aReplacingProcess, "%s", `,"replaces":["m.server"]`, 1) + `]}`
|
|
if _, err := Parse([]byte(raw)); err == nil || !strings.Contains(err.Error(), "still declares") {
|
|
t.Fatalf("a process replacing a declared resource was accepted: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestOnlyAProcessThatStaysUpReplacesAnything(t *testing.T) {
|
|
for _, mode := range []string{`,"run-once":true`, `,"schedule":"0 3 * * *"`} {
|
|
if _, err := Parse([]byte(replacing(mode + `,"replaces":["m.server"]`))); err == nil {
|
|
t.Errorf("a process with %s was allowed to replace something", mode)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAProcessCannotReplaceItselfOrNothing(t *testing.T) {
|
|
for _, bad := range []string{`,"replaces":["m.controller"]`, `,"replaces":[""]`} {
|
|
if _, err := Parse([]byte(replacing(bad))); err == nil {
|
|
t.Errorf("replaces %s was accepted", bad)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOneThingHasOneReplacement(t *testing.T) {
|
|
second := strings.NewReplacer(`"id":"m.controller"`, `"id":"m.other"`, `"name":"m"`, `"name":"other"`).
|
|
Replace(strings.Replace(aReplacingProcess, "%s", `,"replaces":["m.server"]`, 1))
|
|
raw := `{"declaration":1,"resources":[` +
|
|
strings.Replace(aReplacingProcess, "%s", `,"replaces":["m.server"]`, 1) + "," + second + `]}`
|
|
if _, err := Parse([]byte(raw)); err == nil {
|
|
t.Fatal("two processes replacing one resource were accepted")
|
|
}
|
|
}
|