use crate::client::RestClient;
use crate::error::Result;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use typed_builder::TypedBuilder;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterActionResponse {
pub action_uid: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterNode {
pub id: u32,
pub address: String,
pub status: String,
pub role: Option<String>,
pub total_memory: Option<u64>,
pub used_memory: Option<u64>,
pub cpu_cores: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterInfo {
pub uid: Option<u32>,
pub name: String,
pub created: Option<String>,
pub last_changed_time: Option<String>,
pub license_expired: Option<bool>,
pub nodes: Option<Vec<u32>>,
pub databases: Option<Vec<u32>>,
pub status: Option<String>,
pub email_alerts: Option<bool>,
pub rack_aware: Option<bool>,
pub bigstore_driver: Option<String>,
pub cnm_http_port: Option<u16>,
pub cnm_https_port: Option<u16>,
pub total_memory: Option<u64>,
pub used_memory: Option<u64>,
pub total_shards: Option<u32>,
pub alert_settings: Option<Value>,
pub block_cluster_changes: Option<bool>,
pub ccs_internode_encryption: Option<bool>,
pub cluster_api_internal_port: Option<u32>,
pub cluster_ssh_public_key: Option<String>,
pub cm_port: Option<u32>,
pub cm_server_version: Option<u32>,
pub cm_session_timeout_minutes: Option<u32>,
pub cnm_http_max_threads_per_worker: Option<u32>,
pub cnm_http_workers: Option<u32>,
pub control_cipher_suites: Option<String>,
pub control_cipher_suites_tls_1_3: Option<String>,
pub crdb_coordinator_ignore_requests: Option<bool>,
pub crdb_coordinator_port: Option<u32>,
pub crdt_supported_featureset_version: Option<u32>,
pub crdt_supported_protocol_versions: Option<Vec<String>>,
pub created_time: Option<String>,
pub data_cipher_list: Option<String>,
pub data_cipher_suites_tls_1_3: Option<Vec<Value>>,
pub debuginfo_path: Option<String>,
pub encrypt_pkeys: Option<bool>,
pub entra_id_cache_ttl: Option<u32>,
pub envoy_admin_port: Option<u32>,
pub envoy_external_authorization: Option<bool>,
pub envoy_max_downstream_connections: Option<u32>,
pub envoy_mgmt_server_port: Option<u32>,
pub gossip_envoy_admin_port: Option<u32>,
pub handle_metrics_redirects: Option<bool>,
pub handle_redirects: Option<bool>,
pub http_support: Option<bool>,
pub logrotate_settings: Option<Value>,
pub mask_bdb_credentials: Option<bool>,
pub metrics_system: Option<u32>,
#[serde(rename = "min_control_TLS_version")]
pub min_control_tls_version: Option<String>,
#[serde(rename = "min_data_TLS_version")]
pub min_data_tls_version: Option<String>,
#[serde(rename = "min_sentinel_TLS_version")]
pub min_sentinel_tls_version: Option<String>,
pub module_upload_max_size_mb: Option<u32>,
pub mtls_authorized_subjects: Option<Vec<String>>,
pub mtls_certificate_authentication: Option<bool>,
pub mtls_client_cert_subject_validation_type: Option<String>,
pub multi_commands_opt: Option<String>,
pub options_method_forbidden: Option<bool>,
pub password_complexity: Option<bool>,
pub password_expiration_duration: Option<u32>,
pub password_hashing_algorithm: Option<String>,
pub password_min_length: Option<u32>,
pub proxy_certificate: Option<String>,
pub reserved_ports: Option<Vec<u32>>,
pub robust_crdt_syncer: Option<bool>,
pub s3_certificate_verification: Option<bool>,
pub sentinel_cipher_suites: Option<Vec<String>>,
pub sentinel_cipher_suites_tls_1_3: Option<String>,
pub sentinel_tls_mode: Option<String>,
pub slave_ha: Option<bool>,
pub slave_ha_bdb_cooldown_period: Option<u32>,
pub slave_ha_cooldown_period: Option<u32>,
pub slave_ha_grace_period: Option<u32>,
pub slowlog_in_sanitized_support: Option<bool>,
pub smtp_tls_mode: Option<String>,
pub smtp_use_tls: Option<bool>,
pub syncer_certificate: Option<String>,
pub system_reserved_ports: Option<Vec<u32>>,
pub upgrade_in_progress: Option<bool>,
pub upgrade_mode: Option<bool>,
pub use_external_ipv6: Option<bool>,
pub use_ipv6: Option<bool>,
pub wait_command: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClusterSettings {
pub auto_recovery: Option<bool>,
pub automatic_node_offload: Option<bool>,
pub bigstore_migrate_node_threshold: Option<u32>,
pub bigstore_migrate_node_threshold_p: Option<u32>,
pub bigstore_provision_node_threshold: Option<u32>,
pub bigstore_provision_node_threshold_p: Option<u32>,
pub default_bigstore_version: Option<u32>,
pub data_internode_encryption: Option<bool>,
pub db_conns_auditing: Option<bool>,
pub default_concurrent_restore_actions: Option<u32>,
pub default_fork_evict_ram: Option<bool>,
pub default_non_sharded_proxy_policy: Option<String>,
pub default_sharded_proxy_policy: Option<String>,
pub default_oss_cluster: Option<bool>,
pub default_oss_sharding: Option<bool>,
pub default_provisioned_redis_version: Option<String>,
pub default_recovery_wait_time: Option<u32>,
pub default_shards_placement: Option<String>,
pub default_tracking_table_max_keys_policy: Option<String>,
pub email_alerts: Option<bool>,
pub endpoint_rebind_enabled: Option<bool>,
pub failure_detection_sensitivity: Option<String>,
pub gossip_envoy_admin_port: Option<u32>,
pub gossip_envoy_port: Option<u32>,
pub gossip_envoy_proxy_mode: Option<bool>,
pub hot_spare: Option<bool>,
pub max_saved_events_per_type: Option<u32>,
pub max_simultaneous_backups: Option<u32>,
pub parallel_shards_upgrade: Option<u32>,
pub persistent_node_removal: Option<bool>,
pub rack_aware: Option<bool>,
pub redis_migrate_node_threshold: Option<String>,
pub redis_migrate_node_threshold_p: Option<u32>,
pub redis_provision_node_threshold: Option<String>,
pub redis_provision_node_threshold_p: Option<u32>,
pub redis_upgrade_policy: Option<String>,
pub resp3_default: Option<bool>,
pub show_internals: Option<bool>,
pub slave_threads_when_master: Option<bool>,
pub use_empty_shard_backups: Option<bool>,
}
#[derive(Debug, Serialize, TypedBuilder)]
pub struct BootstrapRequest {
#[builder(setter(into))]
pub action: String,
pub cluster: ClusterBootstrapInfo,
pub credentials: BootstrapCredentials,
}
#[derive(Debug, Serialize, TypedBuilder)]
pub struct ClusterBootstrapInfo {
#[builder(setter(into))]
pub name: String,
}
#[derive(Debug, Serialize, TypedBuilder)]
pub struct BootstrapCredentials {
#[builder(setter(into))]
pub username: String,
#[builder(setter(into))]
pub password: String,
}
pub struct ClusterHandler {
client: RestClient,
}
impl ClusterHandler {
pub fn new(client: RestClient) -> Self {
ClusterHandler { client }
}
pub async fn info(&self) -> Result<ClusterInfo> {
self.client.get("/v1/cluster").await
}
pub async fn bootstrap(&self, request: BootstrapRequest) -> Result<Value> {
self.client
.post_bootstrap("/v1/bootstrap/create_cluster", &request)
.await
}
pub async fn update(&self, updates: Value) -> Result<Value> {
self.client.put("/v1/cluster", &updates).await
}
pub async fn stats(&self) -> Result<Value> {
self.client.get("/v1/cluster/stats").await
}
pub async fn nodes(&self) -> Result<Vec<NodeInfo>> {
self.client.get("/v1/nodes").await
}
pub async fn license(&self) -> Result<LicenseInfo> {
self.client.get("/v1/license").await
}
pub async fn join_node(
&self,
node_address: &str,
username: &str,
password: &str,
) -> Result<Value> {
let body = serde_json::json!({
"action": "join_cluster",
"cluster": {
"nodes": [node_address]
},
"credentials": {
"username": username,
"password": password
}
});
self.client.post("/v1/bootstrap/join", &body).await
}
pub async fn remove_node(&self, node_uid: u32) -> Result<Value> {
self.client
.delete(&format!("/v1/nodes/{}", node_uid))
.await?;
Ok(serde_json::json!({"message": format!("Node {} removed", node_uid)}))
}
pub async fn reset(&self) -> Result<ClusterActionResponse> {
self.client
.post("/v1/cluster/actions/reset", &serde_json::json!({}))
.await
}
pub async fn recover(&self) -> Result<ClusterActionResponse> {
self.client
.post("/v1/cluster/actions/recover", &serde_json::json!({}))
.await
}
pub async fn settings(&self) -> Result<Value> {
self.client.get("/v1/cluster/settings").await
}
pub async fn topology(&self) -> Result<Value> {
self.client.get("/v1/cluster/topology").await
}
pub async fn actions(&self) -> Result<Value> {
self.client.get("/v1/cluster/actions").await
}
pub async fn action_detail(&self, action: &str) -> Result<Value> {
self.client
.get(&format!("/v1/cluster/actions/{}", action))
.await
}
pub async fn action_execute(&self, action: &str, body: Value) -> Result<Value> {
self.client
.post(&format!("/v1/cluster/actions/{}", action), &body)
.await
}
pub async fn action_delete(&self, action: &str) -> Result<()> {
self.client
.delete(&format!("/v1/cluster/actions/{}", action))
.await
}
pub async fn auditing_db_conns(&self) -> Result<Value> {
self.client.get("/v1/cluster/auditing/db_conns").await
}
pub async fn auditing_db_conns_update(&self, cfg: Value) -> Result<Value> {
self.client.put("/v1/cluster/auditing/db_conns", &cfg).await
}
pub async fn auditing_db_conns_delete(&self) -> Result<()> {
self.client.delete("/v1/cluster/auditing/db_conns").await
}
pub async fn certificates(&self) -> Result<Value> {
self.client.get("/v1/cluster/certificates").await
}
pub async fn certificate_delete(&self, uid: u32) -> Result<()> {
self.client
.delete(&format!("/v1/cluster/certificates/{}", uid))
.await
}
pub async fn certificates_rotate(&self) -> Result<Value> {
self.client
.post("/v1/cluster/certificates/rotate", &serde_json::json!({}))
.await
}
pub async fn update_cert(&self, body: Value) -> Result<Value> {
self.client.put("/v1/cluster/update_cert", &body).await
}
pub async fn ldap_delete(&self) -> Result<()> {
self.client.delete("/v1/cluster/ldap").await
}
pub async fn module_capabilities(&self) -> Result<Value> {
self.client.get("/v1/cluster/module-capabilities").await
}
pub async fn policy(&self) -> Result<Value> {
self.client.get("/v1/cluster/policy").await
}
pub async fn policy_update(&self, policy: Value) -> Result<Value> {
self.client.put("/v1/cluster/policy", &policy).await
}
pub async fn policy_restore_default(&self) -> Result<Value> {
self.client
.put("/v1/cluster/policy/restore_default", &serde_json::json!({}))
.await
}
pub async fn services_configuration(&self) -> Result<Value> {
self.client.get("/v1/cluster/services_configuration").await
}
pub async fn services_configuration_update(&self, cfg: Value) -> Result<Value> {
self.client
.put("/v1/cluster/services_configuration", &cfg)
.await
}
pub async fn witness_disk(&self) -> Result<Value> {
self.client.get("/v1/cluster/witness_disk").await
}
pub async fn alert_detail(&self, alert: &str) -> Result<Value> {
self.client
.get(&format!("/v1/cluster/alerts/{}", alert))
.await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeInfo {
pub uid: u32,
pub address: String,
pub status: String,
pub role: Option<String>,
pub shards: Option<Vec<u32>>,
pub total_memory: Option<u64>,
pub used_memory: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LicenseInfo {
pub license_type: Option<String>,
pub expired: Option<bool>,
pub expiration_date: Option<String>,
pub shards_limit: Option<u32>,
pub features: Option<Vec<String>>,
}