forked from DGNum/colmena
Traverse up to find hive.nix by default, and other CLI ergonomics fixes
This commit is contained in:
parent
9c8e3034f7
commit
60d6475897
11 changed files with 212 additions and 95 deletions
50
Cargo.lock
generated
50
Cargo.lock
generated
|
@ -1,5 +1,14 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
@ -83,6 +92,7 @@ dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"clap",
|
"clap",
|
||||||
"console",
|
"console",
|
||||||
|
"env_logger",
|
||||||
"futures",
|
"futures",
|
||||||
"glob",
|
"glob",
|
||||||
"hostname",
|
"hostname",
|
||||||
|
@ -125,6 +135,19 @@ version = "0.3.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.8"
|
version = "0.3.8"
|
||||||
|
@ -257,6 +280,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indicatif"
|
name = "indicatif"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
|
@ -541,7 +570,10 @@ version = "1.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
|
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
|
"thread_local",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -687,6 +719,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "terminal_size"
|
name = "terminal_size"
|
||||||
version = "0.1.15"
|
version = "0.1.15"
|
||||||
|
@ -706,6 +747,15 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
|
|
@ -10,6 +10,7 @@ edition = "2018"
|
||||||
async-trait = "0.1.42"
|
async-trait = "0.1.42"
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
console = "0.13.0"
|
console = "0.13.0"
|
||||||
|
env_logger = "0.8.2"
|
||||||
futures = "0.3.8"
|
futures = "0.3.8"
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
hostname = "0.3.1"
|
hostname = "0.3.1"
|
||||||
|
|
|
@ -10,5 +10,5 @@ in rustPlatform.buildRustPackage {
|
||||||
version = "0.1.0";
|
version = "0.1.0";
|
||||||
|
|
||||||
src = ./.;
|
src = ./.;
|
||||||
cargoSha256 = "0gwjbzvx6hlbjb8892rc2p9rj5l432y13aq1nxr2h71rgqppxflg";
|
cargoSha256 = "1ai046vbvydyqhwiy8qz0d28dch5jpxg3rzk7nrh2sdwcvxirmvm";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use clap::{Arg, App, SubCommand, ArgMatches};
|
use clap::{Arg, App, SubCommand, ArgMatches};
|
||||||
|
|
||||||
use crate::nix::{Hive, DeploymentTask, DeploymentGoal};
|
use crate::nix::{DeploymentTask, DeploymentGoal};
|
||||||
use crate::deployment::deploy;
|
use crate::deployment::deploy;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ pub fn subcommand() -> App<'static, 'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||||
let mut hive = Hive::from_args(local_args).unwrap();
|
let mut hive = util::hive_from_args(local_args).unwrap();
|
||||||
|
|
||||||
println!("Enumerating nodes...");
|
log::info!("Enumerating nodes...");
|
||||||
let all_nodes = hive.deployment_info().await.unwrap();
|
let all_nodes = hive.deployment_info().await.unwrap();
|
||||||
|
|
||||||
let selected_nodes = match local_args.value_of("on") {
|
let selected_nodes = match local_args.value_of("on") {
|
||||||
|
@ -50,14 +50,14 @@ pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if selected_nodes.len() == 0 {
|
if selected_nodes.len() == 0 {
|
||||||
println!("No hosts matched. Exiting...");
|
log::warn!("No hosts matched. Exiting...");
|
||||||
quit::with_code(2);
|
quit::with_code(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if selected_nodes.len() == all_nodes.len() {
|
if selected_nodes.len() == all_nodes.len() {
|
||||||
println!("Building all node configurations...");
|
log::info!("Building all node configurations...");
|
||||||
} else {
|
} else {
|
||||||
println!("Selected {} out of {} hosts. Building node configurations...", selected_nodes.len(), all_nodes.len());
|
log::info!("Selected {} out of {} hosts. Building node configurations...", selected_nodes.len(), all_nodes.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some ugly argument mangling :/
|
// Some ugly argument mangling :/
|
||||||
|
@ -88,9 +88,9 @@ pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if skip_list.len() != 0 {
|
if skip_list.len() != 0 {
|
||||||
println!("Applying configurations ({} skipped)...", skip_list.len());
|
log::info!("Applying configurations ({} skipped)...", skip_list.len());
|
||||||
} else {
|
} else {
|
||||||
println!("Applying configurations...");
|
log::info!("Applying configurations...");
|
||||||
}
|
}
|
||||||
|
|
||||||
deploy(task_list, max_parallelism, !verbose).await;
|
deploy(task_list, max_parallelism, !verbose).await;
|
||||||
|
|
|
@ -4,12 +4,12 @@ use clap::{Arg, App, SubCommand, ArgMatches};
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::nix::{Hive, DeploymentTask, DeploymentGoal, Host};
|
use crate::nix::{DeploymentTask, DeploymentGoal, Host};
|
||||||
use crate::nix::host;
|
use crate::nix::host;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
pub fn subcommand() -> App<'static, 'static> {
|
pub fn subcommand() -> App<'static, 'static> {
|
||||||
let command = SubCommand::with_name("apply-local")
|
SubCommand::with_name("apply-local")
|
||||||
.about("Apply configurations on the local machine")
|
.about("Apply configurations on the local machine")
|
||||||
.arg(Arg::with_name("goal")
|
.arg(Arg::with_name("goal")
|
||||||
.help("Deployment goal")
|
.help("Deployment goal")
|
||||||
|
@ -17,12 +17,6 @@ pub fn subcommand() -> App<'static, 'static> {
|
||||||
.default_value("switch")
|
.default_value("switch")
|
||||||
.index(1)
|
.index(1)
|
||||||
.possible_values(&["push", "switch", "boot", "test", "dry-activate"]))
|
.possible_values(&["push", "switch", "boot", "test", "dry-activate"]))
|
||||||
.arg(Arg::with_name("config")
|
|
||||||
.short("f")
|
|
||||||
.long("config")
|
|
||||||
.help("Path to a Hive expression")
|
|
||||||
.default_value("hive.nix")
|
|
||||||
.required(true))
|
|
||||||
.arg(Arg::with_name("sudo")
|
.arg(Arg::with_name("sudo")
|
||||||
.long("sudo")
|
.long("sudo")
|
||||||
.help("Attempt to escalate privileges if not run as root")
|
.help("Attempt to escalate privileges if not run as root")
|
||||||
|
@ -30,20 +24,18 @@ pub fn subcommand() -> App<'static, 'static> {
|
||||||
.arg(Arg::with_name("we-are-launched-by-sudo")
|
.arg(Arg::with_name("we-are-launched-by-sudo")
|
||||||
.long("we-are-launched-by-sudo")
|
.long("we-are-launched-by-sudo")
|
||||||
.hidden(true)
|
.hidden(true)
|
||||||
.takes_value(false));
|
.takes_value(false))
|
||||||
|
|
||||||
util::register_common_args(command)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||||
// Sanity check: Are we running NixOS?
|
// Sanity check: Are we running NixOS?
|
||||||
if let Ok(os_release) = fs::read_to_string("/etc/os-release").await {
|
if let Ok(os_release) = fs::read_to_string("/etc/os-release").await {
|
||||||
if !os_release.contains("ID=nixos\n") {
|
if !os_release.contains("ID=nixos\n") {
|
||||||
eprintln!("\"apply-local\" only works on NixOS machines.");
|
log::error!("\"apply-local\" only works on NixOS machines.");
|
||||||
quit::with_code(5);
|
quit::with_code(5);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Coult not detect the OS version from /etc/os-release.");
|
log::error!("Coult not detect the OS version from /etc/os-release.");
|
||||||
quit::with_code(5);
|
quit::with_code(5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,42 +44,42 @@ pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||||
let euid: u32 = unsafe { libc::geteuid() };
|
let euid: u32 = unsafe { libc::geteuid() };
|
||||||
if euid != 0 {
|
if euid != 0 {
|
||||||
if local_args.is_present("we-are-launched-by-sudo") {
|
if local_args.is_present("we-are-launched-by-sudo") {
|
||||||
eprintln!("Failed to escalate privileges. We are still not root despite a successful sudo invocation.");
|
log::error!("Failed to escalate privileges. We are still not root despite a successful sudo invocation.");
|
||||||
quit::with_code(3);
|
quit::with_code(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
if local_args.is_present("sudo") {
|
if local_args.is_present("sudo") {
|
||||||
escalate().await;
|
escalate().await;
|
||||||
} else {
|
} else {
|
||||||
eprintln!("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.");
|
||||||
eprintln!("Hint: Add the --sudo flag.");
|
log::warn!("Hint: Add the --sudo flag.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut hive = Hive::from_args(local_args).unwrap();
|
let mut hive = util::hive_from_args(local_args).unwrap();
|
||||||
let hostname = hostname::get().expect("Could not get hostname")
|
let hostname = hostname::get().expect("Could not get hostname")
|
||||||
.to_string_lossy().into_owned();
|
.to_string_lossy().into_owned();
|
||||||
let goal = DeploymentGoal::from_str(local_args.value_of("goal").unwrap()).unwrap();
|
let goal = DeploymentGoal::from_str(local_args.value_of("goal").unwrap()).unwrap();
|
||||||
|
|
||||||
println!("Enumerating nodes...");
|
log::info!("Enumerating nodes...");
|
||||||
let all_nodes = hive.deployment_info().await.unwrap();
|
let all_nodes = hive.deployment_info().await.unwrap();
|
||||||
|
|
||||||
let target: Box<dyn Host> = {
|
let target: Box<dyn Host> = {
|
||||||
if let Some(info) = all_nodes.get(&hostname) {
|
if let Some(info) = all_nodes.get(&hostname) {
|
||||||
if !info.allows_local_deployment() {
|
if !info.allows_local_deployment() {
|
||||||
eprintln!("Local deployment is not enabled for host {}.", hostname);
|
log::error!("Local deployment is not enabled for host {}.", hostname);
|
||||||
eprintln!("Hint: Set deployment.allowLocalDeployment to true.");
|
log::error!("Hint: Set deployment.allowLocalDeployment to true.");
|
||||||
quit::with_code(2);
|
quit::with_code(2);
|
||||||
}
|
}
|
||||||
host::local()
|
host::local()
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Host {} is not present in the Hive configuration.", hostname);
|
log::error!("Host {} is not present in the Hive configuration.", hostname);
|
||||||
quit::with_code(2);
|
quit::with_code(2);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("Building local node configuration...");
|
log::info!("Building local node configuration...");
|
||||||
let profile = {
|
let profile = {
|
||||||
let selected_nodes: Vec<String> = vec![hostname.clone()];
|
let selected_nodes: Vec<String> = vec![hostname.clone()];
|
||||||
let mut profiles = hive.build_selected(selected_nodes).await
|
let mut profiles = hive.build_selected(selected_nodes).await
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use clap::{Arg, App, SubCommand, ArgMatches};
|
use clap::{Arg, App, SubCommand, ArgMatches};
|
||||||
|
|
||||||
use crate::nix::Hive;
|
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
pub fn subcommand() -> App<'static, 'static> {
|
pub fn subcommand() -> App<'static, 'static> {
|
||||||
|
@ -17,9 +16,9 @@ pub fn subcommand() -> App<'static, 'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||||
let mut hive = Hive::from_args(local_args).unwrap();
|
let mut hive = util::hive_from_args(local_args).unwrap();
|
||||||
|
|
||||||
println!("Enumerating nodes...");
|
log::info!("Enumerating nodes...");
|
||||||
let all_nodes = hive.deployment_info().await.unwrap();
|
let all_nodes = hive.deployment_info().await.unwrap();
|
||||||
|
|
||||||
let selected_nodes = match local_args.value_of("on") {
|
let selected_nodes = match local_args.value_of("on") {
|
||||||
|
@ -30,17 +29,17 @@ pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||||
};
|
};
|
||||||
|
|
||||||
if selected_nodes.len() == 0 {
|
if selected_nodes.len() == 0 {
|
||||||
println!("No hosts matched. Exiting...");
|
log::warn!("No hosts matched. Exiting...");
|
||||||
quit::with_code(2);
|
quit::with_code(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if selected_nodes.len() == all_nodes.len() {
|
if selected_nodes.len() == all_nodes.len() {
|
||||||
println!("Building all node configurations...");
|
log::info!("Building all node configurations...");
|
||||||
} else {
|
} else {
|
||||||
println!("Selected {} out of {} hosts. Building node configurations...", selected_nodes.len(), all_nodes.len());
|
log::info!("Selected {} out of {} hosts. Building node configurations...", selected_nodes.len(), all_nodes.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
hive.build_selected(selected_nodes).await.unwrap();
|
hive.build_selected(selected_nodes).await.unwrap();
|
||||||
|
|
||||||
println!("Success!");
|
log::info!("Success!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,10 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::{Arg, App, SubCommand, ArgMatches};
|
use clap::{Arg, App, SubCommand, ArgMatches};
|
||||||
|
|
||||||
use crate::nix::Hive;
|
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
pub fn subcommand() -> App<'static, 'static> {
|
pub fn subcommand() -> App<'static, 'static> {
|
||||||
let command = SubCommand::with_name("introspect")
|
SubCommand::with_name("introspect")
|
||||||
.about("Evaluate expressions using the complete configuration.")
|
.about("Evaluate expressions using the complete configuration.")
|
||||||
.long_about(r#"Your expression should take an attribute set with keys `pkgs`, `lib` and `nodes` (like a NixOS module) and return a JSON-serializable value.
|
.long_about(r#"Your expression should take an attribute set with keys `pkgs`, `lib` and `nodes` (like a NixOS module) and return a JSON-serializable value.
|
||||||
|
|
||||||
|
@ -22,22 +21,13 @@ For example, to retrieve the configuration of one node, you may write something
|
||||||
.short("E")
|
.short("E")
|
||||||
.help("The Nix expression")
|
.help("The Nix expression")
|
||||||
.takes_value(true))
|
.takes_value(true))
|
||||||
.arg(Arg::with_name("config")
|
|
||||||
.short("f")
|
|
||||||
.long("config")
|
|
||||||
.help("Path to a Hive expression")
|
|
||||||
.default_value("hive.nix")
|
|
||||||
.required(true))
|
|
||||||
;
|
|
||||||
|
|
||||||
util::register_common_args(command)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
pub async fn run(_global_args: &ArgMatches<'_>, local_args: &ArgMatches<'_>) {
|
||||||
let mut hive = Hive::from_args(local_args).unwrap();
|
let mut hive = util::hive_from_args(local_args).unwrap();
|
||||||
|
|
||||||
if !(local_args.is_present("expression") ^ local_args.is_present("expression_file")) {
|
if !(local_args.is_present("expression") ^ local_args.is_present("expression_file")) {
|
||||||
eprintln!("Either an expression (-E) xor a .nix file containing an expression should be specified, not both.");
|
log::error!("Either an expression (-E) xor a .nix file containing an expression should be specified, not both.");
|
||||||
quit::with_code(1);
|
quit::with_code(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
45
src/main.rs
45
src/main.rs
|
@ -1,4 +1,5 @@
|
||||||
use clap::{App, AppSettings};
|
use std::env;
|
||||||
|
use clap::{App, AppSettings, Arg};
|
||||||
|
|
||||||
mod nix;
|
mod nix;
|
||||||
mod command;
|
mod command;
|
||||||
|
@ -29,22 +30,60 @@ macro_rules! bind_command {
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
init_logging();
|
||||||
|
|
||||||
let mut app = App::new("Colmena")
|
let mut app = App::new("Colmena")
|
||||||
.version("0.1.0")
|
.version("0.1.0")
|
||||||
.author("Zhaofeng Li <hello@zhaofeng.li>")
|
.author("Zhaofeng Li <hello@zhaofeng.li>")
|
||||||
.about("NixOS deployment tool")
|
.about("NixOS deployment tool")
|
||||||
.global_setting(AppSettings::ColoredHelp)
|
.global_setting(AppSettings::ColoredHelp)
|
||||||
.setting(AppSettings::ArgRequiredElseHelp);
|
.setting(AppSettings::ArgRequiredElseHelp)
|
||||||
|
.arg(Arg::with_name("config")
|
||||||
|
.short("f")
|
||||||
|
.long("config")
|
||||||
|
.help("Path to a Hive expression")
|
||||||
|
|
||||||
|
// The default value is a lie (sort of)!
|
||||||
|
//
|
||||||
|
// The default behavior is to search upwards from the
|
||||||
|
// current working directory for a file named "hive.nix".
|
||||||
|
// This behavior is disabled if --config/-f is explicitly
|
||||||
|
// supplied by the user (occurrences_of > 0).
|
||||||
|
.default_value("hive.nix")
|
||||||
|
.long_help(r#"If this argument is not specified, Colmena will search upwards from the current working directory for a file named "hive.nix". This behavior is disabled if --config/-f is given explicitly.
|
||||||
|
|
||||||
|
For a sample configuration, see <https://github.com/zhaofengli/colmena>.
|
||||||
|
"#)
|
||||||
|
.global(true))
|
||||||
|
.arg(Arg::with_name("show-trace")
|
||||||
|
.long("show-trace")
|
||||||
|
.help("Show debug information for Nix commands")
|
||||||
|
.long_help("Passes --show-trace to Nix commands")
|
||||||
|
.global(true)
|
||||||
|
.takes_value(false));
|
||||||
|
|
||||||
bind_command!(apply, app);
|
bind_command!(apply, app);
|
||||||
bind_command!(apply_local, app);
|
bind_command!(apply_local, app);
|
||||||
bind_command!(build, app);
|
bind_command!(build, app);
|
||||||
bind_command!(introspect, app);
|
bind_command!(introspect, app);
|
||||||
|
|
||||||
let matches = app.get_matches();
|
let matches = app.clone().get_matches();
|
||||||
|
|
||||||
command!(apply, matches);
|
command!(apply, matches);
|
||||||
command!("apply-local", apply_local, matches);
|
command!("apply-local", apply_local, matches);
|
||||||
command!(build, matches);
|
command!(build, matches);
|
||||||
command!(introspect, matches);
|
command!(introspect, matches);
|
||||||
|
|
||||||
|
app.print_long_help().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_logging() {
|
||||||
|
if env::var("RUST_LOG").is_err() {
|
||||||
|
// HACK
|
||||||
|
env::set_var("RUST_LOG", "info")
|
||||||
|
}
|
||||||
|
env_logger::builder()
|
||||||
|
.format_timestamp(None)
|
||||||
|
.format_module_path(false)
|
||||||
|
.init();
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,7 +187,7 @@ impl SSH {
|
||||||
progress.set_message(trimmed);
|
progress.set_message(trimmed);
|
||||||
progress.inc(0);
|
progress.inc(0);
|
||||||
} else {
|
} else {
|
||||||
println!("{} | {}", style(&self.friendly_name).cyan(), trimmed);
|
eprintln!("{} | {}", style(&self.friendly_name).cyan(), trimmed);
|
||||||
}
|
}
|
||||||
self.logs.push(line);
|
self.logs.push(line);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use clap::ArgMatches;
|
|
||||||
use indicatif::ProgressBar;
|
use indicatif::ProgressBar;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
@ -72,17 +71,8 @@ impl Hive {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_args(args: &ArgMatches<'_>) -> NixResult<Self> {
|
pub fn show_trace(&mut self, value: bool) {
|
||||||
let path = args.value_of("config").expect("The config arg should exist").to_owned();
|
self.show_trace = value;
|
||||||
let path = canonicalize_path(path);
|
|
||||||
|
|
||||||
let mut hive = Self::new(path)?;
|
|
||||||
|
|
||||||
if args.is_present("show-trace") {
|
|
||||||
hive.show_trace = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(hive)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve deployment info for all nodes
|
/// Retrieve deployment info for all nodes
|
||||||
|
@ -443,12 +433,3 @@ impl DeploymentTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn canonicalize_path(path: String) -> PathBuf {
|
|
||||||
if !path.starts_with("/") {
|
|
||||||
format!("./{}", path).into()
|
|
||||||
} else {
|
|
||||||
path.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
103
src/util.rs
103
src/util.rs
|
@ -1,15 +1,89 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
use clap::{Arg, App};
|
use clap::{App, Arg, ArgMatches};
|
||||||
use glob::Pattern as GlobPattern;
|
use glob::Pattern as GlobPattern;
|
||||||
|
|
||||||
use super::nix::DeploymentConfig;
|
use super::nix::{DeploymentConfig, Hive, NixResult};
|
||||||
|
|
||||||
enum NodeFilter {
|
enum NodeFilter {
|
||||||
NameFilter(GlobPattern),
|
NameFilter(GlobPattern),
|
||||||
TagFilter(GlobPattern),
|
TagFilter(GlobPattern),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hive_from_args(args: &ArgMatches<'_>) -> NixResult<Hive> {
|
||||||
|
let path = match args.occurrences_of("config") {
|
||||||
|
0 => {
|
||||||
|
// traverse upwards until we find hive.nix
|
||||||
|
let mut cur = std::env::current_dir()?;
|
||||||
|
let mut hive_path = None;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut listing = match fs::read_dir(&cur) {
|
||||||
|
Ok(listing) => listing,
|
||||||
|
Err(e) => {
|
||||||
|
// This can very likely fail in shared environments
|
||||||
|
// where users aren't able to list /home. It's not
|
||||||
|
// unexpected.
|
||||||
|
//
|
||||||
|
// It may not be immediately obvious to the user that
|
||||||
|
// we are traversing upwards to find hive.nix.
|
||||||
|
log::warn!("Could not traverse up ({:?}) to find hive.nix: {}", cur, e);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let found = listing.find_map(|rdirent| {
|
||||||
|
match rdirent {
|
||||||
|
Err(e) => Some(Err(e)),
|
||||||
|
Ok(f) => {
|
||||||
|
if f.file_name() == "hive.nix" {
|
||||||
|
Some(Ok(f))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(rdirent) = found {
|
||||||
|
let dirent = rdirent?;
|
||||||
|
hive_path = Some(dirent.path());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
match cur.parent() {
|
||||||
|
Some(parent) => {
|
||||||
|
cur = parent.to_owned();
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hive_path.is_none() {
|
||||||
|
log::error!("Could not find `hive.nix` in {:?} or any parent directory", std::env::current_dir()?);
|
||||||
|
}
|
||||||
|
|
||||||
|
hive_path.unwrap()
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let path = args.value_of("config").expect("The config arg should exist").to_owned();
|
||||||
|
canonicalize_cli_path(path)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut hive = Hive::new(path)?;
|
||||||
|
|
||||||
|
if args.is_present("show-trace") {
|
||||||
|
hive.show_trace(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(hive)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn filter_nodes(nodes: &HashMap<String, DeploymentConfig>, filter: &str) -> Vec<String> {
|
pub fn filter_nodes(nodes: &HashMap<String, DeploymentConfig>, filter: &str) -> Vec<String> {
|
||||||
let filters: Vec<NodeFilter> = filter.split(",").map(|pattern| {
|
let filters: Vec<NodeFilter> = filter.split(",").map(|pattern| {
|
||||||
use NodeFilter::*;
|
use NodeFilter::*;
|
||||||
|
@ -48,24 +122,7 @@ pub fn filter_nodes(nodes: &HashMap<String, DeploymentConfig>, filter: &str) ->
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_common_args<'a, 'b>(command: App<'a, 'b>) -> App<'a, 'b> {
|
|
||||||
command
|
|
||||||
.arg(Arg::with_name("config")
|
|
||||||
.short("f")
|
|
||||||
.long("config")
|
|
||||||
.help("Path to a Hive expression")
|
|
||||||
.default_value("hive.nix")
|
|
||||||
.required(true))
|
|
||||||
.arg(Arg::with_name("show-trace")
|
|
||||||
.long("show-trace")
|
|
||||||
.help("Show debug information for Nix commands")
|
|
||||||
.long_help("Passes --show-trace to Nix commands")
|
|
||||||
.takes_value(false))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn register_selector_args<'a, 'b>(command: App<'a, 'b>) -> App<'a, 'b> {
|
pub fn register_selector_args<'a, 'b>(command: App<'a, 'b>) -> App<'a, 'b> {
|
||||||
let command = register_common_args(command);
|
|
||||||
|
|
||||||
command
|
command
|
||||||
.arg(Arg::with_name("on")
|
.arg(Arg::with_name("on")
|
||||||
.long("on")
|
.long("on")
|
||||||
|
@ -79,3 +136,11 @@ Valid examples:
|
||||||
- @a-tag,@tags-can-have-*"#)
|
- @a-tag,@tags-can-have-*"#)
|
||||||
.takes_value(true))
|
.takes_value(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn canonicalize_cli_path(path: String) -> PathBuf {
|
||||||
|
if !path.starts_with("/") {
|
||||||
|
format!("./{}", path).into()
|
||||||
|
} else {
|
||||||
|
path.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue