diff --git a/Cargo.lock b/Cargo.lock index a096b85e5e6..354419ef043 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -171,17 +171,39 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.34.0" +version = "4.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +checksum = "335867764ed2de42325fafe6d18b8af74ba97ee0c590fa016f157535b42ab04b" dependencies = [ - "ansi_term", "atty", "bitflags", + "clap_derive", + "clap_lex", + "once_cell", "strsim", - "textwrap", - "unicode-width", - "vec_map", + "termcolor", +] + +[[package]] +name = "clap_derive" +version = "4.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16a1b0f6422af32d5da0c58e2703320f379216ee70198241c84173a8c5ac28f3" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" +dependencies = [ + "os_str_bytes", ] [[package]] @@ -531,6 +553,12 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1136,6 +1164,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "os_str_bytes" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1209,11 +1243,35 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" dependencies = [ "unicode-ident", ] @@ -1516,9 +1574,9 @@ checksum = "02a8428da277a8e3a15271d79943e80ccc2ef254e78813a166a08d65e4c3ece5" [[package]] name = "strsim" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" @@ -1571,15 +1629,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - [[package]] name = "thiserror" version = "1.0.31" @@ -1773,12 +1822,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - [[package]] name = "url" version = "2.2.2" @@ -1797,12 +1840,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 7e281e1acfe..c136bb2278a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ rlimit = "0.8.3" log = "0.4.8" libc = "0.2" vmm-sys-util = "0.10.0" -clap = "2.33" +clap = {version = "4.0.18", features = ["derive", "cargo"]} serde = { version = "1.0.110", features = ["serde_derive", "rc"] } serde_json = "1.0.51" sha2 = "0.10.2" diff --git a/src/bin/nydus-image/main.rs b/src/bin/nydus-image/main.rs index 32491786106..4c587be56cd 100644 --- a/src/bin/nydus-image/main.rs +++ b/src/bin/nydus-image/main.rs @@ -19,7 +19,7 @@ use std::fs::{self, metadata, DirEntry, File, OpenOptions}; use std::path::{Path, PathBuf}; use anyhow::{bail, Context, Result}; -use clap::{App, Arg, ArgMatches, SubCommand}; +use clap::{Arg, ArgMatches, Command as App, ArgAction}; use nix::unistd::{getegid, geteuid}; use nydus_api::http::BackendConfig; use nydus_app::{setup_logging, BuildTimeInfo}; @@ -75,7 +75,7 @@ impl OutputSerializer { build_info: &BuildTimeInfo, ) -> Result<()> { let output_json: Option = matches - .value_of("output-json") + .get_one::("output-json") .map(|o| o.to_string().into()); if let Some(ref f) = output_json { @@ -105,7 +105,7 @@ impl OutputSerializer { blob_ids: Vec, ) -> Result<()> { let output_json: Option = matches - .value_of("output-json") + .get_one::("output-json") .map(|o| o.to_string().into()); if let Some(ref f) = output_json { @@ -130,48 +130,44 @@ impl OutputSerializer { } } -fn prepare_cmd_args(bti_string: String) -> ArgMatches<'static> { - let arg_chunk_dict = Arg::with_name("chunk-dict") +fn prepare_cmd_args(bti_string: &'static str) -> App { + let arg_chunk_dict = Arg::new("chunk-dict") .long("chunk-dict") - .short("M") - .help("specify a chunk dictionary for chunk deduplication") - .takes_value(true); - let arg_prefetch_policy = Arg::with_name("prefetch-policy") + .short('M') + .help("specify a chunk dictionary for chunk deduplication"); + let arg_prefetch_policy = Arg::new("prefetch-policy") .long("prefetch-policy") - .short("P") + .short('P') .help("specify policy for blob data prefetch") - .takes_value(true) .required(false) .default_value("none") - .possible_values(&["fs", "blob", "none"]); - let arg_output_json = Arg::with_name("output-json") + .value_parser(["fs", "blob", "none"]); + let arg_output_json = Arg::new("output-json") .long("output-json") - .short("J") - .help("output file to store result in JSON format") - .takes_value(true); + .short('J') + .help("output file to store result in JSON format"); App::new("") - .version(bti_string.as_str()) + .version(bti_string) .author(crate_authors!()) .about("Build or inspect RAFS filesystems for Nydus accelerated container images.") .subcommand( - SubCommand::with_name("create") + App::new("create") .about("Create a RAFS filesystem from a directory, an OCI tar file or an estargz file") .arg( - Arg::with_name("SOURCE") + Arg::new("SOURCE") .help("source to build the RAFS filesystem from") .required(true) - .multiple(false), + .num_args(1), ) .arg( - Arg::with_name("type") + Arg::new("type") .long("type") - .short("t") + .short('t') .alias("source-type") .help("image conversion type:") - .takes_value(true) .default_value("directory") - .possible_values(&[ + .value_parser([ "directory", "dir-rafs", "estargz-rafs", @@ -182,141 +178,127 @@ fn prepare_cmd_args(bti_string: String) -> ArgMatches<'static> { ]) ) .arg( - Arg::with_name("bootstrap") + Arg::new("bootstrap") .long("bootstrap") - .short("B") + .short('B') .help("path to store generated RAFS filesystem metadata blob") - .required_unless_one(&["blob-dir", "inline-bootstrap"]) - .conflicts_with("inline-bootstrap") - .takes_value(true), + .required_unless_present_any(&["blob-dir", "inline-bootstrap"]) + .conflicts_with("inline-bootstrap"), ) .arg( - Arg::with_name("inline-bootstrap") + Arg::new("inline-bootstrap") .long("inline-bootstrap") .help("append RAFS metadata to the data blob") - .takes_value(false) + .action(ArgAction::SetTrue) .required(false), ) .arg( - Arg::with_name("blob-dir") + Arg::new("blob-dir") .long("blob-dir") - .short("D") - .help("directory to store RAFS filesystem metadata and data blobs") - .takes_value(true) + .short('D') + .help("directory to store RAFS filesystem metadata and data blobs"), ) .arg( - Arg::with_name("blob") + Arg::new("blob") .long("blob") - .short("b") + .short('b') .help("path to store generated RAFS filesystem data blob") - .required_unless_one(&["backend-type", "type", "blob-dir"]) - .takes_value(true) + .required_unless_present_any(&["backend-type", "type", "blob-dir"]), ) .arg( - Arg::with_name("blob-id") + Arg::new("blob-id") .long("blob-id") - .help("specify blob id (as object id in backend/oss)") - .takes_value(true), + .help("specify blob id (as object id in backend/oss)"), ) .arg( - Arg::with_name("blob-meta") + Arg::new("blob-meta") .long("blob-meta") .help("path to store generated data blob compression information") - .conflicts_with("inline-bootstrap") - .takes_value(true) + .conflicts_with("inline-bootstrap"), ) .arg( - Arg::with_name("blob-offset") + Arg::new("blob-offset") .long("blob-offset") .help("add an offset for compressed blob (used to put the blob in the tarball)") - .default_value("0") - .takes_value(true) + .default_value("0"), ) .arg( - Arg::with_name("blob-data-size") + Arg::new("blob-data-size") .long("blob-data-size") - .help("specify blob data size of estargz conversion") - .takes_value(true), + .help("specify blob data size of estargz conversion"), ) .arg( - Arg::with_name("chunk-size") + Arg::new("chunk-size") .long("chunk-size") - .short("S") + .short('S') .help("size of data chunk, must be power of two and between 0x1000-0x1000000:") - .required(false) - .takes_value(true), + .required(false), ) .arg( - Arg::with_name("compressor") + Arg::new("compressor") .long("compressor") - .short("c") + .short('c') .help("algorithm to compress image data blob:") - .takes_value(true) .required(false) .default_value("lz4_block") - .possible_values(&["none", "lz4_block", "gzip", "zstd"]), + .value_parser(["none", "lz4_block", "gzip", "zstd"]), ) .arg( - Arg::with_name("digester") + Arg::new("digester") .long("digester") - .short("d") + .short('d') .help("algorithm to digest inodes and data chunks:") - .takes_value(true) .required(false) .default_value("blake3") - .possible_values(&["blake3", "sha256"]), + .value_parser(["blake3", "sha256"]), ) .arg( - Arg::with_name("fs-version") + Arg::new("fs-version") .long("fs-version") - .short("v") + .short('v') .help("RAFS filesystem format version number:") - .required(true) .default_value("5") - .possible_values(&["5", "6"]), + .value_parser(["5", "6"]), ) .arg( arg_chunk_dict.clone(), ) .arg( - Arg::with_name("parent-bootstrap") + Arg::new("parent-bootstrap") .long("parent-bootstrap") - .short("p") + .short('p') .help("path to parent/referenced RAFS filesystem metadata blob (optional)") - .takes_value(true) .required(false), ) .arg( - Arg::with_name("aligned-chunk") + Arg::new("aligned-chunk") .long("aligned-chunk") - .short("A") + .short('A') .help("Align uncompressed data chunk to 4K") - .takes_value(false) + .num_args(0) ) .arg( - Arg::with_name("repeatable") + Arg::new("repeatable") .long("repeatable") - .short("R") + .short('R') .help("generate reproducible RAFS filesystem") - .takes_value(false) + .num_args(0) .required(false), ) .arg( - Arg::with_name("disable-check") + Arg::new("disable-check") .long("disable-check") .help("disable validation of metadata after building") - .takes_value(false) + .num_args(0) .required(false) ) .arg( - Arg::with_name("whiteout-spec") + Arg::new("whiteout-spec") .long("whiteout-spec") - .short("W") + .short('W') .help("type of whiteout specification:") - .takes_value(true) - .required(true) .default_value("oci") - .possible_values(&["oci", "overlayfs", "none"]) + .value_parser(["oci", "overlayfs", "none"]) ) .arg( arg_prefetch_policy.clone(), @@ -325,30 +307,27 @@ fn prepare_cmd_args(bti_string: String) -> ArgMatches<'static> { arg_output_json.clone(), ) .arg( - Arg::with_name("backend-type") + Arg::new("backend-type") .long("backend-type") .help("[deprecated!] Blob storage backend type, only support localfs for compatibility. Try use --blob instead.") - .takes_value(true) .requires("backend-config") - .possible_values(&["localfs"]), + .value_parser(["localfs"]), ) .arg( - Arg::with_name("backend-config") + Arg::new("backend-config") .long("backend-config") - .help("[deprecated!] Blob storage backend config - JSON string, only support localfs for compatibility") - .takes_value(true) + .help("[deprecated!] Blob storage backend config - JSON string, only support localfs for compatibility"), ) ) .subcommand( - SubCommand::with_name("merge") + App::new("merge") .about("Merge multiple bootstraps into a overlaid bootstrap") .arg( - Arg::with_name("bootstrap") + Arg::new("bootstrap") .long("bootstrap") - .short("B") + .short('B') .help("output path of nydus overlaid bootstrap") - .required(true) - .takes_value(true), + .required(true), ) .arg( arg_chunk_dict, @@ -360,27 +339,26 @@ fn prepare_cmd_args(bti_string: String) -> ArgMatches<'static> { arg_output_json.clone(), ) .arg( - Arg::with_name("SOURCE") + Arg::new("SOURCE") .help("bootstrap paths (allow one or more)") .required(true) - .multiple(true), + .num_args(1..), ) ) .subcommand( - SubCommand::with_name("check") + App::new("check") .about("Validate RAFS filesystem metadata") .arg( - Arg::with_name("bootstrap") + Arg::new("bootstrap") .long("bootstrap") - .short("B") + .short('B') .help("path to RAFS metadata blob (required)") - .required(true) - .takes_value(true), + .required(true), ) .arg( - Arg::with_name("verbose") + Arg::new("verbose") .long("verbose") - .short("v") + .short('V') .help("verbose output") .required(false), ) @@ -389,173 +367,168 @@ fn prepare_cmd_args(bti_string: String) -> ArgMatches<'static> { ) ) .subcommand( - SubCommand::with_name("inspect") + App::new("inspect") .about("Inspects nydus image's filesystem metadata") .arg( - Arg::with_name("bootstrap") + Arg::new("bootstrap") .long("bootstrap") - .short("B") + .short('B') .help("path to nydus image's metadata blob (required)") - .required(true) - .takes_value(true), + .required(true), ) .arg( - Arg::with_name("request") + Arg::new("request") .long("request") - .short("R") + .short('R') .help("inspect nydus image's filesystem metadata in request mode") - .required(false) - .takes_value(true), + .required(false), ) ) .subcommand( - SubCommand::with_name("stat") + App::new("stat") .about("Generate statistics information from a group of RAFS bootstraps") .arg( - Arg::with_name("bootstrap") + Arg::new("bootstrap") .long("bootstrap") - .short("B") + .short('B') .help("generate statistics information from the RAFS bootstrap") - .required(false) - .takes_value(true), + .required(false), ) .arg( - Arg::with_name("blob-dir") + Arg::new("blob-dir") .long("blob-dir") - .short("D") + .short('D') .help("generate statistics information from all RAFS bootstraps in the directory") - .required(false) - .takes_value(true) + .required(false), ) .arg( - Arg::with_name("target") + Arg::new("target") .long("target") - .short("T") + .short('T') .help("generate statistics information for the target RAFS bootstrap after deduplicating data chunks available in other bootstraps") - .required(false) - .takes_value(true), + .required(false), ) .arg( arg_output_json.clone(), ) ) .subcommand( - SubCommand::with_name("compact") + App::new("compact") .about("(experimental)Compact specific nydus image, remove unused chunks in blobs, merge small blobs") .arg( - Arg::with_name("bootstrap") + Arg::new("bootstrap") .long("bootstrap") - .short("B") + .short('B') .help("bootstrap to compact") - .required(true) - .takes_value(true), + .required(true), ) .arg( - Arg::with_name("config") + Arg::new("config") .long("config") - .short("C") + .short('C') .help("config to compactor") - .required(true) - .takes_value(true), + .required(true), ) .arg( - Arg::with_name("backend-type") + Arg::new("backend-type") .long("backend-type") .help("type of backend") - .required(true) - .takes_value(true), + .required(true), ) .arg( - Arg::with_name("backend-config-file") + Arg::new("backend-config-file") .long("backend-config-file") .help("config file of backend") - .required(true) - .takes_value(true), + .required(true), ) .arg( - Arg::with_name("chunk-dict") + Arg::new("chunk-dict") .long("chunk-dict") - .short("M") - .help("Specify a chunk dictionary for chunk deduplication") - .takes_value(true), + .short('M') + .help("Specify a chunk dictionary for chunk deduplication"), ) .arg( - Arg::with_name("output-bootstrap") + Arg::new("output-bootstrap") .long("output-bootstrap") - .short("O") - .help("bootstrap to output, default is source bootstrap add suffix .compact") - .takes_value(true), + .short('O') + .help("bootstrap to output, default is source bootstrap add suffix .compact"), ) .arg( arg_output_json, ) ) .subcommand( - SubCommand::with_name("unpack") + App::new("unpack") .about("Unpack a RAFS filesystem to a tar file") .arg( - Arg::with_name("bootstrap") + Arg::new("bootstrap") .long("bootstrap") - .short("B") + .short('B') .help("path to RAFS bootstrap file") .required(true) - .takes_value(true)) + ) .arg( - Arg::with_name("blob") + Arg::new("blob") .long("blob") - .short("b") + .short('b') .help("path to RAFS data blob file") - .required(false) - .takes_value(true) + .required(false), ) .arg( - Arg::with_name("output") + Arg::new("output") .long("output") .help("path for output tar file") - .required(true) - .takes_value(true) + .required(true), ) ) .arg( - Arg::with_name("log-file") + Arg::new("log-file") .long("log-file") - .short("o") + .short('o') .help("specify log file") - .takes_value(true) .required(false) .global(true), ) .arg( - Arg::with_name("log-level") + Arg::new("log-level") .long("log-level") - .short("l") + .short('l') .help("specify log level:") .default_value("info") - .possible_values(&["trace", "debug", "info", "warn", "error"]) - .takes_value(true) + .value_parser(["trace", "debug", "info", "warn", "error"]) .required(false) .global(true), ) - .get_matches() } fn init_log(matches: &ArgMatches) -> Result<()> { let mut log_file = None; - if let Some(file) = matches.value_of("log-file") { + if let Some(file) = matches.get_one::("log-file") { let path = PathBuf::from(file); log_file = Some(path); } // Safe to unwrap because it has a default value and possible values are defined. - let level = matches.value_of("log-level").unwrap().parse().unwrap(); + let level = matches + .get_one::("log-level") + .unwrap() + .parse() + .unwrap(); setup_logging(log_file, level, 0).context("failed to setup logging") } -fn main() -> Result<()> { - let (bti_string, build_info) = BuildTimeInfo::dump(); +lazy_static! { + static ref BTI_STRING: String = BuildTimeInfo::dump().0; + static ref BTI: BuildTimeInfo = BuildTimeInfo::dump().1; +} - let cmd = prepare_cmd_args(bti_string); +fn main() -> Result<()> { + // let (bti_string, build_info) = BuildTimeInfo::dump(); + let build_info = BTI.to_owned(); + let mut app = prepare_cmd_args(BTI_STRING.as_str()); + let usage = app.render_usage(); + let cmd = app.get_matches(); init_log(&cmd)?; @@ -577,7 +550,7 @@ fn main() -> Result<()> { } else if let Some(matches) = cmd.subcommand_matches("unpack") { Command::unpack(matches) } else { - println!("{}", cmd.usage()); + println!("{}", usage); Ok(()) } } @@ -590,12 +563,12 @@ impl Command { let blob_offset = Self::get_blob_offset(matches)?; let parent_bootstrap = Self::get_parent_bootstrap(matches)?; let prefetch = Self::get_prefetch(matches)?; - let source_path = PathBuf::from(matches.value_of("SOURCE").unwrap()); - let conversion_type: ConversionType = matches.value_of("type").unwrap().parse()?; + let source_path = PathBuf::from(matches.get_one::("SOURCE").unwrap()); + let conversion_type: ConversionType = matches.get_one::("type").unwrap().parse()?; let blob_stor = Self::get_blob_storage(matches, conversion_type)?; let blob_meta_stor = Self::get_blob_meta_storage(matches)?; - let inline_bootstrap = matches.is_present("inline-bootstrap"); - let repeatable = matches.is_present("repeatable"); + let inline_bootstrap = matches.get_flag("inline-bootstrap"); + let repeatable = matches.get_flag("repeatable"); let version = Self::get_fs_version(matches)?; let chunk_size = Self::get_chunk_size(matches, conversion_type)?; let aligned_chunk = if version.is_v6() { @@ -603,14 +576,23 @@ impl Command { true } else { // get_fs_version makes sure it's either v6 or v5. - matches.is_present("aligned-chunk") + matches.get_flag("aligned-chunk") }; let whiteout_spec: WhiteoutSpec = matches - .value_of("whiteout-spec") + .get_one::("whiteout-spec") + .map(|s| s.as_str()) + .unwrap_or_default() + .parse()?; + let mut compressor = matches + .get_one::("compressor") + .map(|s| s.as_str()) + .unwrap_or_default() + .parse()?; + let mut digester = matches + .get_one::("digester") + .map(|s| s.as_str()) .unwrap_or_default() .parse()?; - let mut compressor = matches.value_of("compressor").unwrap_or_default().parse()?; - let mut digester = matches.value_of("digester").unwrap_or_default().parse()?; let blob_data_size = Self::get_blob_size(matches, conversion_type)?; match conversion_type { @@ -694,7 +676,7 @@ impl Command { build_ctx.set_chunk_size(chunk_size); let mut blob_mgr = BlobManager::new(); - if let Some(chunk_dict_arg) = matches.value_of("chunk-dict") { + if let Some(chunk_dict_arg) = matches.get_one::("chunk-dict") { blob_mgr.set_chunk_dict(timing_tracer!( { import_chunk_dict(chunk_dict_arg) }, "import_chunk_dict" @@ -748,11 +730,11 @@ impl Command { fn merge(matches: &clap::ArgMatches, build_info: &BuildTimeInfo) -> Result<()> { let source_bootstrap_paths: Vec = matches - .values_of("SOURCE") + .get_many::("SOURCE") .map(|paths| paths.map(PathBuf::from).collect()) .unwrap(); let target_bootstrap_path = Self::get_bootstrap_storage(matches)?; - let chunk_dict_path = if let Some(arg) = matches.value_of("chunk-dict") { + let chunk_dict_path = if let Some(arg) = matches.get_one::("chunk-dict") { Some(parse_chunk_dict_arg(arg)?) } else { None @@ -772,22 +754,28 @@ impl Command { fn compact(matches: &clap::ArgMatches, build_info: &BuildTimeInfo) -> Result<()> { let bootstrap_path = PathBuf::from(Self::get_bootstrap(matches)?); - let dst_bootstrap = match matches.value_of("output-bootstrap") { + let dst_bootstrap = match matches.get_one::("output-bootstrap") { None => bootstrap_path.with_extension("bootstrap.compact"), Some(s) => PathBuf::from(s), }; - let chunk_dict = match matches.value_of("chunk-dict") { + let chunk_dict = match matches.get_one::("chunk-dict") { None => None, Some(args) => Some(import_chunk_dict(args)?), }; - let backend_type = matches.value_of("backend-type").unwrap(); - let backend_file = matches.value_of("backend-config-file").unwrap(); + let backend_type = matches + .get_one::("backend-type") + .map(|s| s.as_str()) + .unwrap(); + let backend_file = matches + .get_one::("backend-config-file") + .map(|s| s.as_str()) + .unwrap(); let backend_config = BackendConfig::from_file(backend_type, backend_file)?; let backend = BlobFactory::new_backend(backend_config, "compactor")?; - let config_file_path = matches.value_of("config").unwrap(); + let config_file_path = matches.get_one::("config").unwrap(); let file = File::open(config_file_path) .with_context(|| format!("failed to open config file {}", config_file_path))?; let config = serde_json::from_reader(file) @@ -802,16 +790,18 @@ impl Command { } fn unpack(args: &clap::ArgMatches) -> Result<()> { - let bootstrap = args.value_of("bootstrap").expect("pass in bootstrap"); + let bootstrap = args + .get_one::("bootstrap") + .expect("pass in bootstrap"); if bootstrap.is_empty() { return Err(anyhow!("invalid empty --bootstrap option")); } - let output = args.value_of("output").expect("pass in output"); + let output = args.get_one::("output").expect("pass in output"); if output.is_empty() { return Err(anyhow!("invalid empty --output option")); } - let blob = args.value_of("blob"); + let blob = args.get_one::("blob").map(|s| s.as_str()); let unpacker = OCIUnpacker::new(bootstrap, blob, output).with_context(|| "fail to create unpacker")?; @@ -821,7 +811,7 @@ impl Command { fn check(matches: &clap::ArgMatches, build_info: &BuildTimeInfo) -> Result<()> { let bootstrap_path = Self::get_bootstrap(matches)?; - let verbose = matches.is_present("verbose"); + let verbose = matches.get_flag("verbose"); let mut validator = Validator::new(bootstrap_path)?; let blobs = validator .check(verbose) @@ -847,7 +837,7 @@ impl Command { fn inspect(matches: &clap::ArgMatches) -> Result<()> { let bootstrap_path = Self::get_bootstrap(matches)?; - let cmd = matches.value_of("request"); + let cmd = matches.get_one::("request"); let mut inspector = inspect::RafsInspector::new(bootstrap_path, cmd.is_some()).map_err(|e| { error!("failed to create inspector, {:?}", e); @@ -868,13 +858,13 @@ impl Command { fn stat(matches: &clap::ArgMatches) -> Result<()> { let mut stat = stat::ImageStat::new(); let target = matches - .value_of("target") + .get_one::("target") .map(Path::new) .unwrap_or_else(|| Path::new("")); - if let Some(blob) = matches.value_of("bootstrap").map(PathBuf::from) { + if let Some(blob) = matches.get_one::("bootstrap").map(PathBuf::from) { stat.stat(&blob, true)?; - } else if let Some(d) = matches.value_of("blob-dir").map(PathBuf::from) { + } else if let Some(d) = matches.get_one::("blob-dir").map(PathBuf::from) { Self::ensure_directory(d.clone())?; stat.dedup_enabled = true; @@ -898,14 +888,14 @@ impl Command { bail!("one of `--bootstrap` and `--blob-dir` must be specified"); } - if let Some(blob) = matches.value_of("target").map(PathBuf::from) { + if let Some(blob) = matches.get_one::("target").map(PathBuf::from) { stat.target_enabled = true; stat.stat(&blob, false)?; } stat.finalize(); - if let Some(path) = matches.value_of("output-json").map(PathBuf::from) { + if let Some(path) = matches.get_one::("output-json").map(PathBuf::from) { stat.dump_json(&path)?; } else { stat.dump(); @@ -914,17 +904,17 @@ impl Command { Ok(()) } - fn get_bootstrap<'a>(matches: &'a clap::ArgMatches) -> Result<&'a Path> { - match matches.value_of("bootstrap") { + fn get_bootstrap(matches: &clap::ArgMatches) -> Result<&Path> { + match matches.get_one::("bootstrap") { None => bail!("missing parameter `bootstrap`"), Some(s) => Ok(Path::new(s)), } } fn get_bootstrap_storage(matches: &clap::ArgMatches) -> Result { - if let Some(s) = matches.value_of("bootstrap") { + if let Some(s) = matches.get_one::("bootstrap") { Ok(ArtifactStorage::SingleFile(s.into())) - } else if let Some(d) = matches.value_of("blob-dir").map(PathBuf::from) { + } else if let Some(d) = matches.get_one::("blob-dir").map(PathBuf::from) { if !d.exists() { bail!("Directory to store blobs does not exist") } @@ -950,16 +940,16 @@ impl Command { { Ok(None) } else if let Some(p) = matches - .value_of("blob") + .get_one::("blob") .map(|b| ArtifactStorage::SingleFile(b.into())) { Ok(Some(p)) - } else if let Some(d) = matches.value_of("blob-dir").map(PathBuf::from) { + } else if let Some(d) = matches.get_one::("blob-dir").map(PathBuf::from) { if !d.exists() { bail!("Directory to store blobs does not exist") } Ok(Some(ArtifactStorage::FileDir(d))) - } else if let Some(config_json) = matches.value_of("backend-config") { + } else if let Some(config_json) = matches.get_one::("backend-config") { let config: serde_json::Value = serde_json::from_str(config_json).unwrap(); warn!("Using --backend-type=localfs is DEPRECATED. Use --blob instead."); if let Some(bf) = config.get("blob_file") { @@ -981,14 +971,14 @@ impl Command { fn get_blob_meta_storage(matches: &clap::ArgMatches) -> Result> { let blob_meta_stor = matches - .value_of("blob-meta") + .get_one::("blob-meta") .map(|b| ArtifactStorage::SingleFile(b.into())); Ok(blob_meta_stor) } fn get_parent_bootstrap(matches: &clap::ArgMatches) -> Result> { let mut parent_bootstrap_path = Path::new(""); - if let Some(_parent_bootstrap_path) = matches.value_of("parent-bootstrap") { + if let Some(_parent_bootstrap_path) = matches.get_one::("parent-bootstrap") { parent_bootstrap_path = Path::new(_parent_bootstrap_path); } @@ -1012,7 +1002,7 @@ impl Command { fn get_blob_id(matches: &clap::ArgMatches) -> Result { let mut blob_id = String::new(); - if let Some(p_blob_id) = matches.value_of("blob-id") { + if let Some(p_blob_id) = matches.get_one::("blob-id") { blob_id = String::from(p_blob_id); if blob_id.len() > BLOB_ID_MAXIMUM_LENGTH { bail!("blob id is limited to length {}", BLOB_ID_MAXIMUM_LENGTH); @@ -1027,7 +1017,7 @@ impl Command { return Ok(0); } - match matches.value_of("blob-data-size") { + match matches.get_one::("blob-data-size") { None => bail!("no value specified for '--blob-data-size'"), Some(v) => { let param = v.trim_start_matches("0x").trim_start_matches("0X"); @@ -1039,7 +1029,7 @@ impl Command { } fn validate_image(matches: &clap::ArgMatches, bootstrap_path: &Path) -> Result<()> { - if !matches.is_present("disable-check") { + if !matches.get_flag("disable-check") { let mut validator = Validator::new(bootstrap_path)?; timing_tracer!( { @@ -1055,7 +1045,7 @@ impl Command { } fn get_chunk_size(matches: &clap::ArgMatches, ty: ConversionType) -> Result { - match matches.value_of("chunk-size") { + match matches.get_one::("chunk-size") { None => { if ty == ConversionType::EStargzIndexToRef { Ok(0x400000u32) @@ -1080,14 +1070,15 @@ impl Command { fn get_prefetch(matches: &clap::ArgMatches) -> Result { let prefetch_policy = matches - .value_of("prefetch-policy") + .get_one::("prefetch-policy") + .map(|s| s.as_str()) .unwrap_or_default() .parse()?; Prefetch::new(prefetch_policy) } fn get_blob_offset(matches: &clap::ArgMatches) -> Result { - match matches.value_of("blob-offset") { + match matches.get_one::("blob-offset") { None => Ok(0), Some(v) => v .parse::() @@ -1096,7 +1087,7 @@ impl Command { } fn get_fs_version(matches: &clap::ArgMatches) -> Result { - match matches.value_of("fs-version") { + match matches.get_one::("fs-version") { None => Ok(RafsVersion::V6), Some(v) => { let version: u32 = v.parse().context(format!("invalid fs-version: {}", v))?; diff --git a/src/bin/nydusctl/main.rs b/src/bin/nydusctl/main.rs index 62b35813324..97bb506ebd5 100644 --- a/src/bin/nydusctl/main.rs +++ b/src/bin/nydusctl/main.rs @@ -17,7 +17,7 @@ extern crate nydus_rafs as rafs; use std::collections::HashMap; use anyhow::Result; -use clap::{App, Arg, SubCommand}; +use clap::{Arg, Command}; mod client; mod commands; @@ -27,120 +27,115 @@ use commands::{ }; use nydus_app::BuildTimeInfo; +lazy_static! { + static ref BTI: BuildTimeInfo = BuildTimeInfo::dump().1; +} + #[tokio::main] async fn main() -> Result<()> { - let (_, build_info) = BuildTimeInfo::dump(); - let app = App::new("A client to query and configure the nydusd daemon\n") - .version(build_info.package_ver.as_str()) + // let (_, build_info) = BuildTimeInfo::dump(); + // let build_info = BTI.to_owned(); + let app = Command::new("A client to query and configure the nydusd daemon\n") + .version(BTI.package_ver.as_str()) .author(crate_authors!()) .arg( - Arg::with_name("sock") + Arg::new("sock") .help("Sets file path for the nydusd API socket") - .short("S") + .short('S') .long("sock") .required(true) - .takes_value(true) .global(false), ) .arg( - Arg::with_name("raw") + Arg::new("raw") .help("Outputs messages in plain json mode") - .short("r") + .short('r') .long("raw") - .takes_value(false) + .num_args(0) .global(true), ) - .subcommand(SubCommand::with_name("info").about("Gets information about the nydusd daemon")) + .subcommand(Command::new("info").about("Gets information about the nydusd daemon")) .subcommand( - SubCommand::with_name("set") + Command::new("set") .about("Configures parameters for the nydusd daemon") - .help( + .override_help( r#"Configurable parameters: : log-level: trace, debug, info, warn, error"#, ) .arg( - Arg::with_name("KIND") + Arg::new("KIND") .help("the parameter to configure") .required(true) - .takes_value(true) - .possible_values(&["log-level"]) + .value_parser(["log-level"]) .index(1), ) .arg( - Arg::with_name("VALUE") + Arg::new("VALUE") .help("the configuration value") .required(true) - .takes_value(true) .index(2), ), ) .subcommand( - SubCommand::with_name("metrics") + Command::new("metrics") .about("Gets runtime metrics about backend, cache and filesystems") .arg( - Arg::with_name("category") + Arg::new("category") .help("the metrics category to fetch") .required(true) - .possible_values(&["backend", "cache", "fsstats"]) - .takes_value(true) + .value_parser(["backend", "cache", "fsstats"]) .index(1), ) .arg( - Arg::with_name("interval") + Arg::new("interval") .help("interval to refresh the metrics") - .short("I") + .short('I') .long("interval") - .required(false) - .takes_value(true), + .required(false), ), ) .subcommand( - SubCommand::with_name("mount") + Command::new("mount") .about("Mounts a new filesystem instance") .arg( - Arg::with_name("source") + Arg::new("source") .help("Storage backend for the filesystem instance") - .short("s") + .short('s') .long("source") - .required(true) - .takes_value(true), + .required(true), ) .arg( - Arg::with_name("config") + Arg::new("config") .help("Configuration file for the new filesystem instance") - .short("c") + .short('c') .long("config") - .required(true) - .takes_value(true), + .required(true), ) .arg( - Arg::with_name("mountpoint") + Arg::new("mountpoint") .help("Mountpoint for the new filesystem instance") - .short("m") + .short('m') .long("mountpoint") - .required(true) - .takes_value(true), + .required(true), ) .arg( - Arg::with_name("type") + Arg::new("type") .help("Type of the new filesystem instance") - .short("t") + .short('t') .long("type") .required(true) - .takes_value(true) - .possible_values(&["rafs", "passthrough_fs"]), + .value_parser(["rafs", "passthrough_fs"]), ), ) .subcommand( - SubCommand::with_name("umount") + Command::new("umount") .about("Umounts a filesystem instance") .arg( - Arg::with_name("mountpoint") + Arg::new("mountpoint") .help("Mountpoint of the filesystem instance") - .short("m") + .short('m') .required(true) - .takes_value(true) .index(1), ), ); @@ -148,8 +143,8 @@ async fn main() -> Result<()> { let cmd = app.get_matches(); // Safe to unwrap because it is required by Clap - let sock = cmd.value_of("sock").unwrap(); - let raw = cmd.is_present("raw"); + let sock = cmd.get_one::("sock").map(|s| s.as_str()).unwrap(); + let raw = cmd.get_flag("raw"); let client = client::NydusdClient::new(sock); if let Some(_matches) = cmd.subcommand_matches("info") { @@ -157,8 +152,8 @@ async fn main() -> Result<()> { cmd.execute(raw, &client, None).await?; } else if let Some(matches) = cmd.subcommand_matches("set") { // Safe to unwrap since the below two arguments are required by clap. - let kind = matches.value_of("KIND").unwrap().to_string(); - let value = matches.value_of("VALUE").unwrap().to_string(); + let kind = matches.get_one::("KIND").unwrap().to_owned(); + let value = matches.get_one::("VALUE").unwrap().to_owned(); let mut items = HashMap::new(); items.insert(kind, value); @@ -166,10 +161,13 @@ async fn main() -> Result<()> { cmd.execute(raw, &client, Some(items)).await?; } else if let Some(matches) = cmd.subcommand_matches("metrics") { // Safe to unwrap as it is required by clap - let category = matches.value_of("category").unwrap(); + let category = matches + .get_one::("category") + .map(|s| s.as_str()) + .unwrap(); let mut context = HashMap::new(); matches - .value_of("interval") + .get_one::("interval") .map(|i| context.insert("interval".to_string(), i.to_string())); match category { @@ -192,19 +190,19 @@ async fn main() -> Result<()> { let mut context = HashMap::new(); context.insert( "source".to_string(), - matches.value_of("source").unwrap().to_string(), + matches.get_one::("source").unwrap().to_string(), ); context.insert( "mountpoint".to_string(), - matches.value_of("mountpoint").unwrap().to_string(), + matches.get_one::("mountpoint").unwrap().to_string(), ); context.insert( "config".to_string(), - matches.value_of("config").unwrap().to_string(), + matches.get_one::("config").unwrap().to_string(), ); context.insert( "type".to_string(), - matches.value_of("type").unwrap().to_string(), + matches.get_one::("type").unwrap().to_string(), ); let cmd = CommandMount {}; @@ -214,7 +212,7 @@ async fn main() -> Result<()> { let mut context = HashMap::new(); context.insert( "mountpoint".to_string(), - matches.value_of("mountpoint").unwrap().to_string(), + matches.get_one::("mountpoint").unwrap().to_string(), ); let cmd = CommandUmount {}; diff --git a/src/bin/nydusd/main.rs b/src/bin/nydusd/main.rs index 6ddd60b3dae..d2fa55b92eb 100644 --- a/src/bin/nydusd/main.rs +++ b/src/bin/nydusd/main.rs @@ -5,7 +5,7 @@ // SPDX-License-Identifier: (Apache-2.0 AND BSD-3-Clause) #![deny(warnings)] #![allow(dead_code)] -extern crate clap; +// extern crate clap; #[macro_use] extern crate log; #[macro_use] @@ -23,7 +23,8 @@ use std::io::{Error, ErrorKind, Result}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use clap::{App, Arg, ArgMatches, SubCommand, Values}; +use clap::parser::ValuesRef; +use clap::{Arg, ArgAction, ArgMatches, Command}; use mio::{Events, Poll, Token, Waker}; use nix::sys::signal; use rlimit::Resource; @@ -197,167 +198,155 @@ extern "C" fn sig_exit(_sig: std::os::raw::c_int) { const SHARED_DIR_HELP_MESSAGE: &str = "Local directory to share via passthroughfs FUSE driver"; -pub fn thread_validator(v: String) -> std::result::Result<(), String> { - ensure_threads(v).map(|_| ()) +pub fn thread_validator(v: &str) -> std::result::Result { + ensure_threads(v).map(|s| s.to_string()) } -fn append_fs_options(app: App<'static, 'static>) -> App<'static, 'static> { +fn append_fs_options(app: Command) -> Command { app.arg( - Arg::with_name("bootstrap") + Arg::new("bootstrap") .long("bootstrap") - .short("B") + .short('B') .help("RAFS filesystem metadata file, which also enables `rafs` mode") - .takes_value(true) .conflicts_with("shared-dir"), ) .arg( - Arg::with_name("localfs-dir") + Arg::new("localfs-dir") .long("localfs-dir") - .short("D") + .short('D') .help( "Enable 'localfs' storage backend by using the directory for blob and cache files", ) - .takes_value(true) .conflicts_with("config"), ) .arg( - Arg::with_name("shared-dir") + Arg::new("shared-dir") .long("shared-dir") - .short("s") + .short('s') .help(SHARED_DIR_HELP_MESSAGE) - .takes_value(true) .conflicts_with("bootstrap"), ) .arg( - Arg::with_name("prefetch-files") + Arg::new("prefetch-files") .long("prefetch-files") - .short("P") + .short('P') .help("List of files/directories to prefetch") - .takes_value(true) .required(false) .requires("bootstrap") - .multiple(true), + .num_args(1..), ) .arg( - Arg::with_name("virtual-mountpoint") + Arg::new("virtual-mountpoint") .long("virtual-mountpoint") - .short("m") + .short('m') .help("Path within the FUSE/virtiofs device to mount the filesystem") - .takes_value(true) .default_value("/") .required(false), ) } -fn append_fuse_options(app: App<'static, 'static>) -> App<'static, 'static> { +fn append_fuse_options(app: Command) -> Command { app.arg( - Arg::with_name("mountpoint") + Arg::new("mountpoint") .long("mountpoint") - .short("M") + .short('M') .help("Path to mount the FUSE filesystem, target for `mount.fuse`") - .takes_value(true) .required(false), ) .arg( - Arg::with_name("failover-policy") + Arg::new("failover-policy") .long("failover-policy") .default_value("resend") .help("FUSE server failover policy") - .possible_values(&["resend", "flush"]) - .takes_value(true) + .value_parser(["resend", "flush"]) .required(false), ) .arg( - Arg::with_name("threads") + Arg::new("threads") .long("thread-num") - .short("T") + .short('T') .default_value("4") .help("Number of worker threads to serve IO requests") - .takes_value(true) - .required(false) - .validator(thread_validator), + .value_parser(thread_validator) + .required(false), ) .arg( - Arg::with_name("writable") + Arg::new("writable") .long("writable") - .short("W") - .help("Mounts FUSE filesystem in rw mode") - .takes_value(false), + .short('W') + .action(ArgAction::SetTrue) + .help("Mounts FUSE filesystem in rw mode"), ) } -fn append_fuse_subcmd_options(app: App<'static, 'static>) -> App<'static, 'static> { - let subcmd = SubCommand::with_name("fuse").about("Run as a dedicated FUSE server"); +fn append_fuse_subcmd_options(cmd: Command) -> Command { + let subcmd = Command::new("fuse").about("Run as a dedicated FUSE server"); let subcmd = append_fuse_options(subcmd); let subcmd = append_fs_options(subcmd); - app.subcommand(subcmd) + cmd.subcommand(subcmd) } #[cfg(feature = "virtiofs")] -fn append_virtiofs_options(app: App<'static, 'static>) -> App<'static, 'static> { - app.arg( - Arg::with_name("hybrid-mode") +fn append_virtiofs_options(cmd: Command) -> Command { + cmd.arg( + Arg::new("hybrid-mode") .long("hybrid-mode") - .short("H") + .short('H') .help("Enables both `rafs` and `passthroughfs` modes") - .required(false) - .takes_value(false), + .action(ArgAction::SetFalse) + .required(false), ) .arg( - Arg::with_name("sock") + Arg::new("sock") .long("sock") - .short("v") + .short('v') .help("Vhost-user API socket") - .takes_value(true) .required(false), ) } #[cfg(feature = "virtiofs")] -fn append_virtiofs_subcmd_options(app: App<'static, 'static>) -> App<'static, 'static> { - let subcmd = SubCommand::with_name("virtiofs").about("Run as a dedicated virtiofs server"); +fn append_virtiofs_subcmd_options(cmd: Command) -> Command { + let subcmd = Command::new("virtiofs").about("Run as a dedicated virtiofs server"); let subcmd = append_virtiofs_options(subcmd); let subcmd = append_fs_options(subcmd); - app.subcommand(subcmd) + cmd.subcommand(subcmd) } -fn append_fscache_options(app: App<'static, 'static>) -> App<'static, 'static> { +fn append_fscache_options(app: Command) -> Command { app.arg( - Arg::with_name("fscache-tag") + Arg::new("fscache-tag") .long("fscache-tag") .help("Tag to identify the fscache daemon instance") - .takes_value(true) .requires("fscache"), ) .arg( - Arg::with_name("fscache-threads") + Arg::new("fscache-threads") .long("fscache-threads") .default_value("4") .help("Number of working threads to serve fscache requests") - .takes_value(true) .required(false) - .validator(thread_validator), + .value_parser(thread_validator), ) } -fn append_services_subcmd_options(app: App<'static, 'static>) -> App<'static, 'static> { - let subcmd = SubCommand::with_name("singleton") +fn append_services_subcmd_options(cmd: Command) -> Command { + let subcmd = Command::new("singleton") .about( "Run as a global daemon instance to service multiple blobcache/fscache/fuse services.", ) .arg( - Arg::with_name("fscache") + Arg::new("fscache") .long("fscache") - .short("F") - .help("Working directory for Linux fscache driver to store cached files") - .takes_value(true), + .short('F') + .help("Working directory for Linux fscache driver to store cached files"), ); let subcmd = append_fscache_options(subcmd); // TODO: enable support of fuse service /* let subcmd = subcmd.arg( - Arg::with_name("fuse") + Arg::new("fuse") .long("fuse") .short("f") .help("Run as a shared FUSE server"), @@ -366,95 +355,87 @@ fn append_services_subcmd_options(app: App<'static, 'static>) -> App<'static, 's let subcmd = append_fs_options(subcmd); */ - app.subcommand(subcmd) + cmd.subcommand(subcmd) } -fn prepare_commandline_options() -> App<'static, 'static> { - let cmdline = App::new("nydusd") +fn prepare_commandline_options() -> Command { + let cmdline = Command::new("nydusd") .about("Nydus BlobCache/FsCache/Image Service") .arg( - Arg::with_name("apisock") + Arg::new("apisock") .long("apisock") - .short("A") + .short('A') .help("Administration API socket") - .takes_value(true) .required(false) .global(true), ) .arg( - Arg::with_name("config") + Arg::new("config") .long("config") - .short("C") + .short('C') .help("Configuration file") .required(false) - .global(true) - .takes_value(true), + .global(true), ) .arg( - Arg::with_name("id") + Arg::new("id") .long("id") - .short("I") + .short('I') .help("Service instance identifier") - .takes_value(true) .required(false) .requires("supervisor") .global(true), ) .arg( - Arg::with_name("log-level") + Arg::new("log-level") .long("log-level") - .short("l") + .short('l') .help("Log level:") .default_value("info") - .possible_values(&["trace", "debug", "info", "warn", "error"]) - .takes_value(true) + .value_parser(["trace", "debug", "info", "warn", "error"]) .required(false) .global(true), ) .arg( - Arg::with_name("log-file") + Arg::new("log-file") .long("log-file") - .short("L") + .short('L') .help("Log messages to the file. Default extension \".log\" will be used if no extension specified.") - .takes_value(true) .required(false) .global(true), ) .arg( - Arg::with_name("log-rotation-size") + Arg::new("log-rotation-size") .long("log-rotation-size") .help("Specify log rotation size(MB), 0 to disable") .default_value("0") - .takes_value(true) .required(false) .global(true), ) .arg( - Arg::with_name("rlimit-nofile") + Arg::new("rlimit-nofile") .long("rlimit-nofile") - .short("R") + .short('R') .default_value("1000000") .help("Set rlimit for maximum file descriptor number (0 leaves it unchanged)") - .takes_value(true) .required(false) .global(true), ) .arg( - Arg::with_name("supervisor") + Arg::new("supervisor") .long("supervisor") - .short("S") + .short('S') .help("Supervisor API socket") - .takes_value(true) .required(false) .requires("id") .global(true), ) .arg( - Arg::with_name("upgrade") + Arg::new("upgrade") .long("upgrade") - .short("U") + .short('U') .help("Starts daemon in upgrade mode") - .takes_value(false) + .action(ArgAction::SetTrue) .required(false) .global(true), ); @@ -499,12 +480,16 @@ fn get_max_rlimit_nofile() -> Result { /// Handle command line option to tune rlimit for maximum file descriptor number. fn handle_rlimit_nofile_option(args: &ArgMatches, option_name: &str) -> Result<()> { // `rlimit-nofile` has a default value, so safe to unwrap(). - let rlimit_nofile: u64 = args.value_of(option_name).unwrap().parse().map_err(|_e| { - Error::new( - ErrorKind::InvalidInput, - "invalid value for option `rlimit-nofile`", - ) - })?; + let rlimit_nofile: u64 = args + .get_one::(option_name) + .unwrap() + .parse() + .map_err(|_e| { + Error::new( + ErrorKind::InvalidInput, + "invalid value for option `rlimit-nofile`", + ) + })?; if rlimit_nofile != 0 { // Ensures there are fds available for other processes so we don't cause resource exhaustion. @@ -539,8 +524,8 @@ fn handle_rlimit_nofile_option(args: &ArgMatches, option_name: &str) -> Result<( } pub struct SubCmdArgs<'a> { - args: &'a ArgMatches<'a>, - subargs: &'a ArgMatches<'a>, + args: &'a ArgMatches, + subargs: &'a ArgMatches, } impl<'a> SubCmdArgs<'a> { @@ -548,24 +533,24 @@ impl<'a> SubCmdArgs<'a> { SubCmdArgs { args, subargs } } - pub fn value_of(&self, key: &str) -> Option<&str> { - if let Some(v) = self.subargs.value_of(key) { + pub fn value_of(&self, key: &str) -> Option<&String> { + if let Some(v) = self.subargs.get_one::(key) { Some(v) } else { - self.args.value_of(key) + self.args.get_one::(key) } } - pub fn values_of(&self, key: &str) -> Option { - if let Some(v) = self.subargs.values_of(key) { + pub fn values_of(&self, key: &str) -> Option> { + if let Some(v) = self.subargs.get_many::<&str>(key) { Some(v) } else { - self.args.values_of(key) + self.args.get_many::<&str>(key) } } pub fn is_present(&self, key: &str) -> bool { - self.subargs.is_present(key) || self.args.is_present(key) + self.subargs.get_flag(key) || self.args.get_flag(key) } } @@ -660,7 +645,7 @@ fn process_fs_service( }; // Enable all options required by passthroughfs - if args.is_present("hybrid-mode") { + if !is_fuse && args.is_present("hybrid-mode") { opts.no_open = false; opts.no_opendir = false; opts.killpriv_v2 = true; @@ -681,7 +666,7 @@ fn process_fs_service( let p = args .value_of("failover-policy") - .unwrap_or("flush") + .unwrap_or(&"flush".to_string()) .try_into() .map_err(|e| { error!("Invalid failover policy"); @@ -750,16 +735,25 @@ fn process_singleton_arguments( Ok(()) } +lazy_static! { + static ref BTI_STRING: String = BuildTimeInfo::dump().0; + static ref BTI: BuildTimeInfo = BuildTimeInfo::dump().1; +} + fn main() -> Result<()> { - let (bti_string, bti) = BuildTimeInfo::dump(); - let cmd_options = prepare_commandline_options().version(bti_string.as_str()); - let args = cmd_options.clone().get_matches(); - let logging_file = args.value_of("log-file").map(|l| l.into()); + let bti = BTI.to_owned(); + let cmd_options = prepare_commandline_options().version(BTI_STRING.as_str()); + let args = cmd_options.get_matches(); + let logging_file = args.get_one::("log-file").map(|l| l.into()); // Safe to unwrap because it has default value and possible values are defined - let level = args.value_of("log-level").unwrap().parse().unwrap(); - let apisock = args.value_of("apisock"); + let level = args + .get_one::("log-level") + .unwrap() + .parse() + .unwrap(); + let apisock = args.get_one::("apisock").map(|s| s.as_str()); let rotation_size = args - .value_of("log-rotation-size") + .get_one::("log-rotation-size") .unwrap() .parse::() .map_err(|e| einval!(format!("Invalid log rotation size: {}", e)))?; diff --git a/src/bin/nydusd/service_controller.rs b/src/bin/nydusd/service_controller.rs index 17fe706a570..e232c26153f 100644 --- a/src/bin/nydusd/service_controller.rs +++ b/src/bin/nydusd/service_controller.rs @@ -120,7 +120,7 @@ impl ServiceController { return Err(einval!("--fscache option contains invalid characters")); } }; - let tag = subargs.value_of("fscache-tag"); + let tag = subargs.value_of("fscache-tag").map(|s| s.as_str()); let threads = if let Some(threads_value) = subargs.value_of("fscache-threads") { ensure_threads(threads_value).map_err(|err| einval!(err))? diff --git a/src/bin/nydusd/upgrade.rs b/src/bin/nydusd/upgrade.rs index c248bf7f316..982058d0118 100644 --- a/src/bin/nydusd/upgrade.rs +++ b/src/bin/nydusd/upgrade.rs @@ -33,6 +33,18 @@ impl TryFrom<&str> for FailoverPolicy { } } +impl TryFrom<&String> for FailoverPolicy { + type Error = std::io::Error; + + fn try_from(p: &String) -> std::result::Result { + match p.as_ref() { + "flush" => Ok(FailoverPolicy::Flush), + "resend" => Ok(FailoverPolicy::Resend), + x => Err(einval!(x)), + } + } +} + pub fn add_mounts_state( _mgr: &mut UpgradeManager, _cmd: FsBackendMountCmd,