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
|
|
@ -1,12 +1,15 @@
|
|||
//! `hivectl agents` — container lifecycle over the host admin socket
|
||||
//! (list/restart/restart-all/spawn/kill/destroy/rebuild/set-parent).
|
||||
//! `hivectl agents` — everything scoped to a managed agent: container
|
||||
//! 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 anyhow::{Context as _, Result, bail};
|
||||
use hive_host_sock::HostRequest;
|
||||
|
||||
use crate::cli::AgentsCmd;
|
||||
use crate::cli::{AgentsCmd, QuotaCmd};
|
||||
use crate::dag_progress::wait_for_dags;
|
||||
use crate::util::render;
|
||||
|
||||
|
|
@ -228,5 +231,13 @@ pub(crate) async fn run_agents(socket: &Path, cmd: AgentsCmd) -> Result<()> {
|
|||
.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)]
|
||||
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.
|
||||
///
|
||||
/// Resolves the URL from the running daemon so custom forge / matrix
|
||||
|
|
@ -464,7 +448,12 @@ pub enum QuotaCmd {
|
|||
name: Option<String>,
|
||||
},
|
||||
/// 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.
|
||||
name: String,
|
||||
/// 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
|
||||
/// 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 {
|
||||
/// Agent name.
|
||||
name: String,
|
||||
|
|
@ -583,6 +573,22 @@ pub enum AgentsCmd {
|
|||
)]
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -23,11 +23,10 @@ mod client;
|
|||
/// Rebuild-queue DAG progress rendering (`wait_for_dags` + the spinner /
|
||||
/// plain renderers), split out to keep this file manageable.
|
||||
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 util;
|
||||
use quota::{quota_enable, quota_limit, quota_show};
|
||||
mod completions;
|
||||
use completions::generate_completions;
|
||||
mod gateway;
|
||||
use gateway::{gateway_create_user, gateway_delete_user, gateway_list_users};
|
||||
|
|
@ -47,9 +46,8 @@ mod agents;
|
|||
use agents::run_agents;
|
||||
mod power;
|
||||
use power::{restart, start, stop};
|
||||
mod subvol;
|
||||
use subvol::dispatch_subvol;
|
||||
mod approvals;
|
||||
mod subvol;
|
||||
use approvals::run_approvals;
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -122,16 +120,10 @@ async fn main() -> Result<()> {
|
|||
} => stop(&socket, scope.to_scope(), graceful, 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::Subvol { cmd } => dispatch_subvol(&socket, cmd).await,
|
||||
Cmd::Choom {
|
||||
name,
|
||||
resume_session,
|
||||
} => 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 => {
|
||||
print!("{}", clap_markdown::help_markdown::<Cli>());
|
||||
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 /
|
||||
//! 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
|
||||
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
|
||||
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.
|
||||
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.
|
||||
|
||||
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
|
||||
/// regardless of the migration outcome so a failed migration never leaves
|
||||
/// 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
|
||||
/// subvolume-op subcommands are dispatched in one place.
|
||||
pub(crate) async fn dispatch_subvol(socket: &Path, cmd: SubvolCmd) -> Result<()> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue