Convert apply-local arguments to type-safe clap derive

This commit is contained in:
i1i1 2023-08-09 23:24:11 +03:00 committed by Zhaofeng Li
parent 1e38582451
commit b80b57cb48
2 changed files with 69 additions and 72 deletions

View file

@ -1,10 +1,7 @@
use regex::Regex; use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::str::FromStr;
use clap::{ use clap::{ArgMatches, Args, Command as ClapCommand, FromArgMatches};
builder::PossibleValuesParser, Arg, ArgMatches, Command as ClapCommand, FromArgMatches,
};
use tokio::fs; use tokio::fs;
use crate::error::ColmenaError; use crate::error::ColmenaError;
@ -14,57 +11,66 @@ use crate::nix::hive::HiveArgs;
use crate::nix::{host::Local as LocalHost, NodeName}; use crate::nix::{host::Local as LocalHost, NodeName};
use crate::progress::SimpleProgressOutput; use crate::progress::SimpleProgressOutput;
pub fn subcommand() -> ClapCommand { #[derive(Debug, Args)]
ClapCommand::new("apply-local") #[command(
.about("Apply configurations on the local machine") name = "apply-local",
.arg(Arg::new("goal") about = "Apply configurations on the local machine"
.help("Deployment goal") )]
.long_help("Same as the targets for switch-to-configuration.\n\"push\" is noop in apply-local.") pub struct Opts {
.default_value("switch") #[arg(
.index(1) help = "Deployment goal",
.value_parser(PossibleValuesParser::new([ value_name = "GOAL",
"push", default_value_t,
"switch", long_help = "Same as the targets for switch-to-configuration.\n\"push\" is noop in apply-local."
"boot", )]
"test", goal: Goal,
"dry-activate", #[arg(long, help = "Attempt to escalate privileges if not run as root")]
"keys", sudo: bool,
]))) #[arg(
.arg(Arg::new("sudo") short,
.long("sudo") long,
.help("Attempt to escalate privileges if not run as root") help = "Be verbose",
.num_args(0)) long_help = "Deactivates the progress spinner and prints every line of output."
.arg(Arg::new("verbose") )]
.short('v') verbose: bool,
.long("verbose") #[arg(
.help("Be verbose") long,
.long_help("Deactivates the progress spinner and prints every line of output.") help = "Do not deploy keys",
.num_args(0)) long_help = r#"Do not deploy secret keys set in `deployment.keys`.
.arg(Arg::new("no-keys")
.long("no-keys")
.help("Do not deploy keys")
.long_help(r#"Do not deploy secret keys set in `deployment.keys`.
By default, Colmena will deploy keys set in `deployment.keys` before activating the profile on this host. By default, Colmena will deploy keys set in `deployment.keys` before activating the profile on this host.
"#) "#
.num_args(0)) )]
.arg(Arg::new("node") no_keys: bool,
.long("node") #[arg(long, help = "Override the node name to use")]
.value_name("NODE") node: Option<String>,
.help("Override the node name to use") #[arg(
.num_args(1)) long,
value_name = "COMMAND",
hide = true,
help = "Removed: Configure deployment.privilegeEscalationCommand in node configuration"
)]
sudo_command: Option<String>,
#[command(flatten)]
hive_args: HiveArgs,
}
// Removed pub fn subcommand() -> ClapCommand {
.arg(Arg::new("sudo-command") Opts::augment_args(ClapCommand::new("apply-local"))
.long("sudo-command")
.value_name("COMMAND")
.help("Removed: Configure deployment.privilegeEscalationCommand in node configuration")
.hide(true)
.num_args(1))
} }
pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> { pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(), ColmenaError> {
if local_args.contains_id("sudo-command") { let Opts {
goal,
sudo,
verbose,
no_keys,
node,
sudo_command,
hive_args,
} = Opts::from_arg_matches(local_args).expect("Failed to parse `apply-local` options.");
if sudo_command.is_some() {
log::error!("--sudo-command has been removed. Please configure it in deployment.privilegeEscalationCommand in the node configuration."); log::error!("--sudo-command has been removed. Please configure it in deployment.privilegeEscalationCommand in the node configuration.");
quit::with_code(1); quit::with_code(1);
} }
@ -81,35 +87,26 @@ pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(
quit::with_code(5); quit::with_code(5);
} }
let escalate_privileges = local_args.get_flag("sudo"); let verbose = verbose || sudo; // cannot use spinners with interactive sudo
let verbose = local_args.get_flag("verbose") || escalate_privileges; // cannot use spinners with interactive sudo
{ {
let euid: u32 = unsafe { libc::geteuid() }; let euid: u32 = unsafe { libc::geteuid() };
if euid != 0 && !escalate_privileges { if euid != 0 && !sudo {
log::warn!("Colmena was not started by root. This is probably not going to work."); log::warn!("Colmena was not started by root. This is probably not going to work.");
log::warn!("Hint: Add the --sudo flag."); log::warn!("Hint: Add the --sudo flag.");
} }
} }
let hive = HiveArgs::from_arg_matches(local_args) let hive = hive_args
.unwrap()
.into_hive() .into_hive()
.await .await
.unwrap(); .expect("Failed to get hive from arguments");
let hostname = { let hostname = NodeName::new(node.unwrap_or_else(|| {
let s = if local_args.contains_id("node") {
local_args.get_one::<String>("node").unwrap().to_owned()
} else {
hostname::get() hostname::get()
.expect("Could not get hostname") .expect("Could not get hostname")
.to_string_lossy() .to_string_lossy()
.into_owned() .into_owned()
}; }))?;
NodeName::new(s)?
};
let goal = Goal::from_str(local_args.get_one::<String>("goal").unwrap()).unwrap();
let target = { let target = {
if let Some(info) = hive.deployment_info_single(&hostname).await.unwrap() { if let Some(info) = hive.deployment_info_single(&hostname).await.unwrap() {
@ -123,7 +120,7 @@ pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(
quit::with_code(2); quit::with_code(2);
} }
let mut host = LocalHost::new(nix_options); let mut host = LocalHost::new(nix_options);
if escalate_privileges { if sudo {
let command = info.privilege_escalation_command().to_owned(); let command = info.privilege_escalation_command().to_owned();
host.set_privilege_escalation_command(Some(command)); host.set_privilege_escalation_command(Some(command));
} }
@ -148,13 +145,13 @@ pub async fn run(_global_args: &ArgMatches, local_args: &ArgMatches) -> Result<(
let options = { let options = {
let mut options = Options::default(); let mut options = Options::default();
options.set_upload_keys(!local_args.get_flag("no-keys")); options.set_upload_keys(!no_keys);
options options
}; };
deployment.set_options(options); deployment.set_options(options);
let (deployment, output) = tokio::join!(deployment.execute(), output.run_until_completion(),); let (deployment, output) = tokio::join!(deployment.execute(), output.run_until_completion());
deployment?; deployment?;
output?; output?;

View file

@ -3,7 +3,7 @@
use std::str::FromStr; use std::str::FromStr;
/// The goal of a deployment. /// The goal of a deployment.
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Default, PartialEq, Eq, clap::ValueEnum)]
pub enum Goal { pub enum Goal {
/// Build the configurations only. /// Build the configurations only.
Build, Build,