use std::time::Duration;
use crate::{context::RegionContext, error::Error};
use mz_cloud_api::client::{cloud_provider::CloudProvider, region::RegionState};
use mz_ore::retry::Retry;
use serde::{Deserialize, Serialize};
use tabled::Tabled;
pub async fn enable(
cx: RegionContext,
version: Option<String>,
environmentd_extra_arg: Option<Vec<String>>,
) -> Result<(), Error> {
let loading_spinner = cx
.output_formatter()
.loading_spinner("Retrieving information...");
let cloud_provider = cx.get_cloud_provider().await?;
loading_spinner.set_message("Enabling the region...");
let environmentd_extra_arg: Vec<String> = environmentd_extra_arg.unwrap_or_else(Vec::new);
let _ = Retry::default()
.max_duration(Duration::from_secs(720))
.clamp_backoff(Duration::from_secs(1))
.retry_async(|_| async {
let _ = cx
.cloud_client()
.create_region(
version.clone(),
environmentd_extra_arg.clone(),
cloud_provider.clone(),
)
.await?;
Ok(())
})
.await
.map_err(|e| Error::TimeoutError(Box::new(e)))?;
loading_spinner.set_message("Waiting for the region to be online...");
let _ = Retry::default()
.max_duration(Duration::from_secs(720))
.clamp_backoff(Duration::from_secs(1))
.retry_async(|_| async {
let region = cx.get_region().await?;
match region.region_state {
RegionState::EnablementPending => {
loading_spinner.set_message("Waiting for the region to be ready...");
Err(Error::NotReadyRegion)
}
RegionState::DeletionPending => Err(Error::CommandExecutionError(
"This region is pending deletion!".to_string(),
)),
RegionState::SoftDeleted => Err(Error::CommandExecutionError(
"This region has been marked soft-deleted!".to_string(),
)),
RegionState::Enabled => match region.region_info {
Some(region_info) => {
loading_spinner.set_message("Waiting for the region to be resolvable...");
if region_info.resolvable {
let claims = cx.admin_client().claims().await?;
let user = claims.user()?;
if cx.sql_client().is_ready(®ion_info, user)? {
return Ok(());
}
Err(Error::NotPgReadyError)
} else {
Err(Error::NotResolvableRegion)
}
}
None => Err(Error::NotReadyRegion),
},
}
})
.await
.map_err(|e| Error::TimeoutError(Box::new(e)))?;
loading_spinner.finish_with_message(format!("Region in {} is now online", cloud_provider.id));
Ok(())
}
pub async fn disable(cx: RegionContext, hard: bool) -> Result<(), Error> {
let loading_spinner = cx
.output_formatter()
.loading_spinner("Retrieving information...");
let cloud_provider = cx.get_cloud_provider().await?;
Retry::default()
.max_duration(Duration::from_secs(720))
.clamp_backoff(Duration::from_secs(1))
.retry_async(|_| async {
loading_spinner.set_message("Disabling region...");
cx.cloud_client()
.delete_region(cloud_provider.clone(), hard)
.await?;
loading_spinner.finish_with_message("Region disabled.");
Ok(())
})
.await
}
pub async fn list(cx: RegionContext) -> Result<(), Error> {
let output_formatter = cx.output_formatter();
let loading_spinner = output_formatter.loading_spinner("Retrieving regions...");
#[derive(Deserialize, Serialize, Tabled)]
pub struct Region<'a> {
#[tabled(rename = "Region")]
region: String,
#[tabled(rename = "Status")]
status: &'a str,
}
let cloud_providers: Vec<CloudProvider> = cx.cloud_client().list_cloud_regions().await?;
let mut regions: Vec<Region> = vec![];
for cloud_provider in cloud_providers {
match cx.cloud_client().get_region(cloud_provider.clone()).await {
Ok(_) => regions.push(Region {
region: cloud_provider.id,
status: "enabled",
}),
Err(mz_cloud_api::error::Error::EmptyRegion) => regions.push(Region {
region: cloud_provider.id,
status: "disabled",
}),
Err(err) => {
println!("Error: {:?}", err)
}
}
}
loading_spinner.finish_and_clear();
output_formatter.output_table(regions)?;
Ok(())
}
pub async fn show(cx: RegionContext) -> Result<(), Error> {
let output_formatter = cx.output_formatter();
let loading_spinner = output_formatter.loading_spinner("Retrieving region...");
let region_info = cx.get_region_info().await?;
loading_spinner.set_message("Checking environment health...");
let claims = cx.admin_client().claims().await?;
let sql_client = cx.sql_client();
let environment_health = match sql_client.is_ready(®ion_info, claims.user()?) {
Ok(healthy) => match healthy {
true => "yes",
_ => "no",
},
Err(_) => "no",
};
loading_spinner.finish_and_clear();
output_formatter.output_scalar(Some(&format!("Healthy: \t{}", environment_health)))?;
output_formatter.output_scalar(Some(&format!("SQL address: \t{}", region_info.sql_address)))?;
output_formatter.output_scalar(Some(&format!("HTTP URL: \t{}", region_info.http_address)))?;
Ok(())
}