@@ -16,6 +16,7 @@ use std::os::unix::fs::MetadataExt;
1616use std:: path:: { Path , PathBuf } ;
1717
1818use anyhow:: { bail, Context , Result } ;
19+ use clap:: { Parser , Subcommand } ;
1920use cmd_lib:: { run_cmd, run_fun} ;
2021use dstack_types:: { AppCompose , VerityVolume as RequestedVolume } ;
2122use 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+
4268fn 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> {
447448mod 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