Skip to content

Commit 38087de

Browse files
committed
refactor(volume): parse commands with clap derive
1 parent aa47b6b commit 38087de

3 files changed

Lines changed: 42 additions & 30 deletions

File tree

dstack/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dstack/crates/dstack-volume/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ license.workspace = true
1212
anyhow = { workspace = true, features = ["std"] }
1313
binrw.workspace = true
1414
cmd_lib.workspace = true
15+
clap.workspace = true
1516
dstack-types.workspace = true
1617
tokio = { workspace = true, features = ["rt"] }
1718
serde_json = { workspace = true, features = ["std"] }

dstack/crates/dstack-volume/src/bin/dstack-volume.rs

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::os::unix::fs::MetadataExt;
1616
use std::path::{Path, PathBuf};
1717

1818
use anyhow::{bail, Context, Result};
19+
use clap::{Parser, Subcommand};
1920
use cmd_lib::{run_cmd, run_fun};
2021
use dstack_types::{AppCompose, VerityVolume as RequestedVolume};
2122
use dstack_volume::volume_format::{
@@ -39,38 +40,38 @@ struct VerityVolume {
3940
root_hash: [u8; 32],
4041
}
4142

43+
#[derive(Parser)]
44+
#[command(about = "Discover and activate dstack verity volumes")]
45+
struct Cli {
46+
#[command(subcommand)]
47+
command: VolumeCommand,
48+
}
49+
50+
#[derive(Subcommand)]
51+
enum VolumeCommand {
52+
/// Activate every verity volume required by the measured app compose.
53+
MountAll {
54+
#[arg(default_value = "app-compose.json")]
55+
compose: PathBuf,
56+
},
57+
/// Activate one required volume by its index in app compose.
58+
Mount { compose: PathBuf, index: usize },
59+
/// List recognized dstack volume devices.
60+
Scan,
61+
/// Compare required volumes with attached and active devices.
62+
Status {
63+
#[arg(default_value = "app-compose.json")]
64+
compose: PathBuf,
65+
},
66+
}
67+
4268
fn main() -> Result<()> {
4369
tracing_subscriber::fmt().init();
44-
let mut args = std::env::args_os().skip(1);
45-
let command = args
46-
.next()
47-
.context(
48-
"usage: dstack-volume <mount-all [COMPOSE] | mount COMPOSE INDEX | scan | status [COMPOSE]>",
49-
)?;
50-
match command.to_str() {
51-
Some("mount-all") => mount_all(
52-
args.next()
53-
.map(PathBuf::from)
54-
.unwrap_or_else(|| PathBuf::from("app-compose.json")),
55-
),
56-
Some("mount") => {
57-
let compose = args.next().context("mount requires COMPOSE and INDEX")?;
58-
let index = args
59-
.next()
60-
.context("mount requires COMPOSE and INDEX")?
61-
.to_str()
62-
.context("INDEX is not UTF-8")?
63-
.parse()
64-
.context("invalid volume INDEX")?;
65-
mount_one(PathBuf::from(compose), index)
66-
}
67-
Some("scan") => scan(),
68-
Some("status") => status(
69-
args.next()
70-
.map(PathBuf::from)
71-
.unwrap_or_else(|| PathBuf::from("app-compose.json")),
72-
),
73-
_ => bail!("unknown command {:?}", command),
70+
match Cli::parse().command {
71+
VolumeCommand::MountAll { compose } => mount_all(compose),
72+
VolumeCommand::Mount { compose, index } => mount_one(compose, index),
73+
VolumeCommand::Scan => scan(),
74+
VolumeCommand::Status { compose } => status(compose),
7475
}
7576
}
7677

@@ -447,6 +448,15 @@ fn mapping_root(mapper_name: &str) -> Result<String> {
447448
mod tests {
448449
use super::*;
449450

451+
#[test]
452+
fn cli_uses_default_compose_for_mount_all() {
453+
let cli = Cli::try_parse_from(["dstack-volume", "mount-all"]).unwrap();
454+
let VolumeCommand::MountAll { compose } = cli.command else {
455+
panic!("unexpected command");
456+
};
457+
assert_eq!(compose, Path::new("app-compose.json"));
458+
}
459+
450460
fn header() -> DstackVolumeHeader {
451461
DstackVolumeHeader::new_verity([0x5a; 32])
452462
}

0 commit comments

Comments
 (0)