Skip to content

Commit 5dd7f07

Browse files
feat: enhance Docker Compose setup and improve error messages (#363)
* feat: enhance Docker Compose with comprehensive redisctl examples - Add second database creation with different parameters (cache vs persistent) - Add license information service - Add module listing service - Add detailed node information with JMESPath filtering - Add user listing service - Add cluster policy and alerts monitoring - Add comprehensive cluster info with key fields - Fix database persistence field (use data_persistence not persistence) - Fix Docker workflow version extraction to use Cargo.toml when running from branch - Expose port 12002 for second database - Remove --wait flags (not available in current version) * docs: enhance JMESPath documentation with Enterprise examples - Add Quick Start section with simple examples - Add comprehensive Enterprise-specific examples for databases, nodes, modules - Include practical examples for license checking and alert monitoring - Reorganize to separate Cloud and Enterprise examples - Add examples showing memory conversion and status filtering * fix: improve error messages to include HTTP status codes - Enhanced error conversion to preserve HTTP status codes and details - Replaced .context() calls with proper error conversion for REST API calls - Now shows specific messages like '401 Unauthorized' and '404 Not Found' - Maintains backward compatibility while providing clearer error information * fix: resolve formatting and clippy warnings - Applied cargo fmt to fix formatting issues - Simplified redundant closures per clippy recommendations - Changed map_err(|e| RedisCtlError::from(e)) to map_err(RedisCtlError::from) * fix: apply cargo fmt to resolve CI formatting issues
1 parent c6d1e31 commit 5dd7f07

30 files changed

+639
-179
lines changed

.github/workflows/docker.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,27 @@ jobs:
2828
id: version
2929
run: |
3030
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
31-
# For manual dispatch, use the current ref name or latest tag
31+
# For manual dispatch, use the current ref name or extract from Cargo.toml
3232
if [[ "${{ github.ref }}" == refs/tags/v* ]]; then
33+
# Running from a tag
3334
VERSION=${GITHUB_REF#refs/tags/v}
3435
else
35-
VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.0.0")
36+
# Running from a branch - extract version from Cargo.toml
37+
VERSION=$(grep '^version = ' crates/redisctl/Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
38+
echo "Extracted version from Cargo.toml: $VERSION"
3639
fi
3740
else
41+
# Tag push event
3842
VERSION=${GITHUB_REF#refs/tags/v}
3943
fi
40-
echo "Extracted version: $VERSION"
44+
45+
# Validate version format
46+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
47+
echo "Warning: Invalid version format: $VERSION"
48+
VERSION="0.0.0"
49+
fi
50+
51+
echo "Final version: $VERSION"
4152
echo "version=$VERSION" >> $GITHUB_OUTPUT
4253
echo "major=$(echo $VERSION | cut -d. -f1)" >> $GITHUB_OUTPUT
4354
echo "minor=$(echo $VERSION | cut -d. -f1-2)" >> $GITHUB_OUTPUT

crates/redisctl/src/commands/enterprise/actions.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::error::RedisCtlError;
12
use anyhow::Context;
23
use clap::Subcommand;
34
use redis_enterprise::ActionHandler;
@@ -81,7 +82,7 @@ impl ActionCommands {
8182
.await
8283
.context("Failed to list actions (v2)")?
8384
} else {
84-
handler.list().await.context("Failed to list actions")?
85+
handler.list().await.map_err(RedisCtlError::from)?
8586
};
8687

8788
// Convert to JSON Value for filtering and output

crates/redisctl/src/commands/enterprise/alerts.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use anyhow::{Context, Result as AnyhowResult};
1+
use crate::error::RedisCtlError;
2+
use anyhow::Result as AnyhowResult;
23
use clap::Subcommand;
34
use serde_json::Value;
45

@@ -283,7 +284,7 @@ async fn handle_cluster_alerts(
283284
let response = client
284285
.get::<Value>(&endpoint)
285286
.await
286-
.context("Failed to get cluster alerts")?;
287+
.map_err(RedisCtlError::from)?;
287288

288289
let response = if let Some(q) = query {
289290
super::utils::apply_jmespath(&response, q)?
@@ -314,7 +315,7 @@ async fn handle_node_alerts(
314315
let response = client
315316
.get::<Value>(&endpoint)
316317
.await
317-
.context("Failed to get node alerts")?;
318+
.map_err(RedisCtlError::from)?;
318319

319320
let response = if let Some(q) = query {
320321
super::utils::apply_jmespath(&response, q)?
@@ -345,7 +346,7 @@ async fn handle_database_alerts(
345346
let response = client
346347
.get::<Value>(&endpoint)
347348
.await
348-
.context("Failed to get database alerts")?;
349+
.map_err(RedisCtlError::from)?;
349350

350351
let response = if let Some(q) = query {
351352
super::utils::apply_jmespath(&response, q)?
@@ -368,7 +369,7 @@ async fn handle_get_alert_settings(
368369
let response = client
369370
.get::<Value>("/v1/cluster")
370371
.await
371-
.context("Failed to get cluster configuration")?;
372+
.map_err(RedisCtlError::from)?;
372373

373374
// Extract alert_settings from the cluster config
374375
let alert_settings = response
@@ -404,7 +405,7 @@ async fn handle_update_alert_settings(
404405
let response = client
405406
.put::<_, Value>("/v1/cluster", &update_payload)
406407
.await
407-
.context("Failed to update alert settings")?;
408+
.map_err(RedisCtlError::from)?;
408409

409410
let response = if let Some(q) = query {
410411
super::utils::apply_jmespath(&response, q)?

crates/redisctl/src/commands/enterprise/bdb_group.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use anyhow::Context;
1+
use crate::error::RedisCtlError;
22
use clap::Subcommand;
33

44
use crate::cli::OutputFormat;
@@ -82,7 +82,7 @@ impl BdbGroupCommands {
8282
let response: serde_json::Value = client
8383
.get("/v1/bdb_groups")
8484
.await
85-
.context("Failed to list database groups")?;
85+
.map_err(RedisCtlError::from)?;
8686

8787
let output_data = if let Some(q) = query {
8888
super::utils::apply_jmespath(&response, q)?
@@ -96,7 +96,7 @@ impl BdbGroupCommands {
9696
let response: serde_json::Value = client
9797
.get(&format!("/v1/bdb_groups/{}", uid))
9898
.await
99-
.context("Failed to get database group")?;
99+
.map_err(RedisCtlError::from)?;
100100

101101
let output_data = if let Some(q) = query {
102102
super::utils::apply_jmespath(&response, q)?
@@ -112,7 +112,7 @@ impl BdbGroupCommands {
112112
let response: serde_json::Value = client
113113
.post("/v1/bdb_groups", &json_data)
114114
.await
115-
.context("Failed to create database group")?;
115+
.map_err(RedisCtlError::from)?;
116116

117117
let output_data = if let Some(q) = query {
118118
super::utils::apply_jmespath(&response, q)?
@@ -128,7 +128,7 @@ impl BdbGroupCommands {
128128
let response: serde_json::Value = client
129129
.put(&format!("/v1/bdb_groups/{}", uid), &json_data)
130130
.await
131-
.context("Failed to update database group")?;
131+
.map_err(RedisCtlError::from)?;
132132

133133
let output_data = if let Some(q) = query {
134134
super::utils::apply_jmespath(&response, q)?
@@ -148,7 +148,7 @@ impl BdbGroupCommands {
148148
client
149149
.delete(&format!("/v1/bdb_groups/{}", uid))
150150
.await
151-
.context("Failed to delete database group")?;
151+
.map_err(RedisCtlError::from)?;
152152

153153
println!("Database group {} deleted successfully", uid);
154154
}
@@ -161,7 +161,7 @@ impl BdbGroupCommands {
161161
let mut group_data: serde_json::Value = client
162162
.get(&format!("/v1/bdb_groups/{}", group_uid))
163163
.await
164-
.context("Failed to get database group")?;
164+
.map_err(RedisCtlError::from)?;
165165

166166
// Add database to the group
167167
if let Some(bdbs) = group_data["bdbs"].as_array_mut() {
@@ -179,7 +179,7 @@ impl BdbGroupCommands {
179179
let response: serde_json::Value = client
180180
.put(&format!("/v1/bdb_groups/{}", group_uid), &group_data)
181181
.await
182-
.context("Failed to add database to group")?;
182+
.map_err(RedisCtlError::from)?;
183183

184184
println!("Database {} added to group {}", database, group_uid);
185185

@@ -199,7 +199,7 @@ impl BdbGroupCommands {
199199
let mut group_data: serde_json::Value = client
200200
.get(&format!("/v1/bdb_groups/{}", group_uid))
201201
.await
202-
.context("Failed to get database group")?;
202+
.map_err(RedisCtlError::from)?;
203203

204204
// Remove database from the group
205205
if let Some(bdbs) = group_data["bdbs"].as_array_mut() {
@@ -218,7 +218,7 @@ impl BdbGroupCommands {
218218
let response: serde_json::Value = client
219219
.put(&format!("/v1/bdb_groups/{}", group_uid), &group_data)
220220
.await
221-
.context("Failed to remove database from group")?;
221+
.map_err(RedisCtlError::from)?;
222222

223223
println!("Database {} removed from group {}", database, group_uid);
224224

@@ -234,7 +234,7 @@ impl BdbGroupCommands {
234234
let response: serde_json::Value = client
235235
.get(&format!("/v1/bdb_groups/{}", group_uid))
236236
.await
237-
.context("Failed to get database group")?;
237+
.map_err(RedisCtlError::from)?;
238238

239239
// Extract just the databases list
240240
let databases = &response["bdbs"];

crates/redisctl/src/commands/enterprise/bootstrap.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ async fn handle_bootstrap_status(
7575
let response = client
7676
.get::<Value>("/v1/bootstrap")
7777
.await
78-
.context("Failed to get bootstrap status")?;
78+
.map_err(RedisCtlError::from)?;
7979

8080
let result = if let Some(q) = query {
8181
utils::apply_jmespath(&response, q)?
@@ -102,7 +102,7 @@ async fn handle_create_cluster(
102102
let response = client
103103
.post_raw("/v1/bootstrap/create_cluster", payload)
104104
.await
105-
.context("Failed to create cluster")?;
105+
.map_err(RedisCtlError::from)?;
106106

107107
let result = if let Some(q) = query {
108108
utils::apply_jmespath(&response, q)?
@@ -129,7 +129,7 @@ async fn handle_join_cluster(
129129
let response = client
130130
.post_raw("/v1/bootstrap/join_cluster", payload)
131131
.await
132-
.context("Failed to join cluster")?;
132+
.map_err(RedisCtlError::from)?;
133133

134134
let result = if let Some(q) = query {
135135
utils::apply_jmespath(&response, q)?

crates/redisctl/src/commands/enterprise/cluster_impl.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use crate::cli::OutputFormat;
66
use crate::connection::ConnectionManager;
7+
use crate::error::RedisCtlError;
78
use crate::error::Result as CliResult;
89
use anyhow::Context;
910
use redis_enterprise::alerts::AlertHandler;
@@ -164,7 +165,7 @@ pub async fn bootstrap_cluster(
164165
let result = client
165166
.post_raw("/v1/bootstrap", bootstrap_data)
166167
.await
167-
.context("Failed to bootstrap cluster")?;
168+
.map_err(RedisCtlError::from)?;
168169
let data = handle_output(result, output_format, query)?;
169170
print_formatted_output(data, output_format)?;
170171
Ok(())

crates/redisctl/src/commands/enterprise/cm_settings.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::error::RedisCtlError;
12
use anyhow::Context;
23
use clap::Subcommand;
34

@@ -100,7 +101,7 @@ impl CmSettingsCommands {
100101
let response: serde_json::Value = client
101102
.get("/v1/cm_settings")
102103
.await
103-
.context("Failed to get cluster manager settings")?;
104+
.map_err(RedisCtlError::from)?;
104105

105106
let output_data = if let Some(s) = setting {
106107
// Use the setting parameter as a JMESPath query
@@ -120,11 +121,10 @@ impl CmSettingsCommands {
120121

121122
let json_data = super::utils::read_json_data(data)?;
122123

123-
let response: serde_json::Value =
124-
client
125-
.put("/v1/cm_settings", &json_data)
126-
.await
127-
.context("Failed to update cluster manager settings")?;
124+
let response: serde_json::Value = client
125+
.put("/v1/cm_settings", &json_data)
126+
.await
127+
.map_err(RedisCtlError::from)?;
128128

129129
println!("Cluster manager settings updated successfully");
130130

@@ -146,7 +146,7 @@ impl CmSettingsCommands {
146146
let mut settings: serde_json::Value = client
147147
.get("/v1/cm_settings")
148148
.await
149-
.context("Failed to get current settings")?;
149+
.map_err(RedisCtlError::from)?;
150150

151151
// Parse value as JSON if possible, otherwise as string
152152
let parsed_value: serde_json::Value =
@@ -173,7 +173,7 @@ impl CmSettingsCommands {
173173
let response: serde_json::Value = client
174174
.put("/v1/cm_settings", &settings)
175175
.await
176-
.context("Failed to update setting")?;
176+
.map_err(RedisCtlError::from)?;
177177

178178
println!("Setting '{}' updated to: {}", name, value);
179179

@@ -198,7 +198,7 @@ impl CmSettingsCommands {
198198
let response: serde_json::Value = client
199199
.put("/v1/cm_settings", &serde_json::json!({}))
200200
.await
201-
.context("Failed to reset settings")?;
201+
.map_err(RedisCtlError::from)?;
202202

203203
println!("Cluster manager settings reset to defaults");
204204

@@ -214,7 +214,7 @@ impl CmSettingsCommands {
214214
let settings: serde_json::Value = client
215215
.get("/v1/cm_settings")
216216
.await
217-
.context("Failed to get settings for export")?;
217+
.map_err(RedisCtlError::from)?;
218218

219219
if output == "-" {
220220
// Output to stdout
@@ -240,7 +240,7 @@ impl CmSettingsCommands {
240240
let response: serde_json::Value = client
241241
.put("/v1/cm_settings", &json_data)
242242
.await
243-
.context("Failed to import settings")?;
243+
.map_err(RedisCtlError::from)?;
244244

245245
println!("Settings imported successfully");
246246

@@ -279,7 +279,7 @@ impl CmSettingsCommands {
279279
let settings: serde_json::Value = client
280280
.get("/v1/cm_settings")
281281
.await
282-
.context("Failed to get settings")?;
282+
.map_err(RedisCtlError::from)?;
283283

284284
// Extract top-level keys as categories
285285
let categories = if let Some(obj) = settings.as_object() {
@@ -301,7 +301,7 @@ impl CmSettingsCommands {
301301
let settings: serde_json::Value = client
302302
.get("/v1/cm_settings")
303303
.await
304-
.context("Failed to get settings")?;
304+
.map_err(RedisCtlError::from)?;
305305

306306
// Extract specific category
307307
let category_data = &settings[category];

0 commit comments

Comments
 (0)