Skip to content

Commit 1acfc4c

Browse files
committed
cli: massive code refactor
Modularize the code to get rid of the massive types module, hopefully making the code much easier to read. Also removes dead code and crates that were not being used.
1 parent 6df892b commit 1acfc4c

File tree

23 files changed

+944
-2135
lines changed

23 files changed

+944
-2135
lines changed

nix/cli/packages/cli/Cargo.lock

Lines changed: 0 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nix/cli/packages/cli/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ anyhow = "^1.0.0"
2020
deploy-rs = { git = "https://github.com/input-output-hk/deploy-rs" }
2121
uuid = { version = "^1", features = ["serde"] }
2222
reqwest = { version = "^0.11.0", features = ["json", "gzip"] }
23-
thiserror = "^1.0.0"
24-
netrc-rs = "0.1.2"
2523
enum-utils = "^0"
2624
clap_complete = "^3"
2725
aws-config = "^0"
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
pub mod node;
2+
pub mod provider;
3+
4+
use super::nomad::{alloc::NomadAlloc, client::NomadClient};
5+
use anyhow::Result;
6+
use clap::ArgMatches;
7+
use node::BitteNode;
8+
use node::BitteNodes;
9+
pub use provider::BitteProvider;
10+
use reqwest::{
11+
header::{HeaderMap, HeaderValue},
12+
Client,
13+
};
14+
use serde::{Deserialize, Serialize};
15+
use std::sync::Arc;
16+
use std::time::Duration;
17+
use std::time::SystemTime;
18+
use tokio::task::JoinHandle;
19+
use uuid::Uuid;
20+
21+
pub type ClusterHandle = JoinHandle<Result<BitteCluster>>;
22+
23+
pub trait BitteFind
24+
where
25+
Self: IntoIterator,
26+
{
27+
fn find_needle(self, needle: &str) -> Result<Self::Item>;
28+
fn find_needles(self, needles: Vec<&str>) -> Self;
29+
fn find_clients(self, node_class: Option<String>) -> Self;
30+
fn find_with_job(
31+
self,
32+
name: &str,
33+
group: &str,
34+
index: &str,
35+
namespace: &str,
36+
) -> Result<(Self::Item, NomadAlloc)>;
37+
}
38+
39+
/// A description of a Bitte cluster and its nodes
40+
#[derive(Debug, Serialize, Deserialize)]
41+
pub struct BitteCluster {
42+
pub name: String,
43+
pub nodes: BitteNodes,
44+
pub domain: String,
45+
pub provider: BitteProvider,
46+
#[serde(skip)]
47+
pub nomad_api_client: Option<Arc<Client>>,
48+
pub ttl: SystemTime,
49+
}
50+
51+
impl BitteCluster {
52+
pub async fn new(args: &ArgMatches, token: Option<Uuid>) -> Result<Self> {
53+
let name = args.get_one::<String>("name").unwrap().to_owned();
54+
let domain = args.get_one::<String>("domain").unwrap().to_owned();
55+
let provider: BitteProvider = args
56+
.get_one::<BitteProvider>("provider")
57+
.unwrap()
58+
.to_owned();
59+
60+
let nomad_api_client = match token {
61+
Some(token) => {
62+
let mut token = HeaderValue::from_str(&token.to_string())?;
63+
token.set_sensitive(true);
64+
let mut headers = HeaderMap::new();
65+
headers.insert("X-Nomad-Token", token);
66+
Some(Arc::new(
67+
Client::builder()
68+
.default_headers(headers)
69+
.gzip(true)
70+
.build()?,
71+
))
72+
}
73+
None => None,
74+
};
75+
76+
let nodes = if let Some(client) = &nomad_api_client {
77+
let allocs = tokio::spawn(NomadAlloc::find_allocs(
78+
Arc::clone(client),
79+
domain.to_owned(),
80+
));
81+
82+
let client_nodes = tokio::spawn(NomadClient::find_nomad_nodes(
83+
Arc::clone(client),
84+
domain.to_owned(),
85+
));
86+
87+
tokio::spawn(BitteNode::find_nodes(
88+
provider,
89+
name.to_owned(),
90+
Some(allocs),
91+
Some(client_nodes),
92+
args.clone(),
93+
))
94+
.await??
95+
} else {
96+
tokio::spawn(BitteNode::find_nodes(
97+
provider,
98+
name.to_owned(),
99+
None,
100+
None,
101+
args.clone(),
102+
))
103+
.await??
104+
};
105+
106+
let cluster = Self {
107+
name,
108+
domain,
109+
provider,
110+
nomad_api_client,
111+
nodes,
112+
ttl: SystemTime::now()
113+
.checked_add(Duration::from_secs(300))
114+
.unwrap(),
115+
};
116+
117+
Ok(cluster)
118+
}
119+
120+
#[inline(always)]
121+
pub fn init(args: ArgMatches, token: Option<Uuid>) -> ClusterHandle {
122+
tokio::spawn(async move { BitteCluster::new(&args, token).await })
123+
}
124+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use super::BitteFind;
2+
use crate::nomad::alloc::NomadAlloc;
3+
use anyhow::{Context, Result};
4+
use std::net::IpAddr;
5+
6+
impl BitteFind for super::BitteNodes {
7+
fn find_with_job(
8+
self,
9+
name: &str,
10+
group: &str,
11+
index: &str,
12+
namespace: &str,
13+
) -> Result<(Self::Item, NomadAlloc)> {
14+
let node = self
15+
.into_iter()
16+
.find(|node| {
17+
let client = &node.nomad_client;
18+
if client.is_none() {
19+
return false;
20+
};
21+
22+
let allocs = &client.as_ref().unwrap().allocs;
23+
if allocs.is_none() || allocs.as_ref().unwrap().is_empty() {
24+
return false;
25+
};
26+
27+
allocs.as_ref().unwrap().iter().any(|alloc| {
28+
alloc.namespace == namespace
29+
&& alloc.job_id == name
30+
&& alloc.task_group == group
31+
&& alloc.index.get() == index.parse().ok()
32+
&& alloc.status == "running"
33+
})
34+
})
35+
.with_context(|| {
36+
format!(
37+
"{}, {}, {} does not match any running nomad allocations in namespace {}",
38+
name, group, index, namespace
39+
)
40+
})?;
41+
let alloc = node
42+
.nomad_client
43+
.as_ref()
44+
.unwrap()
45+
.allocs
46+
.as_ref()
47+
.unwrap()
48+
.iter()
49+
.find(|alloc| {
50+
alloc.namespace == namespace
51+
&& alloc.job_id == name
52+
&& alloc.task_group == group
53+
&& alloc.index.get() == index.parse().ok()
54+
&& alloc.status == "running"
55+
})
56+
.unwrap()
57+
.clone();
58+
Ok((node, alloc))
59+
}
60+
61+
fn find_needle(self, needle: &str) -> Result<Self::Item> {
62+
self.into_iter()
63+
.find(|node| {
64+
let ip = needle.parse::<IpAddr>().ok();
65+
66+
node.id == needle
67+
|| node.name == needle
68+
|| node
69+
.nomad_client
70+
.as_ref()
71+
.unwrap_or(&Default::default())
72+
.id
73+
.hyphenated()
74+
.to_string()
75+
== needle
76+
|| Some(node.priv_ip) == ip
77+
|| Some(node.pub_ip) == ip
78+
})
79+
.with_context(|| format!("{} does not match any nodes", needle))
80+
}
81+
82+
fn find_clients(self, node_class: Option<String>) -> Self {
83+
match node_class {
84+
Some(class) => self
85+
.into_iter()
86+
.filter(|node| match &node.nomad_client {
87+
Some(client) => client.node_class.clone().unwrap_or_default() == class,
88+
None => false,
89+
})
90+
.collect(),
91+
None => self.into_iter().filter(|node| node.asg.is_some()).collect(),
92+
}
93+
}
94+
95+
fn find_needles(self, needles: Vec<&str>) -> Self {
96+
self.into_iter()
97+
.filter(|node| {
98+
let ips: Vec<Option<IpAddr>> = needles
99+
.iter()
100+
.map(|needle| needle.parse::<IpAddr>().ok())
101+
.collect();
102+
103+
needles.contains(&&*node.id)
104+
|| needles.contains(&&*node.name)
105+
|| needles.contains(
106+
&&*node
107+
.nomad_client
108+
.as_ref()
109+
.unwrap_or(&Default::default())
110+
.id
111+
.hyphenated()
112+
.to_string(),
113+
)
114+
|| ips.contains(&Some(node.priv_ip))
115+
|| ips.contains(&Some(node.pub_ip))
116+
})
117+
.collect()
118+
}
119+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use crate::bitte::BitteNode;
2+
use aws_sdk_ec2::model::{Instance, Tag};
3+
use std::net::{IpAddr, Ipv4Addr};
4+
use std::str::FromStr;
5+
6+
impl From<Instance> for BitteNode {
7+
fn from(instance: Instance) -> Self {
8+
let tags = instance.tags.unwrap_or_default();
9+
let empty_tag = Tag::builder().build();
10+
11+
let nixos = tags
12+
.iter()
13+
.find(|tag| tag.key == Some("UID".into()))
14+
.unwrap_or(&empty_tag)
15+
.value
16+
.as_ref();
17+
18+
let name = tags
19+
.iter()
20+
.find(|tag| tag.key == Some("Name".into()))
21+
.unwrap_or(&empty_tag)
22+
.value
23+
.as_ref();
24+
25+
let asg = tags
26+
.iter()
27+
.find(|tag| tag.key == Some("aws:autoscaling:groupName".into()))
28+
.unwrap_or(&empty_tag)
29+
.value
30+
.as_ref();
31+
32+
let no_ip = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
33+
34+
let zone = if let Some(p) = instance.placement {
35+
p.availability_zone
36+
} else {
37+
None
38+
};
39+
40+
Self {
41+
id: instance.instance_id.unwrap_or_default(),
42+
name: match name {
43+
Some(name) => name.to_owned(),
44+
None => "".into(),
45+
},
46+
priv_ip: IpAddr::from_str(&instance.private_ip_address.unwrap_or_default())
47+
.unwrap_or(no_ip),
48+
pub_ip: IpAddr::from_str(&instance.public_ip_address.unwrap_or_default())
49+
.unwrap_or(no_ip),
50+
nomad_client: None,
51+
nixos: match nixos {
52+
Some(nixos) => nixos.to_owned(),
53+
None => "".into(),
54+
},
55+
node_type: instance.instance_type.map(|s| s.as_str().to_owned()),
56+
zone,
57+
asg: asg.map(|asg| asg.to_owned()),
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)