From b070e6c4c08f431899108e7603155b43ae28c38e Mon Sep 17 00:00:00 2001 From: Max batleforc Date: Sat, 4 Jan 2025 16:41:00 +0100 Subject: [PATCH] feat: working open vscode ide and check install extension --- Cargo.lock | 2 + apps/dev_cli/Cargo.toml | 2 + apps/dev_cli/src/code/mod.rs | 158 +++++++++++++++++++++++++++++++++- apps/dev_cli/src/main.rs | 4 +- libs/helper/src/lib.rs | 1 + libs/helper/src/select_pod.rs | 34 ++++++++ 6 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 libs/helper/src/select_pod.rs diff --git a/Cargo.lock b/Cargo.lock index 214e850..a91ddee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,6 +444,7 @@ name = "dev_cli" version = "0.1.0" dependencies = [ "clap", + "crd", "devfile", "helper", "k8s-openapi", @@ -451,6 +452,7 @@ dependencies = [ "tokio", "tool_tracing", "tracing", + "vscode", ] [[package]] diff --git a/apps/dev_cli/Cargo.toml b/apps/dev_cli/Cargo.toml index 51eefdb..d57c3c1 100644 --- a/apps/dev_cli/Cargo.toml +++ b/apps/dev_cli/Cargo.toml @@ -9,6 +9,8 @@ default-run = "dev_cli" helper = { path = "../../libs/helper" } tool_tracing = { path = "../../libs/tool_tracing" } devfile = { path = "../../libs/devfile" } +vscode = { path = "../../libs/vscode" } +crd = { path = "../../libs/crd" } tokio = { workspace = true } tracing = { workspace = true } k8s-openapi = { workspace = true } diff --git a/apps/dev_cli/src/code/mod.rs b/apps/dev_cli/src/code/mod.rs index be284e1..443bc29 100644 --- a/apps/dev_cli/src/code/mod.rs +++ b/apps/dev_cli/src/code/mod.rs @@ -1,6 +1,13 @@ use clap::Subcommand; +use crd::dev_work_space::DevWorkspace; +use devfile::lifecycle::{ + ask_if_pod_should_up::ask_if_pod_should_up, find_pod_by_ws_name::find_pod_by_ws_name, + start_stop::start_stop_devworkspace, wait_for_status::wait_for_status, +}; +use helper::{select_pod::select_pod, Helper}; +use vscode::{extensions::Extensions, healthcheck, open_code}; -#[derive(Subcommand)] +#[derive(Subcommand, Debug)] #[command( name = "Code", about = "Handle the code subcommand", @@ -9,7 +16,7 @@ use clap::Subcommand; pub enum Code { /// Open the selected workspace in vscode Open { - /// The name of the container to spawn the vscode in + /// The name of the container to spawn the vscode in, by default it will use the first one #[arg(long)] name: Option, @@ -43,5 +50,150 @@ pub enum Code { impl Code { /// Run the subcommand - pub async fn run(&self) {} + #[tracing::instrument(level = "trace")] + pub async fn run(&self) { + match self { + Code::Open { + name, + port, + path, + workspace_name, + namespace, + context, + } => { + Self::open( + name.clone(), + *port, + path.clone(), + workspace_name.clone(), + namespace.clone(), + context.clone(), + ) + .await; + } + Code::Check { install } => { + Self::check(*install).await; + } + } + } + + #[tracing::instrument(level = "trace")] + pub async fn check(install: bool) { + // Check if the needed extensions are installed + let extensions = Extensions::new(); + let missing_extensions = extensions.check_missing_extensions(); + match missing_extensions { + Ok(missing_extensions) => { + if missing_extensions.is_empty() { + tracing::info!("All mandatory extensions are installed"); + } else { + tracing::info!("Missing extensions: {:?}", missing_extensions); + if install { + for extension in missing_extensions { + tracing::info!("Installing extension: {}", extension); + match extensions.install_extension(&extension) { + Ok(_) => { + tracing::info!("Extension {} installed", extension); + } + Err(err) => { + tracing::error!("Error: {:?}", err); + } + }; + } + } + } + } + Err(err) => { + tracing::error!("Error: {:?}", err); + } + } + } + + #[tracing::instrument(level = "trace")] + pub async fn open( + name: Option, + port: u16, + path: String, + workspace_name: String, + namespace: Option, + context: Option, + ) { + tracing::info!("Opening workspace {} in vscode, please dont kill this terminal or the workspaces will close itself due to inactivity", workspace_name); + let client = match Helper::get_client().await { + Some(client) => client, + None => return, + }; + let pod = match find_pod_by_ws_name( + client.clone(), + Some(workspace_name.clone()), + namespace.clone(), + ) + .await + { + Some(pod) => pod, + None => { + if ask_if_pod_should_up().await { + start_stop_devworkspace( + client.clone(), + workspace_name.clone(), + namespace.clone(), + true, + ) + .await; + if wait_for_status( + Helper::get_api::(client.clone(), namespace.clone()), + workspace_name.clone(), + "Running".to_string(), + 2000, + 150, // Fail after 5 minutes + ) + .await + .is_none() + { + return; + } + match find_pod_by_ws_name( + client.clone(), + Some(workspace_name.clone()), + namespace, + ) + .await + { + Some(pod) => pod, + None => return, + } + } else { + return; + } + } + }; + let container_name = match select_pod(name, pod.clone()) { + Some(container_name) => container_name, + None => return, + }; + + let open_code = open_code::OpenCode { + context, + pod_name: Some(pod.metadata.name.clone().unwrap()), + namespace: Some(pod.metadata.namespace.clone().unwrap()), + container_name: Some(container_name), + container_image: Some( + pod.spec.clone().unwrap().containers[0] + .image + .clone() + .unwrap(), + ), + path: Some(path), + }; + tracing::info!("Opening VsCode"); + open_code.open(); + tracing::info!("Healthcheck started"); + healthcheck::healthcheck( + client, + pod.metadata.name.unwrap(), + port, + pod.metadata.namespace, + ) + .await; + } } diff --git a/apps/dev_cli/src/main.rs b/apps/dev_cli/src/main.rs index 1cfd7df..3eef75e 100644 --- a/apps/dev_cli/src/main.rs +++ b/apps/dev_cli/src/main.rs @@ -76,8 +76,8 @@ async fn main() { println!("Namespace: {:?}", namespace); println!("Workspace name: {:?}", workspace_name); } - Some(Commands::Code { code: _ }) => { - println!("Code command"); + Some(Commands::Code { code }) => { + code.as_ref().unwrap().run().await; } None => tracing::info!("No command provided"), }; diff --git a/libs/helper/src/lib.rs b/libs/helper/src/lib.rs index af36ad9..5f600dd 100644 --- a/libs/helper/src/lib.rs +++ b/libs/helper/src/lib.rs @@ -7,6 +7,7 @@ use std::collections::HashMap; use tokio::io::AsyncReadExt; pub mod error; +pub mod select_pod; #[derive(Clone, Debug)] pub struct Helper {} diff --git a/libs/helper/src/select_pod.rs b/libs/helper/src/select_pod.rs new file mode 100644 index 0000000..c0ad019 --- /dev/null +++ b/libs/helper/src/select_pod.rs @@ -0,0 +1,34 @@ +use k8s_openapi::api::core::v1::Pod; + +#[tracing::instrument(level = "trace")] +pub fn select_pod(target_name: Option, pod: Pod) -> Option { + match target_name { + Some(container_named) => { + if !pod + .spec + .clone() + .unwrap() + .containers + .into_iter() + .any(|c| c.name == container_named) + { + tracing::error!( + "Pod does not have container : {}", + container_named.to_string() + ); + return None; + } + Some(container_named) + } + None => match pod.spec.unwrap().containers.first() { + Some(container) => { + tracing::info!("Using first container : {}", container.name.to_string()); + Some(container.name.to_string()) + } + None => { + tracing::error!("No container in the pod ? what did you do?"); + return None; + } + }, + } +}