From 9d3a0556a5e9e27272e15b7e7f29f070889acab5 Mon Sep 17 00:00:00 2001 From: Adarsh Shah Date: Sun, 10 Dec 2023 17:47:35 -0500 Subject: [PATCH 1/4] adds support for updating instance --- tembo-cli/src/cli/context.rs | 8 ++ tembo-cli/src/cmd/apply.rs | 143 +++++++++++++++++++++++++++++------ tembo-cli/src/cmd/init.rs | 24 +++++- 3 files changed, 149 insertions(+), 26 deletions(-) diff --git a/tembo-cli/src/cli/context.rs b/tembo-cli/src/cli/context.rs index 611e4bec7..cdb89ad4d 100644 --- a/tembo-cli/src/cli/context.rs +++ b/tembo-cli/src/cli/context.rs @@ -88,6 +88,14 @@ pub fn tembo_credentials_file_path() -> String { tembo_home_dir() + "/credentials" } +pub fn dot_tembo_folder() -> String { + ".tembo".to_string() +} + +pub fn tembo_state_file_path() -> String { + dot_tembo_folder() + "/tembo.state" +} + pub fn list_context() -> Result { let filename = tembo_context_file_path(); diff --git a/tembo-cli/src/cmd/apply.rs b/tembo-cli/src/cmd/apply.rs index 43cdd6428..f04db3c6b 100644 --- a/tembo-cli/src/cmd/apply.rs +++ b/tembo-cli/src/cmd/apply.rs @@ -1,21 +1,25 @@ use crate::{ cli::{ - context::{get_current_context, Environment, Target}, + context::{get_current_context, tembo_state_file_path, Environment, Target}, tembo_config, }, Result, }; use clap::{ArgMatches, Command}; +use std::io::Write; use std::{ collections::HashMap, - fs::{self}, + fs::{self, OpenOptions}, str::FromStr, }; use temboclient::{ - apis::{configuration::Configuration, instance_api::create_instance}, + apis::{ + configuration::Configuration, + instance_api::{create_instance, put_instance}, + }, models::{ Cpu, CreateInstance, Extension, ExtensionInstallLocation, Memory, PgConfig, StackType, - Storage, TrunkInstall, + Storage, TrunkInstall, UpdateInstance, }, }; use tokio::runtime::Runtime; @@ -86,39 +90,106 @@ fn execute_docker() -> Result<()> { pub fn execute_tembo_cloud(env: Environment) -> Result<()> { let instance_settings: HashMap = get_instance_settings()?; - let profile = env.selected_profile.unwrap(); + let profile = env.clone().selected_profile.unwrap(); let config = Configuration { base_path: profile.tembo_host, bearer_access_token: Some(profile.tembo_access_token), ..Default::default() }; - let mut instance: CreateInstance; - for (_key, value) in instance_settings.iter() { - instance = get_instance(value); - - let v = Runtime::new().unwrap().block_on(create_instance( - &config, - env.org_id.clone().unwrap().as_str(), - instance, - )); - - match v { - Ok(result) => { - println!( - "Instance creation started for Instance Name: {}", - result.instance_name - ) - } - Err(error) => eprintln!("Error creating instance: {}", error), - }; + let instance_id = get_instance_id_from_state(value.instance_name.clone())?; + if let Some(env_instance_id) = instance_id { + update_existing_instance(env_instance_id, value, &config, env.clone()); + } else { + create_new_instance(value, &config, env.clone()); + } } Ok(()) } -fn get_instance(instance_settings: &InstanceSettings) -> CreateInstance { +fn get_instance_id_from_state(instance_name: String) -> Result> { + let contents = match fs::read_to_string(tembo_state_file_path()) { + Ok(c) => c, + Err(e) => { + panic!( + "Couldn't read context file {}: {}", + tembo_state_file_path(), + e + ); + } + }; + + let tembo_state_map: HashMap = toml::from_str(&contents)?; + + let tembo_state = tembo_state_map.get(&instance_name); + if tembo_state.is_none() { + Ok(None) + } else { + let instance_id = tembo_state.unwrap().clone(); + Ok(Some(instance_id)) + } +} + +fn update_existing_instance( + instance_id: String, + value: &InstanceSettings, + config: &Configuration, + env: Environment, +) { + let instance = get_update_instance(value); + + let v = Runtime::new().unwrap().block_on(put_instance( + config, + env.org_id.clone().unwrap().as_str(), + &instance_id, + instance, + )); + + match v { + Ok(result) => { + println!( + "Instance update started for Instance Id: {}", + result.instance_id + ) + } + Err(error) => eprintln!("Error updating instance: {}", error), + }; +} + +fn create_new_instance(value: &InstanceSettings, config: &Configuration, env: Environment) { + let instance = get_create_instance(value); + + let v = Runtime::new().unwrap().block_on(create_instance( + config, + env.org_id.clone().unwrap().as_str(), + instance, + )); + + match v { + Ok(result) => { + println!( + "Instance creation started for instance_name: {} with instance_id: {}", + result.instance_name, result.instance_id + ); + + let mut state_file = OpenOptions::new() + .append(true) + .open(tembo_state_file_path()) + .expect("cannot open file"); + + let state = format!("{} = \"{}\"\n", result.instance_name, result.instance_id); + + state_file + .write_all(state.as_bytes()) + .expect("write failed"); + } + Err(error) => eprintln!("Error creating instance: {}", error), + }; +} + +fn get_create_instance(instance_settings: &InstanceSettings) -> CreateInstance { return CreateInstance { cpu: Cpu::from_str(instance_settings.cpu.as_str()).unwrap(), memory: Memory::from_str(instance_settings.memory.as_str()).unwrap(), @@ -142,6 +213,28 @@ fn get_instance(instance_settings: &InstanceSettings) -> CreateInstance { }; } +fn get_update_instance(instance_settings: &InstanceSettings) -> UpdateInstance { + return UpdateInstance { + cpu: Cpu::from_str(instance_settings.cpu.as_str()).unwrap(), + memory: Memory::from_str(instance_settings.memory.as_str()).unwrap(), + environment: temboclient::models::Environment::from_str( + instance_settings.environment.as_str(), + ) + .unwrap(), + storage: Storage::from_str(instance_settings.storage.as_str()).unwrap(), + replicas: instance_settings.replicas, + app_services: None, + connection_pooler: None, + extensions: Some(Some(get_extensions(instance_settings.extensions.clone()))), + extra_domains_rw: None, + ip_allow_list: None, + trunk_installs: Some(Some(get_trunk_installs( + instance_settings.extensions.clone(), + ))), + postgres_configs: Some(Some(get_postgres_config_cloud(instance_settings))), + }; +} + fn get_postgres_config_cloud(instance_settings: &InstanceSettings) -> Vec { let mut pg_configs: Vec = vec![]; diff --git a/tembo-cli/src/cmd/init.rs b/tembo-cli/src/cmd/init.rs index 707d5e2d6..65d253517 100644 --- a/tembo-cli/src/cmd/init.rs +++ b/tembo-cli/src/cmd/init.rs @@ -1,4 +1,7 @@ -use crate::Result; +use crate::{ + cli::context::{dot_tembo_folder, tembo_state_file_path}, + Result, +}; use clap::{ArgMatches, Command}; use crate::cli::{ @@ -66,5 +69,24 @@ pub fn execute(_args: &ArgMatches) -> Result<()> { } } + match FileUtils::create_dir(".tembo directory".to_string(), dot_tembo_folder()) { + Ok(t) => t, + Err(e) => { + return Err(e); + } + } + + match FileUtils::create_file( + tembo_state_file_path(), + tembo_state_file_path(), + "".to_string(), + false, + ) { + Ok(t) => t, + Err(e) => { + return Err(e); + } + } + Ok(()) } From 417692bb82fd8c27a1c55c52e8b06d0e6c7a6177 Mon Sep 17 00:00:00 2001 From: Adarsh Shah Date: Sun, 10 Dec 2023 18:06:43 -0500 Subject: [PATCH 2/4] adds support for deleting instance --- tembo-cli/src/cmd/apply.rs | 2 +- tembo-cli/src/cmd/delete.rs | 58 +++++++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/tembo-cli/src/cmd/apply.rs b/tembo-cli/src/cmd/apply.rs index f04db3c6b..1a498b2b0 100644 --- a/tembo-cli/src/cmd/apply.rs +++ b/tembo-cli/src/cmd/apply.rs @@ -109,7 +109,7 @@ pub fn execute_tembo_cloud(env: Environment) -> Result<()> { Ok(()) } -fn get_instance_id_from_state(instance_name: String) -> Result> { +pub fn get_instance_id_from_state(instance_name: String) -> Result> { let contents = match fs::read_to_string(tembo_state_file_path()) { Ok(c) => c, Err(e) => { diff --git a/tembo-cli/src/cmd/delete.rs b/tembo-cli/src/cmd/delete.rs index 2df243101..4cb6d853a 100644 --- a/tembo-cli/src/cmd/delete.rs +++ b/tembo-cli/src/cmd/delete.rs @@ -1,5 +1,19 @@ -use crate::{cli::docker::Docker, Result}; +use std::collections::HashMap; + +use crate::{ + cli::{ + context::{get_current_context, Environment, Target}, + docker::Docker, + tembo_config::InstanceSettings, + }, + Result, +}; use clap::{ArgMatches, Command}; +use core::result::Result::Ok; +use temboclient::apis::{configuration::Configuration, instance_api::delete_instance}; +use tokio::runtime::Runtime; + +use super::apply::{get_instance_id_from_state, get_instance_settings}; // Create init subcommand arguments pub fn make_subcommand() -> Command { @@ -7,7 +21,47 @@ pub fn make_subcommand() -> Command { } pub fn execute(_args: &ArgMatches) -> Result<()> { - Docker::stop_remove("tembo-pg")?; + let env = get_current_context()?; + + if env.target == Target::Docker.to_string() { + Docker::stop_remove("tembo-pg")?; + } else if env.target == Target::TemboCloud.to_string() { + return execute_tembo_cloud(env); + } + + Ok(()) +} + +fn execute_tembo_cloud(env: Environment) -> Result<()> { + let instance_settings: HashMap = get_instance_settings()?; + + let profile = env.clone().selected_profile.unwrap(); + let config = Configuration { + base_path: profile.tembo_host, + bearer_access_token: Some(profile.tembo_access_token), + ..Default::default() + }; + + for (_key, value) in instance_settings.iter() { + let instance_id = get_instance_id_from_state(value.instance_name.clone())?; + if let Some(env_instance_id) = instance_id { + let v = Runtime::new().unwrap().block_on(delete_instance( + &config, + env.org_id.clone().unwrap().as_str(), + &env_instance_id, + )); + + match v { + Ok(result) => { + println!( + "Instance delete started for Instance Id: {}", + result.instance_id + ) + } + Err(error) => eprintln!("Error deleting instance: {}", error), + }; + } + } Ok(()) } From 3879d2fe5a951a6043ec52853f79ee756c5ad355 Mon Sep 17 00:00:00 2001 From: Adarsh Shah Date: Sun, 10 Dec 2023 19:08:52 -0500 Subject: [PATCH 3/4] using ? instead of match --- tembo-cli/src/cmd/apply.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tembo-cli/src/cmd/apply.rs b/tembo-cli/src/cmd/apply.rs index 1a498b2b0..1b49b53d7 100644 --- a/tembo-cli/src/cmd/apply.rs +++ b/tembo-cli/src/cmd/apply.rs @@ -110,16 +110,7 @@ pub fn execute_tembo_cloud(env: Environment) -> Result<()> { } pub fn get_instance_id_from_state(instance_name: String) -> Result> { - let contents = match fs::read_to_string(tembo_state_file_path()) { - Ok(c) => c, - Err(e) => { - panic!( - "Couldn't read context file {}: {}", - tembo_state_file_path(), - e - ); - } - }; + let contents = fs::read_to_string(tembo_state_file_path())?; let tembo_state_map: HashMap = toml::from_str(&contents)?; From 8413ff0dedfe07a83320d6f6be7d7832c0107de4 Mon Sep 17 00:00:00 2001 From: Adarsh Shah Date: Sun, 10 Dec 2023 19:24:08 -0500 Subject: [PATCH 4/4] uses const instead of function for DOT_TEMBO_FOLDER --- tembo-cli/src/cli/context.rs | 6 ++---- tembo-cli/src/cmd/init.rs | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/tembo-cli/src/cli/context.rs b/tembo-cli/src/cli/context.rs index cdb89ad4d..78c2e9107 100644 --- a/tembo-cli/src/cli/context.rs +++ b/tembo-cli/src/cli/context.rs @@ -88,12 +88,10 @@ pub fn tembo_credentials_file_path() -> String { tembo_home_dir() + "/credentials" } -pub fn dot_tembo_folder() -> String { - ".tembo".to_string() -} +pub const DOT_TEMBO_FOLDER: &str = ".tembo"; pub fn tembo_state_file_path() -> String { - dot_tembo_folder() + "/tembo.state" + DOT_TEMBO_FOLDER.to_string() + "/tembo.state" } pub fn list_context() -> Result { diff --git a/tembo-cli/src/cmd/init.rs b/tembo-cli/src/cmd/init.rs index 65d253517..5a160a18e 100644 --- a/tembo-cli/src/cmd/init.rs +++ b/tembo-cli/src/cmd/init.rs @@ -1,5 +1,5 @@ use crate::{ - cli::context::{dot_tembo_folder, tembo_state_file_path}, + cli::context::{tembo_state_file_path, DOT_TEMBO_FOLDER}, Result, }; use clap::{ArgMatches, Command}; @@ -69,7 +69,7 @@ pub fn execute(_args: &ArgMatches) -> Result<()> { } } - match FileUtils::create_dir(".tembo directory".to_string(), dot_tembo_folder()) { + match FileUtils::create_dir(".tembo directory".to_string(), DOT_TEMBO_FOLDER.to_string()) { Ok(t) => t, Err(e) => { return Err(e);