1use crate::{client, config::KnifeConfig};
2use colored::Colorize;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::{error::Error, process::Stdio};
6
7#[derive(Deserialize, Serialize, Debug, Eq, PartialEq, Clone)]
8pub struct ChefNode {
9 pub automatic: ChefNodeAutomatic,
10 pub chef_environment: String,
11 pub name: String,
12 pub run_list: Vec<String>,
13}
14
15#[derive(Deserialize, Serialize, Debug, Eq, PartialEq, Clone)]
17pub struct ChefNodeAutomatic {
18 #[serde(default)]
19 pub ipaddress: String,
20
21 #[serde(default)]
22 pub macaddress: String,
23
24 #[serde(default)]
25 pub hostname: String,
26
27 #[serde(default)]
28 pub os: String,
29
30 #[serde(default)]
31 pub os_version: String,
32
33 #[serde(default)]
34 pub machinename: String,
35
36 #[serde(default)]
37 pub fqdn: String,
38
39 #[serde(default)]
40 pub platform: String,
41
42 #[serde(default)]
43 pub platform_family: String,
44
45 #[serde(default)]
46 pub platform_version: String,
47
48 #[serde(default)]
49 pub recipes: Vec<String>,
50
51 #[serde(default)]
52 pub roles: Vec<String>,
53}
54
55impl ChefNode {
56 pub fn display(&self) {
57 println!("{}: {}", "Node name".green().bold(), self.name);
58 println!(
59 "{}: {}",
60 "IP Address".green().bold(),
61 self.automatic.ipaddress
62 );
63 println!(
64 "{}: {}",
65 "Chef Environment".green().bold(),
66 self.chef_environment
67 );
68 println!(
69 "{}: {}",
70 "Roles".green().bold(),
71 self.automatic.roles.join(", ")
72 );
73 println!(
74 "{}: {}",
75 "Run List".green().bold(),
76 self.run_list.join(", ")
77 );
78
79 println!(
80 "{}: {}",
81 "Recipes".green().bold(),
82 self.automatic.recipes.join(", ")
83 );
84
85 println!(
86 "{}: {}",
87 "Platform".green().bold(),
88 self.automatic.platform_family
89 );
90
91 println!(
92 "{}: {}",
93 "Platform version".green().bold(),
94 self.automatic.platform_version
95 );
96 println!("\n");
97 }
98}
99
100pub async fn node_list(config: &KnifeConfig) -> Result<(), Box<dyn Error>> {
101 let request_path = format!("/organizations/{}/nodes", config.organization);
102
103 match client::request::get(config, &request_path, "").await {
104 Ok(n) => {
105 let nodes: Value = serde_json::from_str(&n.body)?;
106
107 for (k, _) in nodes.as_object().unwrap() {
108 println!("{k}");
109 }
110 Ok(())
111 }
112 Err(e) => Err(format!("node list: {e}").into()),
113 }
114}
115
116pub async fn node_show(config: &KnifeConfig, node_id: &str) -> Result<(), Box<dyn Error>> {
118 let request_path = format!("/organizations/{}/nodes/{}", config.organization, node_id);
119
120 match client::request::get(config, &request_path, "").await {
121 Ok(n) => {
122 match n.status {
123 404 => {
124 println!("Node not found: {}", node_id);
125 }
126 200 => {
127 let node: ChefNode = serde_json::from_str(&n.body)?;
128 node.display();
129 }
130 _ => {
131 println!("Unkown status code: {}", n.status)
132 }
133 }
134
135 Ok(())
136 }
137 Err(e) => Err(format!("node show: {e}").into()),
138 }
139}
140
141pub async fn node_ssh(
143 config: &KnifeConfig,
144 node_id: &str,
145 user: Option<String>,
146) -> Result<(), Box<dyn Error>> {
147 let request_path: String = format!("/organizations/{}/nodes/{}", config.organization, node_id);
148
149 match client::request::get(config, &request_path, "").await {
150 Ok(n) => {
151 match n.status {
152 200 => {
153 let node: ChefNode = serde_json::from_str(&n.body)?;
154
155 open_ssh_connection(node.automatic.ipaddress, user).await?;
156 }
157 404 => {
158 println!("Node not found: {}", node_id);
159 }
160 _ => {
161 println!("Unknown status code: {}", n.status)
162 }
163 }
164 Ok(())
165 }
166 Err(e) => Err(format!("node show: {}", e).into()),
167 }
168}
169
170async fn open_ssh_connection(
173 node_ipaddress: String,
174 user: Option<String>,
175) -> Result<(), Box<dyn Error>> {
176 let ssh_command = match user {
177 Some(u) => format!("{}@{}", u, node_ipaddress),
178 None => node_ipaddress,
179 };
180
181 let mut child = std::process::Command::new("ssh")
182 .arg(ssh_command)
183 .stdin(Stdio::inherit())
184 .stdout(Stdio::inherit())
185 .stderr(Stdio::inherit())
186 .spawn()
187 .unwrap();
188
189 let status = child.wait().unwrap();
190 println!("SSH process existed with: {}", status);
191 Ok(())
192}