refactor(hivectl): move quota + subvol under agents
Both groups only ever act on a single managed agent's state dir, so they belong in the `agents` namespace rather than as top-level verbs next to `forge` / `matrix` / `wg`. Renames `quota limit` -> `quota set`: the enclosing group already carries the noun, so the bare verb matches the flat `set-parent` / `set-limits` spelling without stuttering, and it removes the `set-limits` (cpu/mem) vs `quota limit` (disk) ambiguity. Adds a cross-pointer from `set-limits` to `agents quota`. Handlers stay in their own modules; `run_agents` gains the reparenting glue. Regenerates docs/tools/hivectl-cli.md. Refs #2724
This commit is contained in:
parent
2af8c2d17d
commit
766b1f71fb
9 changed files with 215 additions and 204 deletions
|
|
@ -33,6 +33,16 @@ This document contains the help content for the `hivectl` command-line program.
|
||||||
* [`hivectl agents rebuild`↴](#hivectl-agents-rebuild)
|
* [`hivectl agents rebuild`↴](#hivectl-agents-rebuild)
|
||||||
* [`hivectl agents set-parent`↴](#hivectl-agents-set-parent)
|
* [`hivectl agents set-parent`↴](#hivectl-agents-set-parent)
|
||||||
* [`hivectl agents set-limits`↴](#hivectl-agents-set-limits)
|
* [`hivectl agents set-limits`↴](#hivectl-agents-set-limits)
|
||||||
|
* [`hivectl agents quota`↴](#hivectl-agents-quota)
|
||||||
|
* [`hivectl agents quota enable`↴](#hivectl-agents-quota-enable)
|
||||||
|
* [`hivectl agents quota show`↴](#hivectl-agents-quota-show)
|
||||||
|
* [`hivectl agents quota set`↴](#hivectl-agents-quota-set)
|
||||||
|
* [`hivectl agents subvol`↴](#hivectl-agents-subvol)
|
||||||
|
* [`hivectl agents subvol upgrade`↴](#hivectl-agents-subvol-upgrade)
|
||||||
|
* [`hivectl agents subvol snapshot`↴](#hivectl-agents-subvol-snapshot)
|
||||||
|
* [`hivectl agents subvol snapshot create`↴](#hivectl-agents-subvol-snapshot-create)
|
||||||
|
* [`hivectl agents subvol snapshot delete`↴](#hivectl-agents-subvol-snapshot-delete)
|
||||||
|
* [`hivectl agents subvol snapshot send`↴](#hivectl-agents-subvol-snapshot-send)
|
||||||
* [`hivectl approvals`↴](#hivectl-approvals)
|
* [`hivectl approvals`↴](#hivectl-approvals)
|
||||||
* [`hivectl approvals pending`↴](#hivectl-approvals-pending)
|
* [`hivectl approvals pending`↴](#hivectl-approvals-pending)
|
||||||
* [`hivectl approvals approve`↴](#hivectl-approvals-approve)
|
* [`hivectl approvals approve`↴](#hivectl-approvals-approve)
|
||||||
|
|
@ -46,16 +56,6 @@ This document contains the help content for the `hivectl` command-line program.
|
||||||
* [`hivectl stop`↴](#hivectl-stop)
|
* [`hivectl stop`↴](#hivectl-stop)
|
||||||
* [`hivectl start`↴](#hivectl-start)
|
* [`hivectl start`↴](#hivectl-start)
|
||||||
* [`hivectl restart`↴](#hivectl-restart)
|
* [`hivectl restart`↴](#hivectl-restart)
|
||||||
* [`hivectl quota`↴](#hivectl-quota)
|
|
||||||
* [`hivectl quota enable`↴](#hivectl-quota-enable)
|
|
||||||
* [`hivectl quota show`↴](#hivectl-quota-show)
|
|
||||||
* [`hivectl quota limit`↴](#hivectl-quota-limit)
|
|
||||||
* [`hivectl subvol`↴](#hivectl-subvol)
|
|
||||||
* [`hivectl subvol upgrade`↴](#hivectl-subvol-upgrade)
|
|
||||||
* [`hivectl subvol snapshot`↴](#hivectl-subvol-snapshot)
|
|
||||||
* [`hivectl subvol snapshot create`↴](#hivectl-subvol-snapshot-create)
|
|
||||||
* [`hivectl subvol snapshot delete`↴](#hivectl-subvol-snapshot-delete)
|
|
||||||
* [`hivectl subvol snapshot send`↴](#hivectl-subvol-snapshot-send)
|
|
||||||
* [`hivectl open`↴](#hivectl-open)
|
* [`hivectl open`↴](#hivectl-open)
|
||||||
* [`hivectl completions`↴](#hivectl-completions)
|
* [`hivectl completions`↴](#hivectl-completions)
|
||||||
|
|
||||||
|
|
@ -79,8 +79,6 @@ Sibling to the `hive-c0re` daemon binary. Covers host-side admin operations that
|
||||||
* `stop` — Stop containers hive-wide in one operator action
|
* `stop` — Stop containers hive-wide in one operator action
|
||||||
* `start` — Start containers hive-wide — the inverse of `hivectl stop`
|
* `start` — Start containers hive-wide — the inverse of `hivectl stop`
|
||||||
* `restart` — Restart containers hive-wide — `stop` then `start` over one scope
|
* `restart` — Restart containers hive-wide — `stop` then `start` over one scope
|
||||||
* `quota` — Per-agent disk accounting + optional quotas via btrfs qgroups
|
|
||||||
* `subvol` — btrfs subvolume management for agent state dirs
|
|
||||||
* `open` — Print (and best-effort open in a browser) a hive web surface URL
|
* `open` — Print (and best-effort open in a browser) a hive web surface URL
|
||||||
* `completions` — Generate a shell completion script for `hivectl` and print it to stdout
|
* `completions` — Generate a shell completion script for `hivectl` and print it to stdout
|
||||||
|
|
||||||
|
|
@ -351,6 +349,8 @@ Lifecycle actions on managed agent containers. Needs the hive-c0re daemon runnin
|
||||||
* `rebuild` — Apply pending config to a managed container
|
* `rebuild` — Apply pending config to a managed container
|
||||||
* `set-parent` — Move an agent in the topology tree — under a new parent, or to root
|
* `set-parent` — Move an agent in the topology tree — under a new parent, or to root
|
||||||
* `set-limits` — Declare an agent's CPU/memory limits, overriding the hive-wide defaults
|
* `set-limits` — Declare an agent's CPU/memory limits, overriding the hive-wide defaults
|
||||||
|
* `quota` — Per-agent disk accounting + optional quotas via btrfs qgroups
|
||||||
|
* `subvol` — btrfs subvolume management for agent state dirs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -507,7 +507,7 @@ Move an agent in the topology tree — under a new parent, or to root
|
||||||
|
|
||||||
Declare an agent's CPU/memory limits, overriding the hive-wide defaults.
|
Declare an agent's CPU/memory limits, overriding the hive-wide defaults.
|
||||||
|
|
||||||
Replaces the agent's whole override entry rather than merging into it: any limit you don't pass returns to the hive-wide default. To change one and keep the other, pass both.
|
Replaces the agent's whole override entry rather than merging into it: any limit you don't pass returns to the hive-wide default. To change one and keep the other, pass both. Disk is a separate resource with its own group — see `agents quota`.
|
||||||
|
|
||||||
**Usage:** `hivectl agents set-limits [OPTIONS] <NAME>`
|
**Usage:** `hivectl agents set-limits [OPTIONS] <NAME>`
|
||||||
|
|
||||||
|
|
@ -523,6 +523,153 @@ Replaces the agent's whole override entry rather than merging into it: any limit
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents quota`
|
||||||
|
|
||||||
|
Per-agent disk accounting + optional quotas via btrfs qgroups.
|
||||||
|
|
||||||
|
Opt-in: enable qgroup accounting, then report per-agent usage or cap an agent. No-op on non-btrfs hosts.
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents quota <COMMAND>`
|
||||||
|
|
||||||
|
###### **Subcommands:**
|
||||||
|
|
||||||
|
* `enable` — Enable btrfs qgroup accounting on the agent-state filesystem
|
||||||
|
* `show` — Report per-agent disk usage from btrfs qgroups (all agents, or one by name)
|
||||||
|
* `set` — Set or clear an agent's disk-usage quota
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents quota enable`
|
||||||
|
|
||||||
|
Enable btrfs qgroup accounting on the agent-state filesystem.
|
||||||
|
|
||||||
|
Run once before `show` / `limit`. No-op on non-btrfs hosts.
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents quota enable`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents quota show`
|
||||||
|
|
||||||
|
Report per-agent disk usage from btrfs qgroups (all agents, or one by name)
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents quota show [NAME]`
|
||||||
|
|
||||||
|
###### **Arguments:**
|
||||||
|
|
||||||
|
* `<NAME>` — Agent to show (omit for all agents with a state subvolume)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents quota set`
|
||||||
|
|
||||||
|
Set or clear an agent's disk-usage quota.
|
||||||
|
|
||||||
|
Named `set` rather than `set-quota` because the enclosing `quota` group already carries the noun — `agents quota set iris 5G`. The `set-<noun>` spelling stays for the flat verbs (`set-parent`, `set-limits`), which have no group to inherit it from.
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents quota set <NAME> <SIZE>`
|
||||||
|
|
||||||
|
###### **Arguments:**
|
||||||
|
|
||||||
|
* `<NAME>` — Agent whose state subvolume to limit
|
||||||
|
* `<SIZE>` — Size cap (`5G`, `500M`, `1073741824`) or `none` to clear
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents subvol`
|
||||||
|
|
||||||
|
btrfs subvolume management for agent state dirs.
|
||||||
|
|
||||||
|
Upgrade an existing plain-dir agent's state into a btrfs subvolume so it gains snapshots and per-subvol usage/quota.
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents subvol <COMMAND>`
|
||||||
|
|
||||||
|
###### **Subcommands:**
|
||||||
|
|
||||||
|
* `upgrade` — Convert a plain-dir agent state root into a btrfs subvolume in place, so it gains snapshots and per-subvol usage/quota
|
||||||
|
* `snapshot` — Read-only snapshots of an agent's state subvolume
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents subvol upgrade`
|
||||||
|
|
||||||
|
Convert a plain-dir agent state root into a btrfs subvolume in place, so it gains snapshots and per-subvol usage/quota.
|
||||||
|
|
||||||
|
Bounces the agent to migrate its state, so it requires `--yes`.
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents subvol upgrade [OPTIONS] <NAME>`
|
||||||
|
|
||||||
|
###### **Arguments:**
|
||||||
|
|
||||||
|
* `<NAME>` — Agent name (e.g. `damocles`, `iris`)
|
||||||
|
|
||||||
|
###### **Options:**
|
||||||
|
|
||||||
|
* `--yes` — Confirm: this stops the agent, migrates its state dir, and restarts it. Required — the command refuses without it
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents subvol snapshot`
|
||||||
|
|
||||||
|
Read-only snapshots of an agent's state subvolume
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents subvol snapshot <COMMAND>`
|
||||||
|
|
||||||
|
###### **Subcommands:**
|
||||||
|
|
||||||
|
* `create` — Create a read-only snapshot (agent must already be a subvolume)
|
||||||
|
* `delete` — Delete a snapshot created by `subvol snapshot create`
|
||||||
|
* `send` — Export a snapshot to a local file via `btrfs send` (the local-file half of inter-hive migration transport; the cross-hive `ssh ... btrfs receive` leg isn't wired up yet). Also useful standalone as a point-in-time backup: a full send with no `--parent` produces a self-contained archive of the snapshot
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents subvol snapshot create`
|
||||||
|
|
||||||
|
Create a read-only snapshot (agent must already be a subvolume)
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents subvol snapshot create --label <LABEL> <NAME>`
|
||||||
|
|
||||||
|
###### **Arguments:**
|
||||||
|
|
||||||
|
* `<NAME>` — Agent name (e.g. `damocles`, `iris`)
|
||||||
|
|
||||||
|
###### **Options:**
|
||||||
|
|
||||||
|
* `--label <LABEL>` — Snapshot label. Mandatory, and must start with `hive-` — the prefix doubles as an allow-list hive-priv checks so only hivectl-issued snapshot names can reach the `btrfs subvolume snapshot` shellout
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents subvol snapshot delete`
|
||||||
|
|
||||||
|
Delete a snapshot created by `subvol snapshot create`
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents subvol snapshot delete <NAME> <LABEL>`
|
||||||
|
|
||||||
|
###### **Arguments:**
|
||||||
|
|
||||||
|
* `<NAME>` — Agent name the snapshot belongs to
|
||||||
|
* `<LABEL>` — Snapshot label passed to `subvol snapshot create --label`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## `hivectl agents subvol snapshot send`
|
||||||
|
|
||||||
|
Export a snapshot to a local file via `btrfs send` (the local-file half of inter-hive migration transport; the cross-hive `ssh ... btrfs receive` leg isn't wired up yet). Also useful standalone as a point-in-time backup: a full send with no `--parent` produces a self-contained archive of the snapshot
|
||||||
|
|
||||||
|
**Usage:** `hivectl agents subvol snapshot send [OPTIONS] --dest <DEST> <NAME> <LABEL>`
|
||||||
|
|
||||||
|
###### **Arguments:**
|
||||||
|
|
||||||
|
* `<NAME>` — Agent name the snapshot belongs to
|
||||||
|
* `<LABEL>` — Snapshot label passed to `subvol snapshot create --label`
|
||||||
|
|
||||||
|
###### **Options:**
|
||||||
|
|
||||||
|
* `--parent <PARENT>` — Optional parent snapshot label for an incremental send (`btrfs send -p`) — must be an existing, older snapshot of the same agent. Omit for a full send
|
||||||
|
* `--dest <DEST>` — Destination filename (not a path) under the migrate-staging dir. Refused if it already exists
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl approvals`
|
## `hivectl approvals`
|
||||||
|
|
||||||
Operator approval queue: list, approve, or deny pending requests.
|
Operator approval queue: list, approve, or deny pending requests.
|
||||||
|
|
@ -719,151 +866,6 @@ Bare `hivectl restart` restarts everything; scope flags narrow it.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl quota`
|
|
||||||
|
|
||||||
Per-agent disk accounting + optional quotas via btrfs qgroups.
|
|
||||||
|
|
||||||
Opt-in: enable qgroup accounting, then report per-agent usage or cap an agent. No-op on non-btrfs hosts.
|
|
||||||
|
|
||||||
**Usage:** `hivectl quota <COMMAND>`
|
|
||||||
|
|
||||||
###### **Subcommands:**
|
|
||||||
|
|
||||||
* `enable` — Enable btrfs qgroup accounting on the agent-state filesystem
|
|
||||||
* `show` — Report per-agent disk usage from btrfs qgroups (all agents, or one by name)
|
|
||||||
* `limit` — Set or clear an agent's disk-usage quota
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl quota enable`
|
|
||||||
|
|
||||||
Enable btrfs qgroup accounting on the agent-state filesystem.
|
|
||||||
|
|
||||||
Run once before `show` / `limit`. No-op on non-btrfs hosts.
|
|
||||||
|
|
||||||
**Usage:** `hivectl quota enable`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl quota show`
|
|
||||||
|
|
||||||
Report per-agent disk usage from btrfs qgroups (all agents, or one by name)
|
|
||||||
|
|
||||||
**Usage:** `hivectl quota show [NAME]`
|
|
||||||
|
|
||||||
###### **Arguments:**
|
|
||||||
|
|
||||||
* `<NAME>` — Agent to show (omit for all agents with a state subvolume)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl quota limit`
|
|
||||||
|
|
||||||
Set or clear an agent's disk-usage quota
|
|
||||||
|
|
||||||
**Usage:** `hivectl quota limit <NAME> <SIZE>`
|
|
||||||
|
|
||||||
###### **Arguments:**
|
|
||||||
|
|
||||||
* `<NAME>` — Agent whose state subvolume to limit
|
|
||||||
* `<SIZE>` — Size cap (`5G`, `500M`, `1073741824`) or `none` to clear
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl subvol`
|
|
||||||
|
|
||||||
btrfs subvolume management for agent state dirs.
|
|
||||||
|
|
||||||
Upgrade an existing plain-dir agent's state into a btrfs subvolume so it gains snapshots and per-subvol usage/quota.
|
|
||||||
|
|
||||||
**Usage:** `hivectl subvol <COMMAND>`
|
|
||||||
|
|
||||||
###### **Subcommands:**
|
|
||||||
|
|
||||||
* `upgrade` — Convert a plain-dir agent state root into a btrfs subvolume in place, so it gains snapshots and per-subvol usage/quota
|
|
||||||
* `snapshot` — Read-only snapshots of an agent's state subvolume
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl subvol upgrade`
|
|
||||||
|
|
||||||
Convert a plain-dir agent state root into a btrfs subvolume in place, so it gains snapshots and per-subvol usage/quota.
|
|
||||||
|
|
||||||
Bounces the agent to migrate its state, so it requires `--yes`.
|
|
||||||
|
|
||||||
**Usage:** `hivectl subvol upgrade [OPTIONS] <NAME>`
|
|
||||||
|
|
||||||
###### **Arguments:**
|
|
||||||
|
|
||||||
* `<NAME>` — Agent name (e.g. `damocles`, `iris`)
|
|
||||||
|
|
||||||
###### **Options:**
|
|
||||||
|
|
||||||
* `--yes` — Confirm: this stops the agent, migrates its state dir, and restarts it. Required — the command refuses without it
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl subvol snapshot`
|
|
||||||
|
|
||||||
Read-only snapshots of an agent's state subvolume
|
|
||||||
|
|
||||||
**Usage:** `hivectl subvol snapshot <COMMAND>`
|
|
||||||
|
|
||||||
###### **Subcommands:**
|
|
||||||
|
|
||||||
* `create` — Create a read-only snapshot (agent must already be a subvolume)
|
|
||||||
* `delete` — Delete a snapshot created by `subvol snapshot create`
|
|
||||||
* `send` — Export a snapshot to a local file via `btrfs send` (the local-file half of inter-hive migration transport; the cross-hive `ssh ... btrfs receive` leg isn't wired up yet). Also useful standalone as a point-in-time backup: a full send with no `--parent` produces a self-contained archive of the snapshot
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl subvol snapshot create`
|
|
||||||
|
|
||||||
Create a read-only snapshot (agent must already be a subvolume)
|
|
||||||
|
|
||||||
**Usage:** `hivectl subvol snapshot create --label <LABEL> <NAME>`
|
|
||||||
|
|
||||||
###### **Arguments:**
|
|
||||||
|
|
||||||
* `<NAME>` — Agent name (e.g. `damocles`, `iris`)
|
|
||||||
|
|
||||||
###### **Options:**
|
|
||||||
|
|
||||||
* `--label <LABEL>` — Snapshot label. Mandatory, and must start with `hive-` — the prefix doubles as an allow-list hive-priv checks so only hivectl-issued snapshot names can reach the `btrfs subvolume snapshot` shellout
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl subvol snapshot delete`
|
|
||||||
|
|
||||||
Delete a snapshot created by `subvol snapshot create`
|
|
||||||
|
|
||||||
**Usage:** `hivectl subvol snapshot delete <NAME> <LABEL>`
|
|
||||||
|
|
||||||
###### **Arguments:**
|
|
||||||
|
|
||||||
* `<NAME>` — Agent name the snapshot belongs to
|
|
||||||
* `<LABEL>` — Snapshot label passed to `subvol snapshot create --label`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl subvol snapshot send`
|
|
||||||
|
|
||||||
Export a snapshot to a local file via `btrfs send` (the local-file half of inter-hive migration transport; the cross-hive `ssh ... btrfs receive` leg isn't wired up yet). Also useful standalone as a point-in-time backup: a full send with no `--parent` produces a self-contained archive of the snapshot
|
|
||||||
|
|
||||||
**Usage:** `hivectl subvol snapshot send [OPTIONS] --dest <DEST> <NAME> <LABEL>`
|
|
||||||
|
|
||||||
###### **Arguments:**
|
|
||||||
|
|
||||||
* `<NAME>` — Agent name the snapshot belongs to
|
|
||||||
* `<LABEL>` — Snapshot label passed to `subvol snapshot create --label`
|
|
||||||
|
|
||||||
###### **Options:**
|
|
||||||
|
|
||||||
* `--parent <PARENT>` — Optional parent snapshot label for an incremental send (`btrfs send -p`) — must be an existing, older snapshot of the same agent. Omit for a full send
|
|
||||||
* `--dest <DEST>` — Destination filename (not a path) under the migrate-staging dir. Refused if it already exists
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## `hivectl open`
|
## `hivectl open`
|
||||||
|
|
||||||
Print (and best-effort open in a browser) a hive web surface URL.
|
Print (and best-effort open in a browser) a hive web surface URL.
|
||||||
|
|
|
||||||
|
|
@ -603,7 +603,7 @@ async fn handle_quota_show(name: Option<&str>) -> Result<HostResponse> {
|
||||||
// varies across btrfs-progs versions).
|
// varies across btrfs-progs versions).
|
||||||
if msg.to_ascii_lowercase().contains("quota not enabled") {
|
if msg.to_ascii_lowercase().contains("quota not enabled") {
|
||||||
return Ok(HostResponse::error(
|
return Ok(HostResponse::error(
|
||||||
"btrfs quota not enabled — run `hivectl quota enable` first",
|
"btrfs quota not enabled — run `hivectl agents quota enable` first",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// A plain-dir agent (no subvolume) has no qgroup; note it
|
// A plain-dir agent (no subvolume) has no qgroup; note it
|
||||||
|
|
|
||||||
|
|
@ -290,12 +290,12 @@ pub enum HostRequest {
|
||||||
/// the agent's `gh` wrapper / git credential helper — no rebuild needed.
|
/// the agent's `gh` wrapper / git credential helper — no rebuild needed.
|
||||||
SetAgentGithubToken { agent: Ident, token: String },
|
SetAgentGithubToken { agent: Ident, token: String },
|
||||||
/// Turn on btrfs qgroup accounting on the agent-state filesystem, via
|
/// Turn on btrfs qgroup accounting on the agent-state filesystem, via
|
||||||
/// the privileged helper. Daemon-side equivalent of `hivectl quota
|
/// the privileged helper. Daemon-side equivalent of `hivectl agents quota
|
||||||
/// enable`. Returns advisory lines in [`HostResponse::messages`].
|
/// enable`. Returns advisory lines in [`HostResponse::messages`].
|
||||||
QuotaEnable,
|
QuotaEnable,
|
||||||
/// Set (or, with `limit = None`, clear) an agent's btrfs disk quota via
|
/// Set (or, with `limit = None`, clear) an agent's btrfs disk quota via
|
||||||
/// the privileged helper. Daemon-side equivalent of `hivectl quota
|
/// the privileged helper. Daemon-side equivalent of `hivectl agents quota
|
||||||
/// limit`; the size string is parsed to bytes client-side. Returns a
|
/// set`; the size string is parsed to bytes client-side. Returns a
|
||||||
/// bare success — the client prints the confirmation from the value it
|
/// bare success — the client prints the confirmation from the value it
|
||||||
/// sent.
|
/// sent.
|
||||||
QuotaLimit {
|
QuotaLimit {
|
||||||
|
|
@ -303,7 +303,7 @@ pub enum HostRequest {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
limit: Option<u64>,
|
limit: Option<u64>,
|
||||||
},
|
},
|
||||||
/// Report per-agent btrfs qgroup usage (`hivectl quota show [name]`).
|
/// Report per-agent btrfs qgroup usage (`hivectl agents quota show [name]`).
|
||||||
/// The daemon resolves the agent set (all kept state dirs when `name`
|
/// The daemon resolves the agent set (all kept state dirs when `name`
|
||||||
/// is absent) and reads each subvolume's referenced/exclusive usage via
|
/// is absent) and reads each subvolume's referenced/exclusive usage via
|
||||||
/// the privileged helper. Result rows land in [`HostResponse::quota`];
|
/// the privileged helper. Result rows land in [`HostResponse::quota`];
|
||||||
|
|
@ -314,19 +314,19 @@ pub enum HostRequest {
|
||||||
name: Option<Ident>,
|
name: Option<Ident>,
|
||||||
},
|
},
|
||||||
/// Migrate an agent's plain state dir to a btrfs subvolume via the
|
/// Migrate an agent's plain state dir to a btrfs subvolume via the
|
||||||
/// privileged helper (`hivectl subvol upgrade`). The agent MUST already
|
/// privileged helper (`hivectl agents subvol upgrade`). The agent MUST already
|
||||||
/// be stopped — the client orchestrates stop → this → start. Returns a
|
/// be stopped — the client orchestrates stop → this → start. Returns a
|
||||||
/// bare success; the client prints its own progress lines.
|
/// bare success; the client prints its own progress lines.
|
||||||
UpgradeSubvolume { name: Ident },
|
UpgradeSubvolume { name: Ident },
|
||||||
/// Create a read-only btrfs snapshot of an agent's state subvolume
|
/// Create a read-only btrfs snapshot of an agent's state subvolume
|
||||||
/// (`hivectl subvol snapshot create`). `label` is validated client-side
|
/// (`hivectl agents subvol snapshot create`). `label` is validated client-side
|
||||||
/// AND by hive-priv. Returns the snapshot's host path in
|
/// AND by hive-priv. Returns the snapshot's host path in
|
||||||
/// [`HostResponse::messages`].
|
/// [`HostResponse::messages`].
|
||||||
SnapshotSubvolume { name: Ident, label: String },
|
SnapshotSubvolume { name: Ident, label: String },
|
||||||
/// Delete a snapshot created by `SnapshotSubvolume` (`hivectl subvol
|
/// Delete a snapshot created by `SnapshotSubvolume` (`hivectl agents subvol
|
||||||
/// snapshot delete`). Bare success; the client prints the confirmation.
|
/// snapshot delete`). Bare success; the client prints the confirmation.
|
||||||
DeleteSnapshot { name: Ident, label: String },
|
DeleteSnapshot { name: Ident, label: String },
|
||||||
/// Export a snapshot to a local file via `btrfs send` (`hivectl subvol
|
/// Export a snapshot to a local file via `btrfs send` (`hivectl agents subvol
|
||||||
/// snapshot send`). `dest` is a bare filename (hive-priv rejects paths);
|
/// snapshot send`). `dest` is a bare filename (hive-priv rejects paths);
|
||||||
/// `parent` names an optional parent snapshot for an incremental send.
|
/// `parent` names an optional parent snapshot for an incremental send.
|
||||||
/// Returns the written file's host path in [`HostResponse::messages`].
|
/// Returns the written file's host path in [`HostResponse::messages`].
|
||||||
|
|
|
||||||
|
|
@ -1178,7 +1178,7 @@ async fn snapshot_agent_subvolume(
|
||||||
let agent_root = PathBuf::from(AGENT_STATE_ROOT).join(agent_name);
|
let agent_root = PathBuf::from(AGENT_STATE_ROOT).join(agent_name);
|
||||||
if !is_btrfs_subvolume(&agent_root) {
|
if !is_btrfs_subvolume(&agent_root) {
|
||||||
bail!(
|
bail!(
|
||||||
"{} is not a btrfs subvolume — nothing to snapshot (run `hivectl subvol upgrade` first)",
|
"{} is not a btrfs subvolume — nothing to snapshot (run `hivectl agents subvol upgrade` first)",
|
||||||
agent_root.display()
|
agent_root.display()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
//! `hivectl agents` — container lifecycle over the host admin socket
|
//! `hivectl agents` — everything scoped to a managed agent: container
|
||||||
//! (list/restart/restart-all/spawn/kill/destroy/rebuild/set-parent).
|
//! lifecycle over the host admin socket
|
||||||
|
//! (list/restart/restart-all/pause/resume/spawn/kill/destroy/rebuild/
|
||||||
|
//! set-parent/set-limits), plus the `quota` and `subvol` groups, whose
|
||||||
|
//! handlers live in their own modules.
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{Context as _, Result, bail};
|
use anyhow::{Context as _, Result, bail};
|
||||||
use hive_host_sock::HostRequest;
|
use hive_host_sock::HostRequest;
|
||||||
|
|
||||||
use crate::cli::AgentsCmd;
|
use crate::cli::{AgentsCmd, QuotaCmd};
|
||||||
use crate::dag_progress::wait_for_dags;
|
use crate::dag_progress::wait_for_dags;
|
||||||
use crate::util::render;
|
use crate::util::render;
|
||||||
|
|
||||||
|
|
@ -228,5 +231,13 @@ pub(crate) async fn run_agents(socket: &Path, cmd: AgentsCmd) -> Result<()> {
|
||||||
.await?,
|
.await?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
// `quota` and `subvol` keep their own modules — this arm is just
|
||||||
|
// the reparenting glue that moved them under `agents`.
|
||||||
|
AgentsCmd::Quota { cmd } => match cmd {
|
||||||
|
QuotaCmd::Enable => crate::quota::quota_enable(socket).await,
|
||||||
|
QuotaCmd::Show { name } => crate::quota::quota_show(socket, name.as_deref()).await,
|
||||||
|
QuotaCmd::Set { name, size } => crate::quota::quota_limit(socket, &name, &size).await,
|
||||||
|
},
|
||||||
|
AgentsCmd::Subvol { cmd } => crate::subvol::dispatch_subvol(socket, cmd).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -158,22 +158,6 @@ pub enum Cmd {
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
graceful: bool,
|
graceful: bool,
|
||||||
},
|
},
|
||||||
/// Per-agent disk accounting + optional quotas via btrfs qgroups.
|
|
||||||
///
|
|
||||||
/// Opt-in: enable qgroup accounting, then report per-agent usage or
|
|
||||||
/// cap an agent. No-op on non-btrfs hosts.
|
|
||||||
Quota {
|
|
||||||
#[command(subcommand)]
|
|
||||||
cmd: QuotaCmd,
|
|
||||||
},
|
|
||||||
/// btrfs subvolume management for agent state dirs.
|
|
||||||
///
|
|
||||||
/// Upgrade an existing plain-dir agent's state into a btrfs subvolume
|
|
||||||
/// so it gains snapshots and per-subvol usage/quota.
|
|
||||||
Subvol {
|
|
||||||
#[command(subcommand)]
|
|
||||||
cmd: SubvolCmd,
|
|
||||||
},
|
|
||||||
/// Print (and best-effort open in a browser) a hive web surface URL.
|
/// Print (and best-effort open in a browser) a hive web surface URL.
|
||||||
///
|
///
|
||||||
/// Resolves the URL from the running daemon so custom forge / matrix
|
/// Resolves the URL from the running daemon so custom forge / matrix
|
||||||
|
|
@ -464,7 +448,12 @@ pub enum QuotaCmd {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
},
|
},
|
||||||
/// Set or clear an agent's disk-usage quota.
|
/// Set or clear an agent's disk-usage quota.
|
||||||
Limit {
|
///
|
||||||
|
/// Named `set` rather than `set-quota` because the enclosing `quota`
|
||||||
|
/// group already carries the noun — `agents quota set iris 5G`. The
|
||||||
|
/// `set-<noun>` spelling stays for the flat verbs (`set-parent`,
|
||||||
|
/// `set-limits`), which have no group to inherit it from.
|
||||||
|
Set {
|
||||||
/// Agent whose state subvolume to limit.
|
/// Agent whose state subvolume to limit.
|
||||||
name: String,
|
name: String,
|
||||||
/// Size cap (`5G`, `500M`, `1073741824`) or `none` to clear.
|
/// Size cap (`5G`, `500M`, `1073741824`) or `none` to clear.
|
||||||
|
|
@ -563,7 +552,8 @@ pub enum AgentsCmd {
|
||||||
///
|
///
|
||||||
/// Replaces the agent's whole override entry rather than merging into
|
/// Replaces the agent's whole override entry rather than merging into
|
||||||
/// it: any limit you don't pass returns to the hive-wide default. To
|
/// it: any limit you don't pass returns to the hive-wide default. To
|
||||||
/// change one and keep the other, pass both.
|
/// change one and keep the other, pass both. Disk is a separate
|
||||||
|
/// resource with its own group — see `agents quota`.
|
||||||
SetLimits {
|
SetLimits {
|
||||||
/// Agent name.
|
/// Agent name.
|
||||||
name: String,
|
name: String,
|
||||||
|
|
@ -583,6 +573,22 @@ pub enum AgentsCmd {
|
||||||
)]
|
)]
|
||||||
reset: bool,
|
reset: bool,
|
||||||
},
|
},
|
||||||
|
/// Per-agent disk accounting + optional quotas via btrfs qgroups.
|
||||||
|
///
|
||||||
|
/// Opt-in: enable qgroup accounting, then report per-agent usage or
|
||||||
|
/// cap an agent. No-op on non-btrfs hosts.
|
||||||
|
Quota {
|
||||||
|
#[command(subcommand)]
|
||||||
|
cmd: QuotaCmd,
|
||||||
|
},
|
||||||
|
/// btrfs subvolume management for agent state dirs.
|
||||||
|
///
|
||||||
|
/// Upgrade an existing plain-dir agent's state into a btrfs subvolume
|
||||||
|
/// so it gains snapshots and per-subvol usage/quota.
|
||||||
|
Subvol {
|
||||||
|
#[command(subcommand)]
|
||||||
|
cmd: SubvolCmd,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Operator approval queue: list, approve, or deny pending requests.
|
/// Operator approval queue: list, approve, or deny pending requests.
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,10 @@ mod client;
|
||||||
/// Rebuild-queue DAG progress rendering (`wait_for_dags` + the spinner /
|
/// Rebuild-queue DAG progress rendering (`wait_for_dags` + the spinner /
|
||||||
/// plain renderers), split out to keep this file manageable.
|
/// plain renderers), split out to keep this file manageable.
|
||||||
mod dag_progress;
|
mod dag_progress;
|
||||||
use cli::{Cli, Cmd, ForgeCmd, GatewayCmd, GithubCmd, QuotaCmd, WgCmd};
|
use cli::{Cli, Cmd, ForgeCmd, GatewayCmd, GithubCmd, WgCmd};
|
||||||
|
mod completions;
|
||||||
mod quota;
|
mod quota;
|
||||||
mod util;
|
mod util;
|
||||||
use quota::{quota_enable, quota_limit, quota_show};
|
|
||||||
mod completions;
|
|
||||||
use completions::generate_completions;
|
use completions::generate_completions;
|
||||||
mod gateway;
|
mod gateway;
|
||||||
use gateway::{gateway_create_user, gateway_delete_user, gateway_list_users};
|
use gateway::{gateway_create_user, gateway_delete_user, gateway_list_users};
|
||||||
|
|
@ -47,9 +46,8 @@ mod agents;
|
||||||
use agents::run_agents;
|
use agents::run_agents;
|
||||||
mod power;
|
mod power;
|
||||||
use power::{restart, start, stop};
|
use power::{restart, start, stop};
|
||||||
mod subvol;
|
|
||||||
use subvol::dispatch_subvol;
|
|
||||||
mod approvals;
|
mod approvals;
|
||||||
|
mod subvol;
|
||||||
use approvals::run_approvals;
|
use approvals::run_approvals;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|
@ -122,16 +120,10 @@ async fn main() -> Result<()> {
|
||||||
} => stop(&socket, scope.to_scope(), graceful, no_wait).await,
|
} => stop(&socket, scope.to_scope(), graceful, no_wait).await,
|
||||||
Cmd::Start { scope, no_wait } => start(&socket, scope.to_scope(), no_wait).await,
|
Cmd::Start { scope, no_wait } => start(&socket, scope.to_scope(), no_wait).await,
|
||||||
Cmd::Restart { scope, graceful } => restart(&socket, scope.to_scope(), graceful).await,
|
Cmd::Restart { scope, graceful } => restart(&socket, scope.to_scope(), graceful).await,
|
||||||
Cmd::Subvol { cmd } => dispatch_subvol(&socket, cmd).await,
|
|
||||||
Cmd::Choom {
|
Cmd::Choom {
|
||||||
name,
|
name,
|
||||||
resume_session,
|
resume_session,
|
||||||
} => choom(&name, resume_session.as_deref()),
|
} => choom(&name, resume_session.as_deref()),
|
||||||
Cmd::Quota { cmd } => match cmd {
|
|
||||||
QuotaCmd::Enable => quota_enable(&socket).await,
|
|
||||||
QuotaCmd::Show { name } => quota_show(&socket, name.as_deref()).await,
|
|
||||||
QuotaCmd::Limit { name, size } => quota_limit(&socket, &name, &size).await,
|
|
||||||
},
|
|
||||||
Cmd::MarkdownDocs => {
|
Cmd::MarkdownDocs => {
|
||||||
print!("{}", clap_markdown::help_markdown::<Cli>());
|
print!("{}", clap_markdown::help_markdown::<Cli>());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! `hivectl quota` — per-agent disk accounting + optional quotas via
|
//! `hivectl agents quota` — per-agent disk accounting + optional quotas via
|
||||||
//! btrfs qgroups. The daemon holds the privileged helper that reads /
|
//! btrfs qgroups. The daemon holds the privileged helper that reads /
|
||||||
//! sets qgroups; hivectl relays the request and formats the reply.
|
//! sets qgroups; hivectl relays the request and formats the reply.
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ pub(crate) async fn quota_show(socket: &Path, name: Option<&str>) -> Result<()>
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
||||||
if !resp.ok {
|
if !resp.ok {
|
||||||
// Carries the "btrfs quota not enabled — run `hivectl quota enable`
|
// Carries the "btrfs quota not enabled — run `hivectl agents quota enable`
|
||||||
// first" hint when qgroups are off.
|
// first" hint when qgroups are off.
|
||||||
bail!("{}", resp.error.as_deref().unwrap_or("quota show failed"));
|
bail!("{}", resp.error.as_deref().unwrap_or("quota show failed"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
//! `hivectl subvol` — btrfs state-subvolume ops: migrate a plain-dir agent
|
//! `hivectl agents subvol` — btrfs state-subvolume ops: migrate a plain-dir agent
|
||||||
//! state root to a subvolume (`upgrade`), and snapshot create/delete/send.
|
//! state root to a subvolume (`upgrade`), and snapshot create/delete/send.
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
@ -28,7 +28,7 @@ fn single_agent_scope(name: &str) -> hive_host_sock::LifecycleScope {
|
||||||
/// migration via hive-priv, then restart it. The restart is attempted
|
/// migration via hive-priv, then restart it. The restart is attempted
|
||||||
/// regardless of the migration outcome so a failed migration never leaves
|
/// regardless of the migration outcome so a failed migration never leaves
|
||||||
/// the agent down; the migration error (if any) is surfaced afterwards.
|
/// the agent down; the migration error (if any) is surfaced afterwards.
|
||||||
/// Route a `hivectl subvol …` subcommand. Split out of `main`'s top-level
|
/// Route a `hivectl agents subvol …` subcommand. Split out of `main`'s top-level
|
||||||
/// match so the CLI router stays within the clippy line budget and the
|
/// match so the CLI router stays within the clippy line budget and the
|
||||||
/// subvolume-op subcommands are dispatched in one place.
|
/// subvolume-op subcommands are dispatched in one place.
|
||||||
pub(crate) async fn dispatch_subvol(socket: &Path, cmd: SubvolCmd) -> Result<()> {
|
pub(crate) async fn dispatch_subvol(socket: &Path, cmd: SubvolCmd) -> Result<()> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue