fundaia 0.55.0

Command line for the Fundaia deployment platform: projects, services, variables, deployments, logs and metrics
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
    }
}