mesa/node/
console.rs

1use core::time;
2
3use k8s_openapi::api::core::v1::Pod;
4use kube::{
5    api::{AttachParams, AttachedProcess},
6    Api,
7};
8use serde_json::Value;
9use tokio_stream::StreamExt;
10use tokio_util::io::ReaderStream;
11
12use crate::common::{
13    kubernetes::{self, get_k8s_client_programmatically},
14    vault::http_client::fetch_shasta_k8s_secrets,
15};
16
17pub async fn get_container_attachment_to_conman(
18    xname: &String,
19    vault_base_url: &str,
20    vault_secret_path: &str,
21    vault_role_id: &str,
22    k8s_api_url: &str,
23) -> AttachedProcess {
24    log::info!("xname: {}", xname);
25    let shasta_k8s_secrets =
26        fetch_shasta_k8s_secrets(vault_base_url, vault_secret_path, vault_role_id).await;
27
28    let client = get_k8s_client_programmatically(k8s_api_url, shasta_k8s_secrets)
29        .await
30        .unwrap();
31
32    let pods_fabric: Api<Pod> = Api::namespaced(client, "services");
33
34    let params = kube::api::ListParams::default()
35        .limit(1)
36        .labels("app.kubernetes.io/name=cray-console-operator");
37
38    let pods_objects = pods_fabric.list(&params).await.unwrap();
39
40    let console_operator_pod = &pods_objects.items[0];
41    let console_operator_pod_name = console_operator_pod.metadata.name.clone().unwrap();
42
43    log::info!("Console operator pod name '{}'", console_operator_pod_name);
44
45    let mut attached = pods_fabric
46        .exec(
47            &console_operator_pod_name,
48            vec!["sh", "-c", &format!("/app/get-node {}", xname)],
49            &AttachParams::default()
50                .container("cray-console-operator")
51                .stderr(false),
52        )
53        .await
54        .unwrap();
55
56    let mut stdout_stream = ReaderStream::new(attached.stdout().unwrap());
57    let next_stdout = stdout_stream.next().await.unwrap().unwrap();
58    let stdout_str = std::str::from_utf8(&next_stdout).unwrap();
59    let output_json: Value = serde_json::from_str(stdout_str).unwrap();
60
61    let console_pod_name = output_json["podname"].as_str().unwrap();
62
63    let command = vec!["conman", "-j", xname]; // Enter the container and open conman to access node's console
64                                               // let command = vec!["bash"]; // Enter the container and open bash to start an interactive
65                                               // terminal session
66
67    log::info!("Console pod name: {}", console_pod_name,);
68
69    log::info!("Connecting to console {}", xname);
70
71    let attachment_rslt = pods_fabric
72        .exec(
73            console_pod_name,
74            command,
75            &AttachParams::default()
76                .container("cray-console-node")
77                .stdin(true)
78                .stdout(true)
79                .stderr(false) // Note to self: tty and stderr cannot both be true
80                .tty(true),
81        )
82        .await;
83
84    if let Ok(attachment) = attachment_rslt {
85        attachment
86    } else {
87        eprintln!(
88            "Error attaching to container 'cray-console-node' in pod '{}'. Exit",
89            console_pod_name
90        );
91        std::process::exit(1);
92    }
93}
94
95pub async fn get_container_attachment_to_cfs_session_image_target(
96    cfs_session_name: &str,
97    vault_base_url: &str,
98    vault_secret_path: &str,
99    vault_role_id: &str,
100    k8s_api_url: &str,
101) -> AttachedProcess {
102    let shasta_k8s_secrets =
103        fetch_shasta_k8s_secrets(vault_base_url, vault_secret_path, vault_role_id).await;
104
105    let client = get_k8s_client_programmatically(k8s_api_url, shasta_k8s_secrets)
106        .await
107        .unwrap();
108
109    let pods_fabric: Api<Pod> = Api::namespaced(client.clone(), "services");
110
111    let params = kube::api::ListParams::default()
112        .limit(1)
113        .labels(format!("cfsession={}", cfs_session_name).as_str());
114
115    let mut pods = pods_fabric.list(&params).await.unwrap();
116
117    let mut i = 0;
118    let max = 30;
119
120    // Waiting for pod to start
121    while pods.items.is_empty() && i <= max {
122        println!(
123            "Pod for cfs session {} not ready. Trying again in 2 secs. Attempt {} of {}",
124            cfs_session_name,
125            i + 1,
126            max
127        );
128        i += 1;
129        tokio::time::sleep(time::Duration::from_secs(2)).await;
130        pods = pods_fabric.list(&params).await.unwrap();
131    }
132
133    if pods.items.is_empty() {
134        eprintln!(
135            "Pod for cfs session {} not ready. Aborting operation",
136            cfs_session_name
137        );
138        std::process::exit(1);
139    }
140
141    let console_operator_pod = &pods.items[0].clone();
142
143    let console_operator_pod_name = console_operator_pod.metadata.name.clone().unwrap();
144
145    log::info!("Ansible pod name: {}", console_operator_pod_name);
146
147    let attached = pods_fabric
148        .exec(
149            &console_operator_pod_name,
150            vec![
151                "sh",
152                "-c",
153                "cat /inventory/hosts/01-cfs-generated.yaml | grep cray-ims- | head -n 1",
154            ],
155            &AttachParams::default().container("ansible").stderr(false),
156        )
157        .await
158        .unwrap();
159
160    let mut output = kubernetes::get_output(attached).await;
161    log::info!("{output}");
162
163    output = output.trim().to_string();
164
165    log::info!("{output}");
166
167    output = output.strip_prefix("ansible_host: ").unwrap().to_string();
168
169    output = output
170        .strip_suffix("-service.ims.svc.cluster.local")
171        .unwrap()
172        .to_string();
173
174    log::info!("{output}");
175
176    let ansible_target_container_label = output + "-customize";
177
178    log::info!("{ansible_target_container_label}");
179
180    // Find ansible target container
181
182    let pods_fabric: Api<Pod> = Api::namespaced(client, "ims");
183
184    let params = kube::api::ListParams::default()
185        .limit(1)
186        .labels(format!("job-name={}", ansible_target_container_label).as_str());
187
188    let mut pods = pods_fabric.list(&params).await.unwrap();
189
190    let mut i = 0;
191    let max = 30;
192
193    // Waiting for pod to start
194    while pods.items.is_empty() && i <= max {
195        println!(
196            "Pod for cfs session {} not ready. Trying again in 2 secs. Attempt {} of {}",
197            cfs_session_name,
198            i + 1,
199            max
200        );
201        i += 1;
202        tokio::time::sleep(time::Duration::from_secs(2)).await;
203        pods = pods_fabric.list(&params).await.unwrap();
204    }
205
206    if pods.items.is_empty() {
207        eprintln!(
208            "Pod for cfs session {} not ready. Aborting operation",
209            cfs_session_name
210        );
211        std::process::exit(1);
212    }
213
214    let console_operator_pod = &pods.items[0].clone();
215
216    log::info!("Connecting to console ansible target container");
217
218    let console_operator_pod_name = console_operator_pod.metadata.name.clone().unwrap();
219
220    let command = vec!["bash"]; // Enter the container and open conman to access node's console
221                                // let command = vec!["bash"]; // Enter the container and open bash to start an interactive
222                                // terminal session
223
224    let attachment_rslt = pods_fabric
225        .exec(
226            &console_operator_pod_name,
227            command,
228            &AttachParams::default()
229                .container("sshd")
230                .stdin(true)
231                .stdout(true)
232                .stderr(false) // Note to self: tty and stderr cannot both be true
233                .tty(true),
234        )
235        .await;
236
237    if let Ok(attachment) = attachment_rslt {
238        attachment
239    } else {
240        eprintln!(
241            "Error attaching to container 'sshd' in pod '{}'. Exit",
242            console_operator_pod_name
243        );
244        std::process::exit(1);
245    }
246}