use super::*;
pub struct NewServer<'a> {
pub name: &'a str,
pub connection: &'a str,
pub address: &'a str,
pub identity: Option<&'a str>,
pub region: Option<&'a str>,
pub labels: &'a [String],
}
fn threshold_query(threshold: Option<u32>) -> String {
threshold.map_or_else(String::new, |value| format!("?threshold={value}"))
}
fn server_path(server: &str) -> String {
format!(
"/api/infra/servers/{}",
utf8_percent_encode(server, NON_ALPHANUMERIC)
)
}
fn placement_path(project_id: &str, environment: &str) -> String {
format!(
"/api/infra/projects/{project_id}/environments/{}/server",
utf8_percent_encode(environment, NON_ALPHANUMERIC)
)
}
impl ApiClient {
pub async fn servers(&self) -> Result<Answer<ServersPage>> {
self.get_answer("/api/infra/servers").await
}
pub async fn server(&self, server: &str) -> Result<Answer<ServerDetail>> {
self.get_answer(&server_path(server)).await
}
pub async fn add_server(&self, draft: &NewServer<'_>) -> Result<Answer<ServerPage>> {
let mut body = serde_json::json!({
"name": draft.name,
"connection": draft.connection,
"address": draft.address,
});
if let Some(identity) = draft.identity {
body["identity"] = serde_json::json!(identity);
}
if let Some(region) = draft.region {
body["region"] = serde_json::json!(region);
}
if !draft.labels.is_empty() {
body["labels"] = serde_json::json!(draft.labels);
}
self.post_answer("/api/infra/servers", &body).await
}
pub async fn cordon_server(&self, server: &str, cordoned: bool) -> Result<Answer<ServerPage>> {
let verb = if cordoned { "cordon" } else { "uncordon" };
self.post_answer(
&format!("{}/{verb}", server_path(server)),
&serde_json::json!({}),
)
.await
}
pub async fn set_server_failover(
&self,
server: &str,
failover: bool,
) -> Result<Answer<ServerPage>> {
self.answer(
Method::PATCH,
&server_path(server),
Some(&serde_json::json!({ "failover": failover })),
)
.await
}
pub async fn plan_drain(&self, server: &str) -> Result<Answer<DrainPlan>> {
self.get_answer(&format!("{}/drain", server_path(server)))
.await
}
pub async fn drain_server(&self, server: &str) -> Result<Answer<DrainPlan>> {
self.post_answer(
&format!("{}/drain", server_path(server)),
&serde_json::json!({}),
)
.await
}
pub async fn remove_server(
&self,
server: &str,
confirmation: &str,
) -> Result<Answer<RemovedServer>> {
self.answer(
Method::DELETE,
&format!(
"{}?confirm={}",
server_path(server),
utf8_percent_encode(confirmation, NON_ALPHANUMERIC)
),
None::<&()>,
)
.await
}
pub async fn environment_server(
&self,
project_id: &str,
environment: &str,
) -> Result<Answer<EnvironmentPlacement>> {
self.get_answer(&placement_path(project_id, environment))
.await
}
pub async fn move_environment(
&self,
project_id: &str,
environment: &str,
server: &str,
) -> Result<Answer<MovePage>> {
self.answer(
Method::PUT,
&placement_path(project_id, environment),
Some(&serde_json::json!({ "server": server })),
)
.await
}
pub async fn environment_move(&self, move_id: &str) -> Result<Answer<MovePage>> {
self.get_answer(&format!(
"/api/infra/moves/{}",
utf8_percent_encode(move_id, NON_ALPHANUMERIC)
))
.await
}
pub async fn replicas(&self, service_environment_id: &str) -> Result<Answer<ReplicaSet>> {
self.get_answer(&format!(
"/api/infra/services/{service_environment_id}/replicas"
))
.await
}
pub async fn label_server(
&self,
server: &str,
set: &[String],
remove: &[String],
) -> Result<Answer<ServerPage>> {
self.post_answer(
&format!("{}/labels", server_path(server)),
&serde_json::json!({ "set": set, "remove": remove }),
)
.await
}
pub async fn require_environment_labels(
&self,
project_id: &str,
environment: &str,
labels: &[String],
) -> Result<Answer<EnvironmentPlacement>> {
self.answer(
Method::PUT,
&format!("{}/requirements", placement_path(project_id, environment)),
Some(&serde_json::json!({ "labels": labels })),
)
.await
}
pub async fn plan_rebalance(&self, threshold: Option<u32>) -> Result<Answer<RebalancePlan>> {
self.get_answer(&format!(
"/api/infra/servers/rebalance{}",
threshold_query(threshold)
))
.await
}
pub async fn rebalance(&self, threshold: Option<u32>) -> Result<Answer<RebalancePlan>> {
self.post_answer(
&format!("/api/infra/servers/rebalance{}", threshold_query(threshold)),
&serde_json::json!({}),
)
.await
}
pub async fn server_metrics(&self, server: &str, range: &str) -> Result<Answer<ServerMetrics>> {
self.get_answer(&format!(
"{}/metrics?range={}",
server_path(server),
utf8_percent_encode(range, NON_ALPHANUMERIC)
))
.await
}
pub async fn server_bootstrap(&self, user: &str) -> Result<Answer<ServerBootstrap>> {
self.get_answer(&format!(
"/api/infra/servers/bootstrap?user={}",
utf8_percent_encode(user, NON_ALPHANUMERIC)
))
.await
}
pub async fn test_server_connection(
&self,
connection: &str,
identity: Option<&str>,
) -> Result<Answer<ConnectionTest>> {
let mut body = serde_json::json!({ "connection": connection });
if let Some(identity) = identity {
body["identity"] = serde_json::json!(identity);
}
self.post_answer("/api/infra/servers/test", &body).await
}
}