refactor(#2352): subvolume snapshot ops via daemon wire commands
This commit is contained in:
parent
860484a193
commit
7b54e7aa50
3 changed files with 138 additions and 29 deletions
|
|
@ -740,19 +740,7 @@ 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 } => match cmd {
|
||||
SubvolCmd::Upgrade { name, yes } => subvol_upgrade(&socket, &name, yes).await,
|
||||
SubvolCmd::Snapshot { cmd } => match cmd {
|
||||
SnapshotCmd::Create { name, label } => subvol_snapshot_create(&name, label).await,
|
||||
SnapshotCmd::Delete { name, label } => subvol_snapshot_delete(&name, &label).await,
|
||||
SnapshotCmd::Send {
|
||||
name,
|
||||
label,
|
||||
parent,
|
||||
dest,
|
||||
} => subvol_snapshot_send(&name, &label, parent.as_deref(), &dest).await,
|
||||
},
|
||||
},
|
||||
Cmd::Subvol { cmd } => dispatch_subvol(&socket, cmd).await,
|
||||
Cmd::Choom {
|
||||
name,
|
||||
resume_session,
|
||||
|
|
@ -1714,6 +1702,29 @@ 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
|
||||
/// match so the CLI router stays within the clippy line budget and the
|
||||
/// subvolume-op subcommands are dispatched in one place.
|
||||
async fn dispatch_subvol(socket: &Path, cmd: SubvolCmd) -> Result<()> {
|
||||
match cmd {
|
||||
SubvolCmd::Upgrade { name, yes } => subvol_upgrade(socket, &name, yes).await,
|
||||
SubvolCmd::Snapshot { cmd } => match cmd {
|
||||
SnapshotCmd::Create { name, label } => {
|
||||
subvol_snapshot_create(socket, &name, label).await
|
||||
}
|
||||
SnapshotCmd::Delete { name, label } => {
|
||||
subvol_snapshot_delete(socket, &name, &label).await
|
||||
}
|
||||
SnapshotCmd::Send {
|
||||
name,
|
||||
label,
|
||||
parent,
|
||||
dest,
|
||||
} => subvol_snapshot_send(socket, &name, &label, parent.as_deref(), &dest).await,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async fn subvol_upgrade(socket: &Path, name: &str, yes: bool) -> Result<()> {
|
||||
if !agent_exists(name)? {
|
||||
bail!("no agent named {name:?} (no state dir under the agents root)");
|
||||
|
|
@ -1749,7 +1760,14 @@ async fn subvol_upgrade(socket: &Path, name: &str, yes: bool) -> Result<()> {
|
|||
.with_context(|| format!("waiting for {name} to stop before the migration"))?;
|
||||
|
||||
println!("migrating {name} state dir to a btrfs subvolume…");
|
||||
let upgrade = hive_c0re::priv_client::upgrade_agent_subvolume(name).await;
|
||||
let upgrade = daemon_request(
|
||||
socket,
|
||||
hive_host_sock::HostRequest::UpgradeSubvolume {
|
||||
name: name.to_owned(),
|
||||
},
|
||||
"upgrade",
|
||||
)
|
||||
.await;
|
||||
|
||||
// Always attempt the restart, even if the migration failed — don't leave
|
||||
// the agent down. Capture the result rather than `?`-ing it so a
|
||||
|
|
@ -1806,7 +1824,7 @@ async fn subvol_upgrade(socket: &Path, name: &str, yes: bool) -> Result<()> {
|
|||
/// — mara: "we are making up the rules here, lets go strict"). hive-priv
|
||||
/// enforces the same rules server-side, so this check is
|
||||
/// belt-and-suspenders (fail fast client-side with a clear message).
|
||||
async fn subvol_snapshot_create(name: &str, label: String) -> Result<()> {
|
||||
async fn subvol_snapshot_create(socket: &Path, name: &str, label: String) -> Result<()> {
|
||||
if !agent_exists(name)? {
|
||||
bail!("no agent named {name:?} (no state dir under the agents root)");
|
||||
}
|
||||
|
|
@ -1821,19 +1839,31 @@ async fn subvol_snapshot_create(name: &str, label: String) -> Result<()> {
|
|||
"snapshot label {label:?} must be [A-Za-z0-9_-] only (no \".\" — hive-priv rejects it)"
|
||||
);
|
||||
}
|
||||
let path = hive_c0re::priv_client::snapshot_agent_subvolume(name, &label)
|
||||
.await
|
||||
.with_context(|| format!("snapshot {name} state subvolume (label {label:?})"))?;
|
||||
println!("{path}");
|
||||
Ok(())
|
||||
// The daemon returns the snapshot's host path as a message line, which
|
||||
// `daemon_request` prints — same bare-path output as before.
|
||||
daemon_request(
|
||||
socket,
|
||||
hive_host_sock::HostRequest::SnapshotSubvolume {
|
||||
name: name.to_owned(),
|
||||
label,
|
||||
},
|
||||
"snapshot",
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `subvol snapshot delete <agent> <label>` — remove a snapshot created by
|
||||
/// `subvol snapshot create`.
|
||||
async fn subvol_snapshot_delete(name: &str, label: &str) -> Result<()> {
|
||||
hive_c0re::priv_client::delete_agent_snapshot(name, label)
|
||||
.await
|
||||
.with_context(|| format!("delete {name} snapshot (label {label:?})"))?;
|
||||
async fn subvol_snapshot_delete(socket: &Path, name: &str, label: &str) -> Result<()> {
|
||||
daemon_request(
|
||||
socket,
|
||||
hive_host_sock::HostRequest::DeleteSnapshot {
|
||||
name: name.to_owned(),
|
||||
label: label.to_owned(),
|
||||
},
|
||||
"snapshot delete",
|
||||
)
|
||||
.await?;
|
||||
println!("deleted snapshot {label:?} for {name}");
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1844,16 +1874,25 @@ async fn subvol_snapshot_delete(name: &str, label: &str) -> Result<()> {
|
|||
/// charset check, which excludes `/`), written under the migrate-staging
|
||||
/// dir. Prints the resulting file's host path.
|
||||
async fn subvol_snapshot_send(
|
||||
socket: &Path,
|
||||
name: &str,
|
||||
label: &str,
|
||||
parent: Option<&str>,
|
||||
dest: &str,
|
||||
) -> Result<()> {
|
||||
let path = hive_c0re::priv_client::send_agent_snapshot_to_file(name, label, parent, dest)
|
||||
.await
|
||||
.with_context(|| format!("send {name} snapshot (label {label:?}) to file {dest:?}"))?;
|
||||
println!("{path}");
|
||||
Ok(())
|
||||
// The daemon returns the written file's host path as a message line,
|
||||
// which `daemon_request` prints — same bare-path output as before.
|
||||
daemon_request(
|
||||
socket,
|
||||
hive_host_sock::HostRequest::SendSnapshot {
|
||||
name: name.to_owned(),
|
||||
label: label.to_owned(),
|
||||
parent: parent.map(str::to_owned),
|
||||
dest: dest.to_owned(),
|
||||
},
|
||||
"snapshot send",
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Render a hive-wide stop/start response: one `<verb>: <name>` line per
|
||||
|
|
|
|||
Loading…
Reference in a new issue