Skip to content

Commit b224216

Browse files
Merge branch 'PreReleaseMain'
2 parents 160b377 + 526ccdf commit b224216

6 files changed

Lines changed: 338 additions & 194 deletions

File tree

cli/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cortexflow-cli"
3-
version = "0.1.4"
3+
version = "0.1.4-bf1"
44
edition = "2024"
55
description = "CortexFlow command line interface made to interact with the CortexBrain core components"
66
authors = ["Lorenzo Tettamanti", "Pranav Verma", "Lorenzo Bradanini","Siddharth Sutar","Andrea Bozzo"]

cli/src/essential.rs

Lines changed: 179 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
use std::{ collections::BTreeMap, fmt, process::Command, result::Result::Ok };
1+
use std::borrow::Cow;
2+
use std::process::Output;
3+
use std::thread;
4+
use std::time::Duration;
5+
use std::{collections::BTreeMap, fmt, process::Command, result::Result::Ok};
26

7+
use anyhow::Error;
8+
use colored::Colorize;
9+
use k8s_openapi::apimachinery::pkg::version;
310
use kube::core::ErrorResponse;
411
use serde::Serialize;
5-
use colored::Colorize;
612

713
use k8s_openapi::api::core::v1::ConfigMap;
814
use k8s_openapi::serde_json::json;
9-
use kube::api::{ Api, ObjectMeta, Patch, PatchParams, PostParams };
15+
use kube::api::{Api, ObjectMeta, Patch, PatchParams, PostParams};
1016
use kube::client::Client;
1117

1218
pub static BASE_COMMAND: &str = "kubectl"; // docs: Kubernetes base command
@@ -41,17 +47,11 @@ pub static BASE_COMMAND: &str = "kubectl"; // docs: Kubernetes base command
4147

4248
#[derive(Debug)]
4349
pub enum CliError {
44-
InstallerError {
45-
reason: String,
46-
},
50+
InstallerError { reason: String },
4751
ClientError(kube::Error),
48-
UninstallError {
49-
reason: String,
50-
},
52+
UninstallError { reason: String },
5153
AgentError(tonic_reflection::server::Error),
52-
MonitoringError {
53-
reason: String,
54-
},
54+
MonitoringError { reason: String },
5555
}
5656
// docs:
5757
// error type conversions
@@ -63,12 +63,14 @@ impl From<kube::Error> for CliError {
6363
}
6464
impl From<anyhow::Error> for CliError {
6565
fn from(e: anyhow::Error) -> Self {
66-
CliError::MonitoringError { reason: format!("{}", e) }
66+
CliError::MonitoringError {
67+
reason: format!("{}", e),
68+
}
6769
}
6870
}
6971
impl From<()> for CliError {
70-
fn from (v: ()) -> Self{
71-
return ().into()
72+
fn from(v: ()) -> Self {
73+
return ().into();
7274
}
7375
}
7476

@@ -142,26 +144,137 @@ pub async fn connect_to_client() -> Result<Client, kube::Error> {
142144
// Returns an error if the command fails
143145

144146
pub fn update_cli() {
147+
let latest_version = get_latest_cfcli_version().expect("Can't get the latest version");
145148
println!("{} {}", "=====>".blue().bold(), "Updating CortexFlow CLI");
146-
println!("{} {}", "=====>".blue().bold(), "Looking for a newer version");
149+
println!(
150+
"{} {}",
151+
"=====>".blue().bold(),
152+
"Looking for a newer version \n "
153+
);
147154

148-
let output = Command::new("cargo").args(["update", "cortexflow-cli"]).output().expect("error");
155+
let output = Command::new("cfcli")
156+
.args(["--version"])
157+
.output()
158+
.expect("error");
149159

150160
if !output.status.success() {
151-
eprintln!("Error updating CLI : {}", String::from_utf8_lossy(&output.stderr));
161+
eprintln!(
162+
"Error extracting the version : {}",
163+
String::from_utf8_lossy(&output.stderr)
164+
);
152165
} else {
153-
println!("✅ Updated CLI");
166+
// extract the cli version:
167+
let version = String::from_utf8_lossy(&output.stdout)
168+
.split_whitespace()
169+
.last()
170+
.expect("An error occured during the version extraction")
171+
.to_string();
172+
173+
if version == latest_version {
174+
println!(
175+
"{} {} {} {} {} {}{}",
176+
"=====>".blue().bold(),
177+
"Your version".green().bold(),
178+
(&version.to_string()).green().bold(),
179+
"is already up to date".green().bold(),
180+
"(latest:".green().bold(),
181+
(&latest_version).green().bold(),
182+
")\n".green().bold()
183+
);
184+
} else {
185+
println!(
186+
"{} {} {} {} {} {}{}",
187+
"=====>".blue().bold(),
188+
"Your version".red().bold(),
189+
(&version.to_string()).red().bold(),
190+
"needs to be updated".red().bold(),
191+
"(latest:".red().bold(),
192+
(&latest_version).red().bold(),
193+
")\n".red().bold()
194+
);
195+
thread::sleep(Duration::from_secs(1));
196+
println!("{} {}", "=====>".blue().bold(), "Updating the CLI...");
197+
let update_command = Command::new("cargo")
198+
.args(["install", "cortexflow-cli", "--force"])
199+
.output()
200+
.expect("error");
201+
if !update_command.status.success() {
202+
eprintln!(
203+
"Error updating the CLI: {} ",
204+
String::from_utf8_lossy(&update_command.stderr)
205+
);
206+
} else {
207+
println!(
208+
"{} {}",
209+
"=====>".blue().bold(),
210+
"CLI updated".green().bold()
211+
)
212+
}
213+
}
214+
}
215+
}
216+
217+
// docs:
218+
//
219+
// This function returns the latest version of the CLI from the crates.io registry
220+
pub fn get_latest_cfcli_version() -> Result<String, Error> {
221+
let output = Command::new("cargo")
222+
.args(["search", "cortexflow-cli", "--limit", "1"])
223+
.output()
224+
.expect("Error");
225+
226+
if !output.status.success() {
227+
return Err(Error::msg(format!(
228+
"An error occured during the latest version extraction"
229+
)));
230+
} else {
231+
let command_stdout = String::from_utf8_lossy(&output.stdout);
232+
233+
// here the data output have this structure:
234+
// cortexflow-cli = "0.1.4" # CortexFlow command line interface made to interact with the CortexBrain core components
235+
// ... and 3 crates more (use --limit N to see more)
236+
237+
// i need to extract only the version tag
238+
let version = extract_version_from_output(command_stdout);
239+
240+
Ok(version)
154241
}
155242
}
156243

244+
// docs:
245+
// this is an helper function used in a unit test
246+
//
247+
// Takes a Clone-On-Write (Cow) smart pointer (the same type returned by the String::from_utf8_lossy(&output.stdout) code )
248+
// and returns a String that contains the cfcli version
249+
250+
fn extract_version_from_output(command_stdout: Cow<'_, str>) -> String {
251+
let version = command_stdout.split('"').nth(1).unwrap().to_string();
252+
version
253+
}
254+
157255
// docs:
158256
//
159257
// This is a function to display the CLI Version,Author and Description using a fancy output style
160258

161259
pub fn info() {
162-
println!("{} {} {}", "=====>".blue().bold(), "Version:", env!("CARGO_PKG_VERSION"));
163-
println!("{} {} {}", "=====>".blue().bold(), "Author:", env!("CARGO_PKG_AUTHORS"));
164-
println!("{} {} {}", "=====>".blue().bold(), "Description:", env!("CARGO_PKG_DESCRIPTION"));
260+
println!(
261+
"{} {} {}",
262+
"=====>".blue().bold(),
263+
"Version:",
264+
env!("CARGO_PKG_VERSION")
265+
);
266+
println!(
267+
"{} {} {}",
268+
"=====>".blue().bold(),
269+
"Author:",
270+
env!("CARGO_PKG_AUTHORS")
271+
);
272+
println!(
273+
"{} {} {}",
274+
"=====>".blue().bold(),
275+
"Description:",
276+
env!("CARGO_PKG_DESCRIPTION")
277+
);
165278
}
166279

167280
// docs:
@@ -210,18 +323,12 @@ pub async fn read_configs() -> Result<Vec<String>, CliError> {
210323

211324
Ok(Vec::new()) //in case the key fails
212325
}
213-
Err(_) => {
214-
Err(
215-
CliError::ClientError(
216-
kube::Error::Api(ErrorResponse {
217-
status: "failed".to_string(),
218-
message: "Failed to connect to kubernetes client".to_string(),
219-
reason: "Your cluster is probably disconnected".to_string(),
220-
code: 404,
221-
})
222-
)
223-
)
224-
}
326+
Err(_) => Err(CliError::ClientError(kube::Error::Api(ErrorResponse {
327+
status: "failed".to_string(),
328+
message: "Failed to connect to kubernetes client".to_string(),
329+
reason: "Your cluster is probably disconnected".to_string(),
330+
code: 404,
331+
}))),
225332
}
226333
}
227334

@@ -271,18 +378,12 @@ pub async fn create_config_file(config_struct: MetadataConfigFile) -> Result<(),
271378
}
272379
Ok(())
273380
}
274-
Err(_) => {
275-
Err(
276-
CliError::ClientError(
277-
kube::Error::Api(ErrorResponse {
278-
status: "failed".to_string(),
279-
message: "Failed to connect to kubernetes client".to_string(),
280-
reason: "Your cluster is probably disconnected".to_string(),
281-
code: 404,
282-
})
283-
)
284-
)
285-
}
381+
Err(_) => Err(CliError::ClientError(kube::Error::Api(ErrorResponse {
382+
status: "failed".to_string(),
383+
message: "Failed to connect to kubernetes client".to_string(),
384+
reason: "Your cluster is probably disconnected".to_string(),
385+
code: 404,
386+
}))),
286387
}
287388
}
288389

@@ -350,21 +451,20 @@ pub async fn update_configmap(config_struct: MetadataConfigFile) -> Result<(), C
350451
let name = "cortexbrain-client-config";
351452
let api: Api<ConfigMap> = Api::namespaced(client, namespace);
352453

353-
let blocklist_yaml = config_struct.blocklist
454+
let blocklist_yaml = config_struct
455+
.blocklist
354456
.iter()
355457
.map(|x| format!("{}", x))
356458
.collect::<Vec<String>>()
357459
.join("\n");
358460

359-
let patch = Patch::Apply(
360-
json!({
361-
"apiVersion": "v1",
362-
"kind": "ConfigMap",
363-
"data": {
364-
"blocklist": blocklist_yaml
365-
}
366-
})
367-
);
461+
let patch = Patch::Apply(json!({
462+
"apiVersion": "v1",
463+
"kind": "ConfigMap",
464+
"data": {
465+
"blocklist": blocklist_yaml
466+
}
467+
}));
368468

369469
let patch_params = PatchParams::apply("cortexbrain").force();
370470
match api.patch(name, &patch_params, &patch).await {
@@ -379,17 +479,28 @@ pub async fn update_configmap(config_struct: MetadataConfigFile) -> Result<(), C
379479

380480
Ok(())
381481
}
382-
Err(_) => {
383-
Err(
384-
CliError::ClientError(
385-
kube::Error::Api(ErrorResponse {
386-
status: "failed".to_string(),
387-
message: "Failed to connect to kubernetes client".to_string(),
388-
reason: "Your cluster is probably disconnected".to_string(),
389-
code: 404,
390-
})
391-
)
392-
)
393-
}
482+
Err(_) => Err(CliError::ClientError(kube::Error::Api(ErrorResponse {
483+
status: "failed".to_string(),
484+
message: "Failed to connect to kubernetes client".to_string(),
485+
reason: "Your cluster is probably disconnected".to_string(),
486+
code: 404,
487+
}))),
488+
}
489+
}
490+
491+
#[cfg(test)]
492+
mod tests {
493+
use crate::essential::extract_version_from_output;
494+
495+
#[test]
496+
fn test_version_extraction() {
497+
let command_stdout = String::from(
498+
r#"cortexflow-cli = "0.1.4-test_123"
499+
# CortexFlow command line interface made to interact with the CortexBrain core components...
500+
and 3 crates more (use --limit N to see more)"#,
501+
);
502+
503+
let extracted_command = extract_version_from_output(command_stdout.into());
504+
assert_eq!(extracted_command, "0.1.4-test_123");
394505
}
395506
}

0 commit comments

Comments
 (0)