ADR 0118: undeclaring removes what the mesh made, gives back what it changed, leaves the machine's units as they are — resolves issue 130
This commit is contained in:
@@ -0,0 +1,100 @@
|
|||||||
|
---
|
||||||
|
topic: what runs on it
|
||||||
|
status: accepted
|
||||||
|
date: 2026-09-27
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 0102-the-mesh-writes-into-a-shared-file-never-over-it.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 118. Undeclaring removes what the mesh made, gives back what it changed, and leaves the machine's units as they are
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
When a resource stops being declared — its module unassigned, the node sent a
|
||||||
|
deliberately-empty declaration ([issue 127](../04-ISSUES/127-a-declaration-that-shrinks-to-empty-is-skipped-not-sent/00-report.md)),
|
||||||
|
or a new catalogue version renaming its id — the host undoes it. The host's own code states
|
||||||
|
the rule it means to follow: **it removes what it made and leaves what it merely configured.**
|
||||||
|
For almost every resource it does exactly that:
|
||||||
|
|
||||||
|
- a container, a network, a process's unit, a directory it created: removed;
|
||||||
|
- a file it created: removed; a file it replaced: its kept original put back
|
||||||
|
([ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md));
|
||||||
|
- keys and list members it wrote into a shared file: given back as they were
|
||||||
|
([ADR 0102](0102-the-mesh-writes-into-a-shared-file-never-over-it.md));
|
||||||
|
- a package: left installed — the host cannot know it is unused;
|
||||||
|
- an operator's path it was given access to: never touched
|
||||||
|
([ADR 0051](0051-shared-data-is-the-operators.md)).
|
||||||
|
|
||||||
|
**A service is the exception.** A `service` resource never installs a unit: it puts one that
|
||||||
|
already exists — the distribution's, the operator's — into a state. Undeclared, the host stops
|
||||||
|
it. That contradicts the rule above, and in practice it is the most dangerous thing an
|
||||||
|
undeclare can do. Found reviewing the uplink modules
|
||||||
|
([issue 130](../04-ISSUES/130-undeclaring-a-service-stops-it/00-report.md)):
|
||||||
|
|
||||||
|
- the private network declares the container runtime's unit only so a change to the registry
|
||||||
|
trust reloads it — unassigning the private network stops the runtime, and every container on
|
||||||
|
the machine, the mesh's and not;
|
||||||
|
- the sshd module declares the ssh daemon — unassigning it stops ssh, the lockout that module's
|
||||||
|
own `listens` rule forbids;
|
||||||
|
- the uplink modules would have stopped the network manager, taking the machine off the only
|
||||||
|
link the mesh reaches it by.
|
||||||
|
|
||||||
|
ADR 0117 (in review) answered that for its own modules with a
|
||||||
|
service declared with no `state`. Every other module that declares a unit it did not make is
|
||||||
|
exposed in the same way, and relying on each author to remember an opt-out is how the next one
|
||||||
|
is missed.
|
||||||
|
|
||||||
|
## Considered Options
|
||||||
|
|
||||||
|
**1. Undeclaring touches nothing on the machine.** Rejected. What the mesh made would outlive
|
||||||
|
the module that made it: a container nobody manages keeps serving and stops being patched; a
|
||||||
|
unit the mesh wrote keeps running a bundle nothing updates; a name collides when the module
|
||||||
|
is assigned again. An undeclare that leaves the mesh's own work behind is an orphan factory.
|
||||||
|
|
||||||
|
**2. Keep stopping services; make "leave it running" an opt-in per resource.** Rejected. It
|
||||||
|
keeps the dangerous behaviour as the default for exactly the units that matter most — the
|
||||||
|
runtime, the ssh daemon, the network — and each new module is one forgotten field away from a
|
||||||
|
machine that goes dark when it is unassigned.
|
||||||
|
|
||||||
|
**3. The line is ownership.** Chosen.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**Undeclaring removes what the mesh made, gives back what it changed, and leaves what was the
|
||||||
|
machine's as it is.** For a unit, that means: **the host never stops, starts, disables or
|
||||||
|
enables a unit it did not create when that unit stops being declared.** An undeclared `service`
|
||||||
|
is forgotten — reported as such — and the unit keeps whatever state it is in.
|
||||||
|
|
||||||
|
- A unit the mesh *did* create — a `process` resource's unit, which the host writes — is still
|
||||||
|
stopped and removed with it. That is the mesh's own code.
|
||||||
|
- The service's settings the mesh wrote are given back by their own resources (a kept original
|
||||||
|
restored, a region or keys removed). A running service keeps running on what it read until it
|
||||||
|
next reads its configuration; the mesh does not restart it to make it notice.
|
||||||
|
- A service declared with no `state` (ADR 0117) remains the way to say the mesh must not
|
||||||
|
**start** a unit either; this record is about what happens when a declaration goes away, and
|
||||||
|
covers every service.
|
||||||
|
- An operator who wants a unit stopped when its module goes says so first: declare it
|
||||||
|
`stopped`, push, then unassign. Stopping a machine's unit is a decision, made visibly — never
|
||||||
|
a side effect of removing a module.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- Unassigning the private network no longer stops the container runtime; unassigning sshd no
|
||||||
|
longer stops ssh; no uplink module can take a machine's network down on its way out.
|
||||||
|
- The host's removal report says "forgotten; the unit is the machine's" where it used to say
|
||||||
|
"stopped". A module that relied on its daemon stopping when unassigned — none in the catalogue
|
||||||
|
today does on purpose — needs the explicit step above.
|
||||||
|
- A daemon can keep running after its module is gone, on configuration that was taken back from
|
||||||
|
under it. That is a visible, running process the operator can see and stop; the alternative
|
||||||
|
was an invisible outage.
|
||||||
|
- **Not decided here:** an unassign preview that lists what an undeclare will remove and what it
|
||||||
|
will leave running. Issue 130 asks for it; it is the controller's to build.
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- [issue 130](../04-ISSUES/130-undeclaring-a-service-stops-it/00-report.md): the finding
|
||||||
|
- ADR 0117 (in review): the uplink modules, and a service with no state
|
||||||
|
- [ADR 0102](0102-the-mesh-writes-into-a-shared-file-never-over-it.md), [ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md):
|
||||||
|
what is given back, and how
|
||||||
|
- mesh-host `internal/apply/apply.go` (`remove`, the service case)
|
||||||
@@ -199,6 +199,7 @@ python3 00-META/checks/index.py fail if stale
|
|||||||
- **0113** — [The vault makes every shared secret, a provider makes resources and data, and the mesh carries both](0113-the-vault-makes-every-secret.md) *(proposed)*
|
- **0113** — [The vault makes every shared secret, a provider makes resources and data, and the mesh carries both](0113-the-vault-makes-every-secret.md) *(proposed)*
|
||||||
- **0114** — [A credential two parties hold rotates over two credentials; one a single party holds rotates in place, staged; and retiring a credential never removes what it reached](0114-a-shared-credential-rotates-over-two-credentials.md) *(proposed)*
|
- **0114** — [A credential two parties hold rotates over two credentials; one a single party holds rotates in place, staged; and retiring a credential never removes what it reached](0114-a-shared-credential-rotates-over-two-credentials.md) *(proposed)*
|
||||||
- **0115** — [One assignment of a module per node: the module's name is the assignment's identity](0115-one-assignment-of-a-module-per-node.md) *(proposed)*
|
- **0115** — [One assignment of a module per node: the module's name is the assignment's identity](0115-one-assignment-of-a-module-per-node.md) *(proposed)*
|
||||||
|
- **0118** — [Undeclaring removes what the mesh made, gives back what it changed, and leaves the machine's units as they are](0118-undeclaring-leaves-the-machines-units-running.md)
|
||||||
|
|
||||||
### How it is built
|
### How it is built
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
---
|
---
|
||||||
status: located
|
status: located
|
||||||
opened: 2026-09-27
|
opened: 2026-09-27
|
||||||
located-in: [mesh-host internal/apply/apply.go (remove), mesh-controller internal/overlay, mesh-catalog modules/sshd]
|
located-in: [mesh-host internal/apply/apply.go (remove)]
|
||||||
|
amended-design: 02-DECISIONS/0118-undeclaring-leaves-the-machines-units-running.md
|
||||||
---
|
---
|
||||||
|
|
||||||
# 130 — undeclaring a service stops it, even one the mesh only reloads or only keeps running
|
# 130 — undeclaring a service stops it, even one the mesh only reloads or only keeps running
|
||||||
@@ -41,3 +42,13 @@ reaches it by.
|
|||||||
module's service too — a machine's ssh daemon outlives any module that configures it.
|
module's service too — a machine's ssh daemon outlives any module that configures it.
|
||||||
- A plan or unassign preview that names every unit an undeclare will stop, so the consequence
|
- A plan or unassign preview that names every unit an undeclare will stop, so the consequence
|
||||||
is read before it happens.
|
is read before it happens.
|
||||||
|
|
||||||
|
## Resolution
|
||||||
|
|
||||||
|
[ADR 0118](../../02-DECISIONS/0118-undeclaring-leaves-the-machines-units-running.md): undeclaring
|
||||||
|
removes what the mesh made, gives back what it changed, and leaves the machine's units as they
|
||||||
|
are. An undeclared `service` is forgotten, never stopped — the host did not create the unit.
|
||||||
|
That covers the runtime, sshd and the uplink modules at once, without each module opting out;
|
||||||
|
the private network and the sshd module need no change. A `process`'s unit, which the host does
|
||||||
|
write, is still stopped and removed. The unassign preview asked for above is left open for the
|
||||||
|
controller.
|
||||||
|
|||||||
Reference in New Issue
Block a user