Hand a replaced resource over to the process that replaces it (hq issue 213)
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. - a run-once process is run by a oneshot unit, so a step has its process's user, working directory and environment and `./name` is its own bundle's binary; it was run directly by the host, as root, with no environment. It is not 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.
This commit is contained in:
@@ -569,6 +569,18 @@ type Process struct {
|
||||
// that runs once does not run on a schedule, and something that is not running cannot be
|
||||
// restarted when a file changes.
|
||||
Schedule string `json:"schedule,omitempty"`
|
||||
|
||||
// Replaces names resources this declaration no longer declares that this process takes the
|
||||
// place of (novox/hq issue 213). Such a resource is not removed with the other orphans, before
|
||||
// anything is applied: it is removed only once this process is applied and still running a
|
||||
// moment later, and kept when it is not. So the thing being replaced answers until the thing
|
||||
// replacing it does — the controller moving from its container to a process is the case: the
|
||||
// container removed first left nothing answering the mesh's verbs for as long as fetching,
|
||||
// unpacking and starting the process took, and for ever if the process did not start.
|
||||
//
|
||||
// For a process that stays up; a step or a scheduled run is not running a moment later by
|
||||
// design, so there is nothing to hand over to.
|
||||
Replaces []string `json:"replaces,omitempty"`
|
||||
}
|
||||
|
||||
func (d *Process) Identity() string { return d.ID }
|
||||
@@ -654,6 +666,20 @@ func (d *Process) validate(where string, _ bool) []string {
|
||||
problems = append(problems, where+": "+err.Error())
|
||||
}
|
||||
}
|
||||
if len(d.Replaces) > 0 && (d.RunOnce || d.Schedule != "") {
|
||||
problems = append(problems, where+
|
||||
": only a process that stays up replaces something — a step or a scheduled run is not "+
|
||||
"running a moment later, so what it replaced would be removed with nothing in its place, "+
|
||||
"or never")
|
||||
}
|
||||
for _, id := range d.Replaces {
|
||||
switch {
|
||||
case strings.TrimSpace(id) == "":
|
||||
problems = append(problems, where+": replaces names an empty id")
|
||||
case id == d.ID:
|
||||
problems = append(problems, where+": a process cannot replace itself")
|
||||
}
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
@@ -1457,6 +1483,7 @@ func parse(raw []byte, allowActions bool) (*Declaration, error) {
|
||||
d.Resources = append(d.Resources, resource)
|
||||
}
|
||||
problems = append(problems, checkAdoption(env.Adoption, d.Resources, allowActions)...)
|
||||
problems = append(problems, checkReplaces(d.Resources)...)
|
||||
if env.Adoption == nil {
|
||||
for _, r := range d.Resources {
|
||||
if r.Kind() == TypeOpening {
|
||||
@@ -1640,3 +1667,36 @@ func stripComments(raw []byte) []byte {
|
||||
}
|
||||
return []byte(strings.Join(kept, "\n"))
|
||||
}
|
||||
|
||||
// checkReplaces refuses a `replaces` naming something this same declaration still declares, or
|
||||
// named by two processes (novox/hq issue 213). What is replaced is what the declaration no longer
|
||||
// says — a resource still declared is applied, not handed over, and one handed to two replacements
|
||||
// would go when the first of them came up, whatever became of the second.
|
||||
func checkReplaces(resources []Resource) []string {
|
||||
declared := map[string]bool{}
|
||||
for _, r := range resources {
|
||||
declared[r.Identity()] = true
|
||||
}
|
||||
var problems []string
|
||||
by := map[string]string{}
|
||||
for _, r := range resources {
|
||||
p, ok := r.(*Process)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, id := range p.Replaces {
|
||||
if declared[id] {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"resource %q: replaces %q, which this declaration still declares — a process "+
|
||||
"replaces what the declaration no longer says", p.ID, id))
|
||||
}
|
||||
if other, taken := by[id]; taken && other != p.ID {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"resource %q: replaces %q, which %q replaces too — one thing has one replacement",
|
||||
p.ID, id, other))
|
||||
}
|
||||
by[id] = p.ID
|
||||
}
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user