diff --git a/app/src/api/cmd.ts b/app/src/api/cmd.ts index 48090bfa..d4adb3fc 100644 --- a/app/src/api/cmd.ts +++ b/app/src/api/cmd.ts @@ -90,7 +90,9 @@ export type Cmd = | "GetAwsInstanceTypes" | "RestartContainer" | "RestartChildSwarmContainers" - | "GetSignedInUserDetails"; + | "GetSignedInUserDetails" + | "UpdateAwsInstanceType" + | "GetInstanceType"; interface CmdData { cmd: Cmd; diff --git a/app/src/api/swarm.ts b/app/src/api/swarm.ts index 80b6aaf0..22ccfb82 100644 --- a/app/src/api/swarm.ts +++ b/app/src/api/swarm.ts @@ -327,3 +327,24 @@ export async function create_new_swarm_ec2({ export async function get_aws_instance_types() { return await swarmCmd("GetAwsInstanceTypes"); } + +export async function update_aws_instance_type({ + instance_id, + instance_type, +}: { + instance_id: string; + instance_type: string; +}) { + return await swarmCmd("UpdateAwsInstanceType", { + instance_id, + instance_type, + }); +} + +export async function get_swarm_instance_type({ + instance_id, +}: { + instance_id: string; +}) { + return await swarmCmd("GetInstanceType", { instance_id }); +} diff --git a/src/bin/super/cmd.rs b/src/bin/super/cmd.rs index b28f923c..a840d7d4 100644 --- a/src/bin/super/cmd.rs +++ b/src/bin/super/cmd.rs @@ -77,6 +77,8 @@ pub enum SwarmCmd { RestartChildSwarmContainers(AccessNodesInfo), CreateNewEc2Instance(CreateEc2InstanceInfo), GetAwsInstanceTypes, + UpdateAwsInstanceType(UpdateInstanceDetails), + GetInstanceType(GetInstanceTypeByInstanceId), } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -84,6 +86,12 @@ pub struct ChildSwarmIdentifier { pub host: String, } +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct UpdateInstanceDetails { + pub instance_id: String, + pub instance_type: String, +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct AddSwarmResponse { pub success: bool, @@ -114,3 +122,13 @@ pub struct CreateEc2InstanceInfo { pub vanity_address: Option, pub instance_type: String, } + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GetInstanceTypeByInstanceId { + pub instance_id: String, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GetInstanceTypeRes { + pub instance_type: Option, +} diff --git a/src/bin/super/mod.rs b/src/bin/super/mod.rs index a80042d0..8abc7d71 100644 --- a/src/bin/super/mod.rs +++ b/src/bin/super/mod.rs @@ -1,6 +1,7 @@ mod auth_token; mod checker; mod cmd; +mod route53; mod routes; mod state; mod util; @@ -11,8 +12,9 @@ use sphinx_swarm::utils::getenv; use state::RemoteStack; use state::Super; use util::{ - accessing_child_container_controller, add_new_swarm_details, get_aws_instance_types, - get_child_swarm_config, get_child_swarm_containers, + accessing_child_container_controller, add_new_swarm_details, add_new_swarm_from_child_swarm, + get_aws_instance_types, get_child_swarm_config, get_child_swarm_containers, + get_swarm_instance_type, update_aws_instance_type, }; use crate::checker::swarm_checker; @@ -171,7 +173,8 @@ pub async fn super_handle( pass: Some("".to_string()), ec2: Some(swarm.instance), note: Some(swarm.description), - default_host: Some("".to_string()), + default_host: "".to_string(), + ec2_instance_id: "".to_string(), }; let hm = add_new_swarm_details(&mut state, swarm_detail, &mut must_save_stack); @@ -189,6 +192,7 @@ pub async fn super_handle( user: state.stacks[ui].user.clone(), pass: state.stacks[ui].pass.clone(), default_host: state.stacks[ui].default_host.clone(), + ec2_instance_id: state.stacks[ui].ec2_instance_id.clone(), }; must_save_stack = true; hm = AddSwarmResponse { @@ -228,9 +232,11 @@ pub async fn super_handle( pass: Some(c.password), user: Some(c.username), ec2: Some("".to_string()), - default_host: Some(c.default_host), + default_host: c.default_host, + ec2_instance_id: "".to_string(), }; - let hm = add_new_swarm_details(&mut state, swarm_details, &mut must_save_stack); + let hm = + add_new_swarm_from_child_swarm(&mut state, swarm_details, &mut must_save_stack); Some(serde_json::to_string(&hm)?) } @@ -309,8 +315,9 @@ pub async fn super_handle( } SwarmCmd::CreateNewEc2Instance(info) => { let res: SuperSwarmResponse; - match create_swarm_ec2(&info).await { + match create_swarm_ec2(&info, &mut state).await { Ok(_) => { + must_save_stack = true; res = SuperSwarmResponse { success: true, message: format!("{} was created successfully", &info.name.clone()), @@ -328,6 +335,41 @@ pub async fn super_handle( Some(serde_json::to_string(&res)?) } + SwarmCmd::UpdateAwsInstanceType(info) => { + let res: SuperSwarmResponse; + match update_aws_instance_type(info, &mut state).await { + Ok(_) => { + must_save_stack = true; + res = SuperSwarmResponse { + success: true, + message: "Instance updated successfully".to_string(), + data: None, + } + } + Err(err) => { + res = SuperSwarmResponse { + success: false, + message: err.to_string(), + data: None, + } + } + } + Some(serde_json::to_string(&res)?) + } + SwarmCmd::GetInstanceType(info) => { + let res: SuperSwarmResponse; + match get_swarm_instance_type(info, &state) { + Ok(result) => res = result, + Err(err) => { + res = SuperSwarmResponse { + success: false, + message: err.to_string(), + data: None, + } + } + } + Some(serde_json::to_string(&res)?) + } }, }; diff --git a/src/bin/super/route53.rs b/src/bin/super/route53.rs new file mode 100644 index 00000000..d697e117 --- /dev/null +++ b/src/bin/super/route53.rs @@ -0,0 +1,67 @@ +use anyhow::{anyhow, Error}; +use aws_config::meta::region::RegionProviderChain; +use aws_config::Region; +use aws_sdk_route53::types::{ + Change, ChangeAction, ChangeBatch, ResourceRecord, ResourceRecordSet, +}; +use aws_sdk_route53::Client as Route53Client; +use aws_smithy_types::retry::RetryConfig; +use sphinx_swarm::utils::getenv; + +pub async fn add_domain_name_to_route53( + domain_names: Vec<&str>, + public_ip: &str, +) -> Result<(), Error> { + let region = getenv("AWS_S3_REGION_NAME")?; + let hosted_zone_id = getenv("ROUTE53_ZONE_ID")?; + let region_provider = RegionProviderChain::first_try(Some(Region::new(region))); + let config = aws_config::from_env() + .region(region_provider) + .retry_config(RetryConfig::standard().with_max_attempts(10)) + .load() + .await; + let route53_client = Route53Client::new(&config); + + let mut changes = Vec::new(); + + for &domain in &domain_names { + let resource_record = ResourceRecord::builder().value(public_ip).build()?; + + let resource_record_set = ResourceRecordSet::builder() + .name(domain) + .r#type("A".into()) // A record for IPv4 + .ttl(300) // Time-to-live (in seconds) + .resource_records(resource_record) + .build() + .map_err(|err| anyhow!(err.to_string()))?; + + // Create a change request to upsert (create or update) the A record + let change = Change::builder() + .action(ChangeAction::Upsert) + .resource_record_set(resource_record_set) + .build() + .map_err(|err| anyhow!(err.to_string()))?; + + changes.push(change); + } + + let change_batch = ChangeBatch::builder() + .set_changes(Some(changes)) + .build() + .map_err(|err| anyhow!(err.to_string()))?; + + let response = route53_client + .change_resource_record_sets() + .hosted_zone_id(hosted_zone_id) + .change_batch(change_batch) + .send() + .await?; + + log::info!( + "Route 53 change status for {:?}: {:?}", + domain_names, + response.change_info() + ); + + Ok(()) +} diff --git a/src/bin/super/state.rs b/src/bin/super/state.rs index 24c46412..61081c7a 100644 --- a/src/bin/super/state.rs +++ b/src/bin/super/state.rs @@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize}; use sphinx_swarm::config::{Role, User}; use sphinx_swarm::secrets; +use crate::util::get_descriptive_instance_type; + #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] pub struct Super { pub stacks: Vec, @@ -19,7 +21,8 @@ pub struct RemoteStack { pub ec2: Option, pub user: Option, pub pass: Option, - pub default_host: Option, + pub default_host: String, + pub ec2_instance_id: String, } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Default)] @@ -76,10 +79,11 @@ impl Super { .map(|n| RemoteStack { host: n.host.clone(), note: n.note.clone(), - ec2: n.ec2.clone(), + ec2: Some(get_descriptive_instance_type(n.ec2.clone())), user: None, pass: None, default_host: n.default_host.clone(), + ec2_instance_id: n.ec2_instance_id.clone(), }) .collect(); let bots = self diff --git a/src/bin/super/superapp/src/Remotes.svelte b/src/bin/super/superapp/src/Remotes.svelte index 1b4a468f..c05e2432 100644 --- a/src/bin/super/superapp/src/Remotes.svelte +++ b/src/bin/super/superapp/src/Remotes.svelte @@ -37,6 +37,7 @@ update_child_swarm_containers, get_aws_instance_types, restart_child_swarm_containers, + update_aws_instance_type, } from "../../../../../app/src/api/swarm"; let open_create_edit = false; @@ -489,6 +490,10 @@ vanity_input_width = max_input_with; swarm_name_width = max_input_with; show_notification = true; + + await getConfig(); + + await getConfigSortByUnhealthy(); } else { error_notification = true; } diff --git a/src/bin/super/superapp/src/ViewNodes.svelte b/src/bin/super/superapp/src/ViewNodes.svelte index dc4865fc..6b585e78 100644 --- a/src/bin/super/superapp/src/ViewNodes.svelte +++ b/src/bin/super/superapp/src/ViewNodes.svelte @@ -1,6 +1,6 @@