From 8578c90b648bcbb24a2ea51e4611975d93cbcb70 Mon Sep 17 00:00:00 2001 From: Sascha Eglau Date: Tue, 20 Feb 2024 20:02:37 +0100 Subject: [PATCH] feat: add manifest support --- src/commands/services.rs | 121 ++++++++++++++++++++++++++++----------- src/main.rs | 2 +- 2 files changed, 87 insertions(+), 36 deletions(-) diff --git a/src/commands/services.rs b/src/commands/services.rs index 4529053..15927c9 100644 --- a/src/commands/services.rs +++ b/src/commands/services.rs @@ -2,6 +2,8 @@ use anyhow::{anyhow, Result}; use clap::{Parser, Subcommand}; use dialoguer::{FuzzySelect, Input}; use difference::{Difference, Changeset}; +use std::fs::File; +use std::io::Read; use std::io::Write; use std::path::Path; use super::CommandBase; @@ -65,6 +67,8 @@ pub struct Deploy { port: Option, #[arg(long, help = "Organization to deploy to")] org: Option, + #[arg(short, long, help = "Path to molnett manifest")] + manifest: Option, } impl Deploy { @@ -86,58 +90,43 @@ impl Deploy { &self.name ); - let existing_svc: Option; - if let Err(e) = response { - existing_svc = None; - if let Some(reqwest::StatusCode::NOT_FOUND) = e.status() { - // User needs to set every attribute if service does not exist yet - if self.image.is_none() || self.port.is_none() { - return Err(anyhow!("Image and port are mandatory if service does not exist")) + let existing_svc = match response { + Ok(svc) => svc, + Err(e) => match e.status() { + Some(reqwest::StatusCode::NOT_FOUND) => { + return self.create_new_service(base, token, &org_name) + }, + Some(reqwest::StatusCode::UNAUTHORIZED) => { + return Err(anyhow!("Unauthorized, please login first")) + }, + _ => { + return Err(anyhow!("Could not check whether service exists or not")) } - } else if let Some(reqwest::StatusCode::UNAUTHORIZED) = e.status() { - return Err(anyhow!("Unauthorized, please login first")) - } else { - return Err(anyhow!("Could not check whether service exists or not")) } - } else { - existing_svc = Some(response.unwrap()); - } + }; - let mut new_svc: Service; - if existing_svc.is_some() { - new_svc = existing_svc.clone().unwrap(); + let new_svc = if self.manifest.is_some() { + self.construct_service_from_args()? + } else { + let mut new_svc = existing_svc.clone(); if let Some(image) = &self.image { new_svc.image = image.to_string() } if let Some(port) = self.port { new_svc.container_port = port } - } else { - new_svc = Service { - name: self.name.clone(), - image: self.image.clone().unwrap(), - container_port: self.port.clone().unwrap() - } + new_svc }; if let Some(false) = self.no_confirm { - if existing_svc.is_some() && existing_svc.clone().unwrap() == new_svc { + if existing_svc == new_svc { println!("no changes detected"); return Ok(()) } - let existing_svc_yaml = if existing_svc.is_some() { - serde_yaml::to_string(&existing_svc)? - } else { - "".to_string() - }; + let existing_svc_yaml = serde_yaml::to_string(&existing_svc)?; let new_svc_yaml = serde_yaml::to_string(&new_svc)?; self.render_diff(existing_svc_yaml, new_svc_yaml)?; - let selection = FuzzySelect::with_theme(&dialoguer::theme::ColorfulTheme::default()) - .with_prompt("Do you want to apply the above changes?") - .items(&["no", "yes"]) - .default(0) - .interact() - .unwrap(); + let selection = self.user_confirmation(); if selection == 0 { println!("Cancelling..."); return Ok(()) @@ -149,6 +138,68 @@ impl Deploy { Ok(()) } + fn create_new_service(&self, base: &CommandBase, token: &str, org_name: &str) -> Result<()> { + let svc = self.construct_service_from_args()?; + + if let Some(false) = self.no_confirm { + let new_svc_yaml = serde_yaml::to_string(&svc)?; + self.render_diff("".to_string(), new_svc_yaml)?; + } + + let selection = self.user_confirmation(); + if selection == 0 { + println!("Cancelling..."); + return Ok(()) + } + + let result = base.api_client().deploy_service(token, org_name, &self.env, svc)?; + println!("Service {} deployed", result.name); + Ok(()) + } + + fn construct_service_from_args(&self) -> Result { + if self.manifest.is_some() { + if self.image.is_some() || self.port.is_some() { + return Err(anyhow!("CLI arguments for service attributes can not be used together with manifest")) + } + + let svc = self.read_manifest()?; + if self.name != svc.name { + return Err(anyhow!("Name given as CLI argument needs to match name in manifest")) + } + + return Ok(svc) + } else { + // User needs to set every attribute if service does not exist yet + if self.image.is_none() || self.port.is_none() { + return Err(anyhow!("Image and port are mandatory if service does not exist")) + } + + return Ok(Service { + name: self.name.clone(), + image: self.image.clone().unwrap(), + container_port: self.port.clone().unwrap() + }) + } + } + + fn user_confirmation(&self) -> usize { + FuzzySelect::with_theme(&dialoguer::theme::ColorfulTheme::default()) + .with_prompt("Do you want to apply the above changes?") + .items(&["no", "yes"]) + .default(0) + .interact() + .unwrap() + } + + fn read_manifest(&self) -> Result { + let file_path = self.manifest.clone().unwrap(); + let mut file_content = String::new(); + File::open(file_path)?.read_to_string(&mut file_content)?; + let svc = serde_yaml::from_str(&file_content)?; + Ok(svc) + } + fn render_diff(&self, a: String, b: String) -> Result<()> { let Changeset { diffs, .. } = Changeset::new(&a, &b, "\n"); let mut t = term::stdout().unwrap(); diff --git a/src/main.rs b/src/main.rs index f770ef6..21adb76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use anyhow::Result; use camino::Utf8PathBuf; use clap::{Parser, Subcommand}; use crate::config::user::UserConfig; -use commands::{CommandBase, environments}; +use commands::CommandBase; mod api; mod commands; mod config;