Skip to content

Commit 17f49ee

Browse files
committed
cli: match against node class for client commands
For `deploy` and `ssh` commands, a new `--class` argument is added that allows selecting only clients in a given Nomad node_class.
1 parent 63bc8e4 commit 17f49ee

File tree

4 files changed

+62
-52
lines changed

4 files changed

+62
-52
lines changed

nix/cli/packages/cli/src/cli.rs

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub(crate) async fn ssh(sub: &ArgMatches, cluster: ClusterHandle) -> Result<()>
2929
let mut args: Vec<String> = sub.get_many("args").unwrap_or_default().cloned().collect();
3030
let job: Vec<String> = sub.get_many("job").unwrap_or_default().cloned().collect();
3131
let delay = Duration::from_secs(*sub.get_one::<u64>("delay").unwrap_or(&0));
32+
let node_class = sub.get_one::<String>("class").cloned();
3233

3334
let namespace = sub
3435
.get_one::<String>("namespace")
@@ -41,7 +42,7 @@ pub(crate) async fn ssh(sub: &ArgMatches, cluster: ClusterHandle) -> Result<()>
4142

4243
if sub.is_present("all") {
4344
let nodes = if sub.is_present("clients") {
44-
cluster.nodes.find_clients()
45+
cluster.nodes.find_clients(node_class)
4546
} else {
4647
cluster.nodes
4748
};
@@ -58,7 +59,7 @@ pub(crate) async fn ssh(sub: &ArgMatches, cluster: ClusterHandle) -> Result<()>
5859
return Ok(());
5960
} else if sub.is_present("parallel") {
6061
let nodes = if sub.is_present("clients") {
61-
cluster.nodes.find_clients()
62+
cluster.nodes.find_clients(node_class)
6263
} else {
6364
cluster.nodes
6465
};
@@ -143,11 +144,12 @@ async fn init_ssh(ip: IpAddr, args: Vec<String>, cluster: String) -> Result<()>
143144
pub(crate) async fn deploy(sub: &ArgMatches, cluster: ClusterHandle) -> Result<()> {
144145
let opts = <subs::Deploy as FromArgMatches>::from_arg_matches(sub).unwrap_or_default();
145146
let cluster = cluster.await??;
147+
let node_class = sub.get_one::<String>("class").cloned();
146148

147149
info!("node needles: {:?}", opts.nodes);
148150

149151
let instances = if opts.clients {
150-
cluster.nodes.find_clients()
152+
cluster.nodes.find_clients(node_class)
151153
} else {
152154
cluster
153155
.nodes
@@ -219,7 +221,6 @@ async fn info_print(cluster: ClusterHandle, json: bool) -> Result<()> {
219221
if json {
220222
let stdout = io::stdout();
221223
let handle = stdout.lock();
222-
env::set_var("BITTE_INFO_NO_ALLOCS", "");
223224
serde_json::to_writer_pretty(handle, &cluster)?;
224225
} else {
225226
let mut core_nodes_table = Table::new();
@@ -234,23 +235,14 @@ async fn info_print(cluster: ClusterHandle, json: bool) -> Result<()> {
234235

235236
for node in nodes.into_iter() {
236237
match node.asg {
237-
Some(asg) => {
238-
let name: String = asg.to_string();
239-
// TODO extract true client group
238+
Some(_) => {
240239
let group: String = {
241-
let suffix = name.split('-').last().unwrap_or_default().to_owned();
242-
let i_type = node
243-
.node_type
244-
.clone()
245-
.unwrap_or_default()
246-
.split('.')
247-
.last()
248-
.unwrap_or_default()
249-
.to_owned();
250-
if suffix == i_type {
251-
"".to_string()
252-
} else {
253-
format!(" ({})", suffix)
240+
match node.nomad_client {
241+
Some(client) => match client.node_class {
242+
Some(class) => format!(" ({})", class),
243+
None => "".to_string(),
244+
},
245+
None => "".to_string(),
254246
}
255247
};
256248

nix/cli/packages/cli/src/cli/subs.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ pub struct Info {
2121
#[clap(short, long)]
2222
/// output as JSON
2323
json: bool,
24+
#[clap(flatten)]
25+
nomad: Nomad,
2426
}
2527

2628
#[derive(Parser, Default)]
@@ -39,6 +41,13 @@ pub struct Deploy {
3941
/// nodes to deploy; takes one or more needles to match against:
4042
/// private & public ip, node name and aws client id
4143
pub nodes: Vec<String>,
44+
45+
#[clap(flatten)]
46+
nomad: Nomad,
47+
48+
#[clap(long, short = 'o', requires_all = &["nomad", "clients"])]
49+
/// the Nomad node class to filter clients against
50+
class: Option<String>,
4251
}
4352
#[derive(Parser)]
4453
/// Generate completions for the given shell
@@ -79,6 +88,19 @@ struct Globals {
7988
aws_asg_regions: Option<Vec<String>>,
8089
}
8190

91+
#[derive(Parser, Default)]
92+
struct Nomad {
93+
#[clap(
94+
long,
95+
value_name = "TOKEN",
96+
env = "NOMAD_TOKEN",
97+
value_parser = clap::value_parser!(Uuid),
98+
setting = ArgSettings::HideEnvValues
99+
)]
100+
/// The Nomad token used to query node information
101+
nomad: Option<Uuid>,
102+
}
103+
82104
#[derive(Parser)]
83105
/// SSH to instances
84106
pub struct Ssh {
@@ -94,15 +116,11 @@ pub struct Ssh {
94116
/// specify client by: job, group, alloc_index;
95117
/// this will also 'cd' to the alloc dir if <ARGS> is empty
96118
job: Option<String>,
97-
#[clap(
98-
long,
99-
value_name = "TOKEN",
100-
env = "NOMAD_TOKEN",
101-
value_parser = clap::value_parser!(Uuid),
102-
setting = ArgSettings::HideEnvValues
103-
)]
104-
/// for '-j': The Nomad token used to query node information
105-
nomad: Option<Uuid>,
119+
#[clap(long, short, env = "NOMAD_NAMESPACE")]
120+
/// Nomad namespace to search for jobs with `-j`
121+
namespace: Option<String>,
122+
#[clap(flatten)]
123+
nomad: Nomad,
106124
#[clap(
107125
long,
108126
short,
@@ -121,12 +139,12 @@ pub struct Ssh {
121139
)]
122140
/// run <ARGS> on nodes in parallel
123141
parallel: bool,
124-
#[clap(long, short, env = "NOMAD_NAMESPACE")]
125-
/// for '-j': specify nomad namespace to search for <JOB>
126-
namespace: Option<String>,
127142
#[clap(long, short = 'l', requires = "multi")]
128143
/// for '-a' or '-p': execute commands only on Nomad clients
129144
clients: bool,
145+
#[clap(long, short = 'o', requires_all = &["nomad", "clients"])]
146+
/// the Nomad node class to filter clients against
147+
class: Option<String>,
130148
#[clap(long, short, requires = "all")]
131149
/// for '-a': seconds to delay between commands
132150
delay: Option<usize>,

nix/cli/packages/cli/src/main.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,18 @@ async fn main() -> Result<()> {
1717

1818
let matches = app.clone().get_matches();
1919

20-
let run = |sub: &ArgMatches, init_log: bool, token| {
20+
let run = |sub: &ArgMatches, init_log: bool| {
2121
if init_log {
2222
cli::init_log(matches.occurrences_of("verbose"))
2323
};
24+
let token = sub.get_one::<Uuid>("nomad").copied();
2425
BitteCluster::init(sub.clone(), token)
2526
};
2627

2728
match matches.subcommand() {
28-
Some(("deploy", sub)) => cli::deploy(sub, run(sub, false, None)).await?,
29-
Some(("info", sub)) => cli::info(sub, run(sub, true, None)).await?,
30-
Some(("ssh", sub)) => {
31-
let token: Option<Uuid> = if sub.is_present("job") {
32-
sub.get_one::<Uuid>("nomad").copied()
33-
} else {
34-
None
35-
};
36-
cli::ssh(sub, run(sub, true, token)).await?
37-
}
29+
Some(("deploy", sub)) => cli::deploy(sub, run(sub, false)).await?,
30+
Some(("info", sub)) => cli::info(sub, run(sub, true)).await?,
31+
Some(("ssh", sub)) => cli::ssh(sub, run(sub, true)).await?,
3832
Some(("completions", sub)) => {
3933
if let Some(shell) = sub.get_one::<Shell>("shell").copied() {
4034
cli::completions(shell, app).await;

nix/cli/packages/cli/src/types.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use clap::{ArgEnum, ArgMatches};
88
use serde::{de::Deserializer, Deserialize, Serialize};
99
use std::cmp::Ordering;
1010
use std::collections::hash_set::HashSet;
11-
use std::env;
1211
use std::fmt::{Display, Formatter};
1312
use std::str::FromStr;
1413
use std::sync::Arc;
@@ -98,6 +97,8 @@ pub struct NomadClient {
9897
pub allocs: Option<NomadAllocs>,
9998
#[serde(rename = "Address")]
10099
pub address: Option<IpAddr>,
100+
#[serde(rename = "NodeClass")]
101+
pub node_class: Option<String>,
101102
}
102103

103104
impl NomadClient {
@@ -122,7 +123,7 @@ pub struct BitteNode {
122123
pub priv_ip: IpAddr,
123124
pub pub_ip: IpAddr,
124125
pub nixos: String,
125-
#[serde(skip_serializing_if = "skip_info")]
126+
#[serde(skip_serializing_if = "Option::is_none")]
126127
pub nomad_client: Option<NomadClient>,
127128
#[serde(skip_serializing_if = "Option::is_none")]
128129
pub node_type: Option<String>,
@@ -132,17 +133,13 @@ pub struct BitteNode {
132133
pub asg: Option<String>,
133134
}
134135

135-
fn skip_info<T>(_: &Option<T>) -> bool {
136-
env::var("BITTE_INFO_NO_ALLOCS").is_ok()
137-
}
138-
139136
pub trait BitteFind
140137
where
141138
Self: IntoIterator,
142139
{
143140
fn find_needle(self, needle: &str) -> Result<Self::Item>;
144141
fn find_needles(self, needles: Vec<&str>) -> Self;
145-
fn find_clients(self) -> Self;
142+
fn find_clients(self, node_class: Option<String>) -> Self;
146143
fn find_with_job(
147144
self,
148145
name: &str,
@@ -248,8 +245,17 @@ impl BitteFind for BitteNodes {
248245
.with_context(|| format!("{} does not match any nodes", needle))
249246
}
250247

251-
fn find_clients(self) -> Self {
252-
self.into_iter().filter(|node| node.asg.is_some()).collect()
248+
fn find_clients(self, node_class: Option<String>) -> Self {
249+
match node_class {
250+
Some(class) => self
251+
.into_iter()
252+
.filter(|node| match &node.nomad_client {
253+
Some(client) => client.node_class.clone().unwrap_or_default() == class,
254+
None => false,
255+
})
256+
.collect(),
257+
None => self.into_iter().filter(|node| node.asg.is_some()).collect(),
258+
}
253259
}
254260

255261
fn find_needles(self, needles: Vec<&str>) -> Self {

0 commit comments

Comments
 (0)