Skip to content

Commit 49e78be

Browse files
feat(enterprise): Phase 1 - Binary download support for debuginfo/support packages (#339)
* feat(enterprise): Phase 1 - Add binary download support for debuginfo - Add get_binary() method to REST client for downloading tar.gz files - Update debuginfo handler with new API endpoints (cluster/debuginfo, nodes/debuginfo, etc.) - Add deprecated binary methods for backward compatibility - Update CLI commands to save support packages as files with --file flag - Add default filename generation with timestamps when --file not specified - Add progress spinners during package generation using indicatif - Show file size and location after successful download - Enable auto-initialization in docker-compose now that v0.5.1 is available Part of #334 - Support package workflow Implements #335 - Phase 1 * test: add comprehensive tests for binary debuginfo endpoints - Add tests for all new binary download methods - Add tests for deprecated binary endpoints for backward compatibility - Use realistic gzip mock responses with proper headers - Test cluster, nodes, and database-specific endpoints - Ensure 100% test coverage for new binary functionality * fix(enterprise): use get_binary() for debuginfo download endpoint - Fix download() method to use get_binary() instead of get() - The /v1/debuginfo/{id}/download endpoint returns binary tar.gz data - Add .env.example with correct credentials for local testing - Document proper environment variable usage
1 parent 578fe53 commit 49e78be

File tree

7 files changed

+645
-66
lines changed

7 files changed

+645
-66
lines changed

.env.example

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
# Docker Compose environment variables for Redis Enterprise
1+
# Docker environment for local development (used by docker-compose)
2+
REDIS_ENTERPRISE_IMAGE=kurtfm/rs-arm:latest
3+
REDIS_ENTERPRISE_PLATFORM=linux/arm64
24

3-
# Redis Enterprise Docker image
4-
# Default: redislabs/redis:latest
5-
REDIS_ENTERPRISE_IMAGE=redislabs/redis:latest
5+
# Redis Enterprise connection settings
6+
REDIS_ENTERPRISE_URL=https://localhost:9443
7+
REDIS_ENTERPRISE_USER=admin@redis.local
8+
REDIS_ENTERPRISE_PASSWORD=Redis123!
9+
REDIS_ENTERPRISE_INSECURE=true
610

7-
# Docker platform
8-
# Default: linux/amd64
9-
# For ARM64 (Apple Silicon): linux/arm64
10-
REDIS_ENTERPRISE_PLATFORM=linux/amd64
11-
12-
# Note: For ARM64 systems (Apple Silicon Macs), you'll need to:
13-
# 1. Use an ARM64-compatible Redis Enterprise image
14-
# 2. Set REDIS_ENTERPRISE_PLATFORM=linux/arm64
11+
# Redis Cloud credentials (replace with your actual values)
12+
REDIS_CLOUD_API_KEY=your_api_key_here
13+
REDIS_CLOUD_API_SECRET=your_api_secret_here
14+
# REDIS_CLOUD_API_URL=https://api.redislabs.com/v1 # Optional, uses default if not set

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,4 @@ tmp/
3939

4040
# mdBook build output
4141
docs/book/
42+
support-package-*.tar.gz

crates/redis-enterprise/src/client.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ impl EnterpriseClient {
110110
EnterpriseClientBuilder::new()
111111
}
112112

113+
/// Get a reference to the underlying client (for use with handlers)
114+
pub fn client(&self) -> Arc<Client> {
115+
self.client.clone()
116+
}
117+
113118
/// Normalize URL path concatenation to avoid double slashes
114119
fn normalize_url(&self, path: &str) -> String {
115120
let base = self.base_url.trim_end_matches('/');
@@ -197,6 +202,44 @@ impl EnterpriseClient {
197202
}
198203
}
199204

205+
/// Make a GET request for binary content (e.g., tar.gz files)
206+
pub async fn get_binary(&self, path: &str) -> Result<Vec<u8>> {
207+
let url = self.normalize_url(path);
208+
debug!("GET {} (binary)", url);
209+
210+
let response = self
211+
.client
212+
.get(&url)
213+
.basic_auth(&self.username, Some(&self.password))
214+
.send()
215+
.await
216+
.map_err(|e| self.map_reqwest_error(e, &url))?;
217+
218+
trace!("Response status: {}", response.status());
219+
trace!(
220+
"Response content-type: {:?}",
221+
response.headers().get("content-type")
222+
);
223+
224+
if response.status().is_success() {
225+
let bytes = response
226+
.bytes()
227+
.await
228+
.map_err(crate::error::RestError::RequestFailed)?;
229+
Ok(bytes.to_vec())
230+
} else {
231+
let status = response.status();
232+
let error_text = response
233+
.text()
234+
.await
235+
.unwrap_or_else(|_| "Unknown error".to_string());
236+
Err(crate::error::RestError::ApiError {
237+
code: status.as_u16(),
238+
message: error_text,
239+
})
240+
}
241+
}
242+
200243
/// Make a POST request
201244
pub async fn post<B: Serialize, T: DeserializeOwned>(&self, path: &str, body: &B) -> Result<T> {
202245
let url = self.normalize_url(path);

crates/redis-enterprise/src/debuginfo.rs

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ impl DebugInfoHandler {
9898
/// Download debug info package
9999
pub async fn download(&self, task_id: &str) -> Result<Vec<u8>> {
100100
self.client
101-
.get(&format!("/v1/debuginfo/{}/download", task_id))
101+
.get_binary(&format!("/v1/debuginfo/{}/download", task_id))
102102
.await
103103
}
104104

@@ -109,27 +109,97 @@ impl DebugInfoHandler {
109109
.await
110110
}
111111

112-
/// Get all debug info across nodes - GET /v1/debuginfo/all
112+
/// Get all debug info across nodes - GET /v1/debuginfo/all (DEPRECATED)
113+
/// Use cluster_debuginfo_binary() for the new endpoint
113114
pub async fn all(&self) -> Result<Value> {
114115
self.client.get("/v1/debuginfo/all").await
115116
}
116117

117-
/// Get all debug info for a specific database - GET /v1/debuginfo/all/bdb/{uid}
118+
/// Get all debug info for a specific database - GET /v1/debuginfo/all/bdb/{uid} (DEPRECATED)
119+
/// Use database_debuginfo_binary() for the new endpoint
118120
pub async fn all_bdb(&self, bdb_uid: u32) -> Result<Value> {
119121
self.client
120122
.get(&format!("/v1/debuginfo/all/bdb/{}", bdb_uid))
121123
.await
122124
}
123125

124-
/// Get node debug info - GET /v1/debuginfo/node
126+
/// Get node debug info - GET /v1/debuginfo/node (DEPRECATED)
127+
/// Use nodes_debuginfo_binary() for the new endpoint
125128
pub async fn node(&self) -> Result<Value> {
126129
self.client.get("/v1/debuginfo/node").await
127130
}
128131

129-
/// Get node debug info for a specific database - GET /v1/debuginfo/node/bdb/{uid}
132+
/// Get node debug info for a specific database - GET /v1/debuginfo/node/bdb/{uid} (DEPRECATED)
133+
/// Use database_debuginfo_binary() for the new endpoint
130134
pub async fn node_bdb(&self, bdb_uid: u32) -> Result<Value> {
131135
self.client
132136
.get(&format!("/v1/debuginfo/node/bdb/{}", bdb_uid))
133137
.await
134138
}
139+
140+
// New binary endpoints (current API)
141+
142+
/// Get cluster debug info package as binary - GET /v1/cluster/debuginfo
143+
/// Returns a tar.gz file containing all cluster debug information
144+
pub async fn cluster_debuginfo_binary(&self) -> Result<Vec<u8>> {
145+
self.client.get_binary("/v1/cluster/debuginfo").await
146+
}
147+
148+
/// Get all nodes debug info package as binary - GET /v1/nodes/debuginfo
149+
/// Returns a tar.gz file containing debug information from all nodes
150+
pub async fn nodes_debuginfo_binary(&self) -> Result<Vec<u8>> {
151+
self.client.get_binary("/v1/nodes/debuginfo").await
152+
}
153+
154+
/// Get specific node debug info package as binary - GET /v1/nodes/{uid}/debuginfo
155+
/// Returns a tar.gz file containing debug information from a specific node
156+
pub async fn node_debuginfo_binary(&self, node_uid: u32) -> Result<Vec<u8>> {
157+
self.client
158+
.get_binary(&format!("/v1/nodes/{}/debuginfo", node_uid))
159+
.await
160+
}
161+
162+
/// Get all databases debug info package as binary - GET /v1/bdbs/debuginfo
163+
/// Returns a tar.gz file containing debug information from all databases
164+
pub async fn databases_debuginfo_binary(&self) -> Result<Vec<u8>> {
165+
self.client.get_binary("/v1/bdbs/debuginfo").await
166+
}
167+
168+
/// Get specific database debug info package as binary - GET /v1/bdbs/{uid}/debuginfo
169+
/// Returns a tar.gz file containing debug information from a specific database
170+
pub async fn database_debuginfo_binary(&self, bdb_uid: u32) -> Result<Vec<u8>> {
171+
self.client
172+
.get_binary(&format!("/v1/bdbs/{}/debuginfo", bdb_uid))
173+
.await
174+
}
175+
176+
// Deprecated binary endpoints (for backward compatibility)
177+
178+
/// Get all debug info as binary - GET /v1/debuginfo/all (DEPRECATED)
179+
/// Returns a tar.gz file - Use cluster_debuginfo_binary() instead
180+
pub async fn all_binary(&self) -> Result<Vec<u8>> {
181+
self.client.get_binary("/v1/debuginfo/all").await
182+
}
183+
184+
/// Get all debug info for a specific database as binary - GET /v1/debuginfo/all/bdb/{uid} (DEPRECATED)
185+
/// Returns a tar.gz file - Use database_debuginfo_binary() instead
186+
pub async fn all_bdb_binary(&self, bdb_uid: u32) -> Result<Vec<u8>> {
187+
self.client
188+
.get_binary(&format!("/v1/debuginfo/all/bdb/{}", bdb_uid))
189+
.await
190+
}
191+
192+
/// Get node debug info as binary - GET /v1/debuginfo/node (DEPRECATED)
193+
/// Returns a tar.gz file - Use nodes_debuginfo_binary() instead
194+
pub async fn node_binary(&self) -> Result<Vec<u8>> {
195+
self.client.get_binary("/v1/debuginfo/node").await
196+
}
197+
198+
/// Get node debug info for a specific database as binary - GET /v1/debuginfo/node/bdb/{uid} (DEPRECATED)
199+
/// Returns a tar.gz file - Use database_debuginfo_binary() instead
200+
pub async fn node_bdb_binary(&self, bdb_uid: u32) -> Result<Vec<u8>> {
201+
self.client
202+
.get_binary(&format!("/v1/debuginfo/node/bdb/{}", bdb_uid))
203+
.await
204+
}
135205
}

0 commit comments

Comments
 (0)