2020-12-18 10:27:44 +01:00
|
|
|
use std::collections::HashMap;
|
2020-12-29 20:31:19 +01:00
|
|
|
use std::path::PathBuf;
|
|
|
|
use std::fs;
|
2020-12-18 10:27:44 +01:00
|
|
|
|
2020-12-29 20:31:19 +01:00
|
|
|
use clap::{App, Arg, ArgMatches};
|
2020-12-16 05:21:26 +01:00
|
|
|
use glob::Pattern as GlobPattern;
|
|
|
|
|
2020-12-29 20:31:19 +01:00
|
|
|
use super::nix::{DeploymentConfig, Hive, NixResult};
|
2020-12-18 10:27:44 +01:00
|
|
|
|
|
|
|
enum NodeFilter {
|
|
|
|
NameFilter(GlobPattern),
|
|
|
|
TagFilter(GlobPattern),
|
|
|
|
}
|
|
|
|
|
2020-12-29 20:31:19 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2020-12-18 10:27:44 +01:00
|
|
|
pub fn filter_nodes(nodes: &HashMap<String, DeploymentConfig>, filter: &str) -> Vec<String> {
|
|
|
|
let filters: Vec<NodeFilter> = filter.split(",").map(|pattern| {
|
|
|
|
use NodeFilter::*;
|
|
|
|
if let Some(tag_pattern) = pattern.strip_prefix("@") {
|
|
|
|
TagFilter(GlobPattern::new(tag_pattern).unwrap())
|
|
|
|
} else {
|
|
|
|
NameFilter(GlobPattern::new(pattern).unwrap())
|
|
|
|
}
|
|
|
|
}).collect();
|
2020-12-16 05:21:26 +01:00
|
|
|
|
|
|
|
if filters.len() > 0 {
|
2020-12-18 10:27:44 +01:00
|
|
|
nodes.iter().filter_map(|(name, node)| {
|
2020-12-16 05:21:26 +01:00
|
|
|
for filter in filters.iter() {
|
2020-12-18 10:27:44 +01:00
|
|
|
use NodeFilter::*;
|
|
|
|
match filter {
|
|
|
|
TagFilter(pat) => {
|
|
|
|
// Welp
|
|
|
|
for tag in node.tags() {
|
|
|
|
if pat.matches(tag) {
|
|
|
|
return Some(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NameFilter(pat) => {
|
|
|
|
if pat.matches(name) {
|
|
|
|
return Some(name)
|
|
|
|
}
|
|
|
|
}
|
2020-12-16 05:21:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-18 10:27:44 +01:00
|
|
|
None
|
2020-12-16 05:21:26 +01:00
|
|
|
}).cloned().collect()
|
|
|
|
} else {
|
2020-12-18 10:27:44 +01:00
|
|
|
nodes.keys().cloned().collect()
|
2020-12-16 05:21:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-29 06:35:43 +01:00
|
|
|
pub fn register_selector_args<'a, 'b>(command: App<'a, 'b>) -> App<'a, 'b> {
|
|
|
|
command
|
2020-12-16 05:21:26 +01:00
|
|
|
.arg(Arg::with_name("on")
|
|
|
|
.long("on")
|
|
|
|
.help("Select a list of machines")
|
2020-12-18 10:27:44 +01:00
|
|
|
.long_help(r#"The list is comma-separated and globs are supported. To match tags, prepend the filter by @.
|
2020-12-16 05:21:26 +01:00
|
|
|
Valid examples:
|
|
|
|
|
|
|
|
- host1,host2,host3
|
|
|
|
- edge-*
|
2020-12-18 10:27:44 +01:00
|
|
|
- edge-*,core-*
|
|
|
|
- @a-tag,@tags-can-have-*"#)
|
2020-12-16 05:21:26 +01:00
|
|
|
.takes_value(true))
|
|
|
|
}
|
2020-12-29 20:31:19 +01:00
|
|
|
|
|
|
|
fn canonicalize_cli_path(path: String) -> PathBuf {
|
|
|
|
if !path.starts_with("/") {
|
|
|
|
format!("./{}", path).into()
|
|
|
|
} else {
|
|
|
|
path.into()
|
|
|
|
}
|
|
|
|
}
|