feat(swarmctl): add agent create, queueing the swarm-controller creation DAG
`swarmctl agent create <name> --hive <hive>` POSTs `/api/agents` to swarm-controller over the daemon's unix socket and prints the queued job's node id. It deliberately does not wait. The endpoint queues a DAG whose last node *publishes* a deploy message; the hive's `hive-c0re` then converges on its own clock, out of the controller's sight. So even a fully settled graph would not mean the agent is up, and there is nothing this CLI could wait for that would let it claim otherwise. Printing the id is exactly what the response says and all of what it says. Transport is a bare hyper HTTP/1.1 client handshaked onto a tokio `UnixStream` via `hyper_util::rt::TokioIo` — the same crate family `hivectl/src/watch.rs` and `hive-agent/src/web_ui/proxy.rs` already use, all of it already workspace-pinned. The request/response shapes are a local mirror rather than a shared crate: the controller's own types are private to its binary and this crate does not link it, the same separation `hivectl` keeps from `hive-c0re`. Errors are reduced to one actionable line — the controller answers RFC 9457 problem+json, so an unknown `--hive` reaches the operator as the roster of hives that would have worked rather than a body dump. Response `warnings` are printed when non-empty. The nix module wraps the binary with `SWARM_CONTROLLER_SOCKET`, read from the same `socketPath` the daemon binds. Refs #4399
This commit is contained in:
parent
a3b672d1d5
commit
30fa54cbc6
9 changed files with 521 additions and 17 deletions
|
|
@ -18,10 +18,16 @@ _that_ work needs either a hand-pinned gid (the container's uids are
|
|||
allocated inside it, at activation — see the uid-assignment issue) or
|
||||
world-readable password hashes. Both are worse than root.
|
||||
|
||||
So there is no socket, no HTTP route and no privileged helper here. When
|
||||
a verb has to run as a non-root user or from another host, the answer is
|
||||
a **group-gated admin socket**, separate from the controller's `0666`
|
||||
gateway-facing one — not a widening of what root does here.
|
||||
So this binary **serves** no socket, publishes no HTTP route and has no
|
||||
privileged helper. When a verb has to run as a non-root user or from
|
||||
another host, the answer is a **group-gated admin socket**, separate from
|
||||
the controller's `0666` gateway-facing one — not a widening of what root
|
||||
does here.
|
||||
|
||||
That rules out a way in, not a way out: `agent create` is a _client_ of
|
||||
the controller's socket, because the work it asks for is a job graph only
|
||||
the controller can queue (see below). Nothing here becomes reachable by
|
||||
that.
|
||||
|
||||
## One file, two writers
|
||||
|
||||
|
|
@ -56,8 +62,9 @@ an error.
|
|||
| `SWARMCTL_AUTHELIA_USERS_FILE` | host-side path of the users database |
|
||||
| `SWARMCTL_AUTHELIA_MACHINE` | container name, for `systemctl -M` |
|
||||
| `SWARMCTL_AUTHELIA_UNIT` | authelia's unit inside that container |
|
||||
| `SWARM_CONTROLLER_SOCKET` | the controller's unix socket, for `agent create` |
|
||||
|
||||
## Usage
|
||||
## `user add`
|
||||
|
||||
```console
|
||||
# swarmctl user add mara --display-name "Mara" --group admins
|
||||
|
|
@ -67,3 +74,38 @@ The password is **generated by authelia** (`crypto hash generate argon2
|
|||
--random`) and printed once. It is never passed on a command line:
|
||||
`/proc/<pid>/cmdline` is world-readable, so a password in argv is readable
|
||||
by any local process for the lifetime of the call.
|
||||
|
||||
## `agent create`
|
||||
|
||||
```console
|
||||
# swarmctl agent create scribe --hive alpha
|
||||
queued: job node 42
|
||||
agent "scribe" will be deployed to hive "alpha" once the job graph runs; `swarmctl` does not wait for it
|
||||
```
|
||||
|
||||
`POST /api/agents` on the swarm-controller, over its unix socket
|
||||
(`--controller-socket`, else `SWARM_CONTROLLER_SOCKET`, which the nix
|
||||
module sets from the daemon's own `socketPath`). It prints the queued
|
||||
job's node id **and stops there**.
|
||||
|
||||
It deliberately does not wait. The endpoint queues a DAG — SSO identity,
|
||||
forge user, forge repo, repo membership, config-repo seed, then a deploy
|
||||
message — and the last of those _publishes_: the hive's `hive-c0re` picks
|
||||
it up and converges on its own clock, out of the controller's sight. So
|
||||
even a fully settled graph would not mean the agent is up, and there is
|
||||
nothing this CLI could wait for that would let it say so honestly. Watch
|
||||
the swarm UI's job view for the rest.
|
||||
|
||||
No approval gate, for the same reason nothing else here has one: running
|
||||
this binary already means being root on the controller's host.
|
||||
|
||||
`--hive` is required. It is an _address_ — where the deploy message goes
|
||||
— not an attribute of the agent, so there is no sensible default. The
|
||||
controller checks it against the swarm's hive roster and names the hives
|
||||
that would have worked when it misses.
|
||||
|
||||
The request and response shapes are mirrored in `src/agent.rs` rather
|
||||
than shared: the controller's own types are private to its binary, this
|
||||
crate does not link it, and there is no wire-type crate between them.
|
||||
Two fields out, two in, both ends validating — a drift shows up as a
|
||||
400 naming the field.
|
||||
|
|
|
|||
Loading…
Reference in a new issue