redis_enterprise/
migrations.rs

1//! Database migration operations
2//!
3//! ## Overview
4//! - Perform database migrations
5//! - Track migration status
6//! - Manage migration plans
7
8use crate::client::RestClient;
9use crate::error::Result;
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use typed_builder::TypedBuilder;
13
14/// Migration task
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct Migration {
17    /// Unique identifier for this migration task
18    pub migration_id: String,
19    /// Source endpoint configuration
20    pub source: MigrationEndpoint,
21    /// Target endpoint configuration
22    pub target: MigrationEndpoint,
23    /// Sync status of this migration (e.g., "syncing", "in-sync", "out-of-sync")
24    pub status: String,
25    /// Migration progress as a percentage (0.0-1.0)
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub progress: Option<f32>,
28    /// Timestamp when migration started
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub start_time: Option<String>,
31    /// Timestamp when migration completed
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub end_time: Option<String>,
34    /// Error message if migration failed
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub error: Option<String>,
37
38    #[serde(flatten)]
39    pub extra: Value,
40}
41
42/// Migration endpoint configuration
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct MigrationEndpoint {
45    /// Type of endpoint (e.g., "redis", "cluster", "azure-cache")
46    pub endpoint_type: String,
47    /// Hostname or IP address of the endpoint
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub host: Option<String>,
50    /// Port number of the endpoint
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub port: Option<u16>,
53    /// Database UID (for internal cluster migrations)
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub bdb_uid: Option<u32>,
56    /// Authentication password for the endpoint
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub password: Option<String>,
59    /// Whether to use SSL/TLS for the connection
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub ssl: Option<bool>,
62}
63
64/// Create migration request
65#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
66pub struct CreateMigrationRequest {
67    /// Source endpoint configuration
68    pub source: MigrationEndpoint,
69    /// Target endpoint configuration
70    pub target: MigrationEndpoint,
71    /// Type of migration to perform (e.g., "full", "incremental")
72    #[serde(skip_serializing_if = "Option::is_none")]
73    #[builder(default, setter(into, strip_option))]
74    pub migration_type: Option<String>,
75    /// Redis key pattern to migrate (supports wildcards)
76    #[serde(skip_serializing_if = "Option::is_none")]
77    #[builder(default, setter(into, strip_option))]
78    pub key_pattern: Option<String>,
79    /// Whether to flush the target database before migration
80    #[serde(skip_serializing_if = "Option::is_none")]
81    #[builder(default, setter(strip_option))]
82    pub flush_target: Option<bool>,
83}
84
85/// Migrations handler
86pub struct MigrationsHandler {
87    client: RestClient,
88}
89
90impl MigrationsHandler {
91    pub fn new(client: RestClient) -> Self {
92        MigrationsHandler { client }
93    }
94
95    /// List all migrations
96    pub async fn list(&self) -> Result<Vec<Migration>> {
97        self.client.get("/v1/migrations").await
98    }
99
100    /// Get specific migration
101    pub async fn get(&self, migration_id: &str) -> Result<Migration> {
102        self.client
103            .get(&format!("/v1/migrations/{}", migration_id))
104            .await
105    }
106
107    /// Create a new migration
108    pub async fn create(&self, request: CreateMigrationRequest) -> Result<Migration> {
109        self.client.post("/v1/migrations", &request).await
110    }
111
112    /// Start a migration
113    pub async fn start(&self, migration_id: &str) -> Result<Migration> {
114        self.client
115            .post(
116                &format!("/v1/migrations/{}/start", migration_id),
117                &Value::Null,
118            )
119            .await
120    }
121
122    /// Pause a migration
123    pub async fn pause(&self, migration_id: &str) -> Result<Migration> {
124        self.client
125            .post(
126                &format!("/v1/migrations/{}/pause", migration_id),
127                &Value::Null,
128            )
129            .await
130    }
131
132    /// Resume a migration
133    pub async fn resume(&self, migration_id: &str) -> Result<Migration> {
134        self.client
135            .post(
136                &format!("/v1/migrations/{}/resume", migration_id),
137                &Value::Null,
138            )
139            .await
140    }
141
142    /// Cancel a migration
143    pub async fn cancel(&self, migration_id: &str) -> Result<()> {
144        self.client
145            .delete(&format!("/v1/migrations/{}", migration_id))
146            .await
147    }
148}