redis_enterprise/
cluster.rs

1//! Cluster management commands for Redis Enterprise
2
3use crate::client::RestClient;
4use crate::error::Result;
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8/// Node information
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct ClusterNode {
11    pub id: u32,
12    pub address: String,
13    pub status: String,
14    pub role: Option<String>,
15    pub total_memory: Option<u64>,
16    pub used_memory: Option<u64>,
17    pub cpu_cores: Option<u32>,
18
19    #[serde(flatten)]
20    pub extra: Value,
21}
22
23/// Cluster information from the REST API
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ClusterInfo {
26    pub name: String,
27    pub version: Option<String>,
28    pub license_expired: Option<bool>,
29    pub nodes: Option<Vec<u32>>,
30    pub databases: Option<Vec<u32>>,
31    pub status: Option<String>,
32    pub email_alerts: Option<bool>,
33    pub rack_aware: Option<bool>,
34
35    // Stats
36    pub total_memory: Option<u64>,
37    pub used_memory: Option<u64>,
38    pub total_shards: Option<u32>,
39
40    #[serde(flatten)]
41    pub extra: Value,
42}
43
44/// Cluster-wide settings configuration (57 fields)
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct ClusterSettings {
47    /// Automatic recovery on shard failure
48    pub auto_recovery: Option<bool>,
49
50    /// Automatic migration of shards from overbooked nodes
51    pub automatic_node_offload: Option<bool>,
52
53    /// BigStore migration thresholds
54    pub bigstore_migrate_node_threshold: Option<u32>,
55    pub bigstore_migrate_node_threshold_p: Option<u32>,
56    pub bigstore_provision_node_threshold: Option<u32>,
57    pub bigstore_provision_node_threshold_p: Option<u32>,
58
59    /// Default BigStore version
60    pub default_bigstore_version: Option<u32>,
61
62    /// Data internode encryption
63    pub data_internode_encryption: Option<bool>,
64
65    /// Database connections auditing
66    pub db_conns_auditing: Option<bool>,
67
68    /// Default concurrent restore actions
69    pub default_concurrent_restore_actions: Option<u32>,
70
71    /// Default fork evict RAM
72    pub default_fork_evict_ram: Option<bool>,
73
74    /// Default proxy policies
75    pub default_non_sharded_proxy_policy: Option<String>,
76    pub default_sharded_proxy_policy: Option<String>,
77
78    /// OSS cluster defaults
79    pub default_oss_cluster: Option<bool>,
80    pub default_oss_sharding: Option<bool>,
81
82    /// Default Redis version for new databases
83    pub default_provisioned_redis_version: Option<String>,
84
85    /// Recovery settings
86    pub default_recovery_wait_time: Option<u32>,
87
88    /// Shards placement strategy
89    pub default_shards_placement: Option<String>,
90
91    /// Tracking table settings
92    pub default_tracking_table_max_keys_policy: Option<String>,
93
94    /// Additional cluster-wide settings
95    pub email_alerts: Option<bool>,
96    pub endpoint_rebind_enabled: Option<bool>,
97    pub failure_detection_sensitivity: Option<String>,
98    pub gossip_envoy_admin_port: Option<u32>,
99    pub gossip_envoy_port: Option<u32>,
100    pub gossip_envoy_proxy_mode: Option<bool>,
101    pub hot_spare: Option<bool>,
102    pub max_saved_events_per_type: Option<u32>,
103    pub max_simultaneous_backups: Option<u32>,
104    pub parallel_shards_upgrade: Option<u32>,
105    pub persistent_node_removal: Option<bool>,
106    pub rack_aware: Option<bool>,
107    pub redis_migrate_node_threshold: Option<String>,
108    pub redis_migrate_node_threshold_p: Option<u32>,
109    pub redis_provision_node_threshold: Option<String>,
110    pub redis_provision_node_threshold_p: Option<u32>,
111    pub redis_upgrade_policy: Option<String>,
112    pub resp3_default: Option<bool>,
113    pub show_internals: Option<bool>,
114    pub slave_threads_when_master: Option<bool>,
115    pub use_empty_shard_backups: Option<bool>,
116
117    #[serde(flatten)]
118    pub extra: Value,
119}
120
121/// Bootstrap request for creating a new cluster
122#[derive(Debug, Serialize)]
123pub struct BootstrapRequest {
124    pub action: String,
125    pub cluster: ClusterBootstrapInfo,
126    pub credentials: BootstrapCredentials,
127}
128
129/// Cluster information for bootstrap
130#[derive(Debug, Serialize)]
131pub struct ClusterBootstrapInfo {
132    pub name: String,
133}
134
135/// Credentials for bootstrap
136#[derive(Debug, Serialize)]
137pub struct BootstrapCredentials {
138    pub username: String,
139    pub password: String,
140}
141
142/// Cluster handler for executing cluster commands
143pub struct ClusterHandler {
144    client: RestClient,
145}
146
147impl ClusterHandler {
148    pub fn new(client: RestClient) -> Self {
149        ClusterHandler { client }
150    }
151
152    /// Get cluster information (CLUSTER.INFO)
153    pub async fn info(&self) -> Result<ClusterInfo> {
154        self.client.get("/v1/cluster").await
155    }
156
157    /// Bootstrap a new cluster (CLUSTER.BOOTSTRAP)
158    pub async fn bootstrap(&self, request: BootstrapRequest) -> Result<Value> {
159        // The bootstrap endpoint returns empty response on success
160        // Note: Despite docs saying /v1/bootstrap, the actual endpoint is /v1/bootstrap/create_cluster
161        self.client
162            .post_bootstrap("/v1/bootstrap/create_cluster", &request)
163            .await
164    }
165
166    /// Update cluster configuration (CLUSTER.UPDATE)
167    pub async fn update(&self, updates: Value) -> Result<Value> {
168        self.client.put("/v1/cluster", &updates).await
169    }
170
171    /// Get cluster stats (CLUSTER.STATS)
172    pub async fn stats(&self) -> Result<Value> {
173        self.client.get("/v1/cluster/stats").await
174    }
175
176    /// Get cluster nodes (CLUSTER.NODES)
177    pub async fn nodes(&self) -> Result<Vec<NodeInfo>> {
178        self.client.get("/v1/nodes").await
179    }
180
181    /// Get cluster license (CLUSTER.LICENSE)
182    pub async fn license(&self) -> Result<LicenseInfo> {
183        self.client.get("/v1/license").await
184    }
185
186    /// Join node to cluster (CLUSTER.JOIN)
187    pub async fn join_node(
188        &self,
189        node_address: &str,
190        username: &str,
191        password: &str,
192    ) -> Result<Value> {
193        let body = serde_json::json!({
194            "action": "join_cluster",
195            "cluster": {
196                "nodes": [node_address]
197            },
198            "credentials": {
199                "username": username,
200                "password": password
201            }
202        });
203        self.client.post("/v1/bootstrap/join", &body).await
204    }
205
206    /// Remove node from cluster (CLUSTER.REMOVE_NODE)
207    pub async fn remove_node(&self, node_uid: u32) -> Result<Value> {
208        self.client
209            .delete(&format!("/v1/nodes/{}", node_uid))
210            .await?;
211        Ok(serde_json::json!({"message": format!("Node {} removed", node_uid)}))
212    }
213
214    /// Reset cluster to factory defaults (CLUSTER.RESET) - DANGEROUS
215    pub async fn reset(&self) -> Result<Value> {
216        self.client
217            .post("/v1/cluster/actions/reset", &serde_json::json!({}))
218            .await
219    }
220
221    /// Recover cluster from failure (CLUSTER.RECOVER)
222    pub async fn recover(&self) -> Result<Value> {
223        self.client
224            .post("/v1/cluster/actions/recover", &serde_json::json!({}))
225            .await
226    }
227
228    /// Get cluster settings (CLUSTER.SETTINGS)
229    pub async fn settings(&self) -> Result<Value> {
230        self.client.get("/v1/cluster/settings").await
231    }
232
233    /// Get cluster topology (CLUSTER.TOPOLOGY)
234    pub async fn topology(&self) -> Result<Value> {
235        self.client.get("/v1/cluster/topology").await
236    }
237}
238
239/// Node information
240#[derive(Debug, Clone, Serialize, Deserialize)]
241pub struct NodeInfo {
242    pub uid: u32,
243    pub address: String,
244    pub status: String,
245    pub role: Option<String>,
246    pub shards: Option<Vec<u32>>,
247    pub total_memory: Option<u64>,
248    pub used_memory: Option<u64>,
249
250    #[serde(flatten)]
251    pub extra: Value,
252}
253
254/// License information
255#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct LicenseInfo {
257    pub license_type: Option<String>,
258    pub expired: Option<bool>,
259    pub expiration_date: Option<String>,
260    pub shards_limit: Option<u32>,
261    pub features: Option<Vec<String>>,
262
263    #[serde(flatten)]
264    pub extra: Value,
265}