Skip to content

Commit cc82bd3

Browse files
feat(workflows): add subscription setup workflow for Redis Cloud (#269)
* feat(workflows): add subscription setup workflow for Redis Cloud - Implement SubscriptionSetupWorkflow to automate subscription creation - Support database creation with modules, HA, and persistence options - Add async operation handling with progress indicators - Include dry-run mode for preview without execution - Support JSON/YAML output formats for automation - Add comprehensive documentation Closes #262 * fix: resolve clippy warnings - Allow dead_code for workflow module (will be used by more workflows) - Fix collapsible if statement in subscription_setup
1 parent 7137c87 commit cc82bd3

File tree

9 files changed

+756
-0
lines changed

9 files changed

+756
-0
lines changed

crates/redisctl/src/cli.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,9 @@ pub enum CloudCommands {
951951
/// Fixed subscription operations
952952
#[command(subcommand, name = "fixed-subscription")]
953953
FixedSubscription(CloudFixedSubscriptionCommands),
954+
/// Workflow operations for multi-step tasks
955+
#[command(subcommand)]
956+
Workflow(CloudWorkflowCommands),
954957
}
955958

956959
/// Enterprise-specific commands (placeholder for now)
@@ -1009,6 +1012,16 @@ pub enum EnterpriseCommands {
10091012
Stats(EnterpriseStatsCommands),
10101013
}
10111014

1015+
/// Cloud workflow commands
1016+
#[derive(Debug, Subcommand)]
1017+
pub enum CloudWorkflowCommands {
1018+
/// List available workflows
1019+
List,
1020+
/// Complete subscription setup with optional database
1021+
#[command(name = "subscription-setup")]
1022+
SubscriptionSetup(crate::workflows::cloud::subscription_setup::SubscriptionSetupArgs),
1023+
}
1024+
10121025
/// Enterprise workflow commands
10131026
#[derive(Debug, Subcommand)]
10141027
pub enum EnterpriseWorkflowCommands {

crates/redisctl/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,4 @@ pub(crate) mod config;
8787
pub(crate) mod connection;
8888
pub(crate) mod error;
8989
pub(crate) mod output;
90+
pub(crate) mod workflows;

crates/redisctl/src/main.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,119 @@ async fn execute_enterprise_command(
268268
}
269269
}
270270

271+
async fn handle_cloud_workflow_command(
272+
conn_mgr: &ConnectionManager,
273+
cli: &cli::Cli,
274+
workflow_cmd: &cli::CloudWorkflowCommands,
275+
) -> Result<(), RedisCtlError> {
276+
use cli::CloudWorkflowCommands::*;
277+
use workflows::{WorkflowArgs, WorkflowContext, WorkflowRegistry};
278+
279+
let output = cli.output;
280+
let profile = cli.profile.as_deref();
281+
282+
match workflow_cmd {
283+
List => {
284+
let registry = WorkflowRegistry::new();
285+
let workflows = registry.list();
286+
287+
// Filter to show only cloud workflows
288+
let cloud_workflows: Vec<_> = workflows
289+
.into_iter()
290+
.filter(|(name, _)| name.contains("subscription") || name.contains("cloud"))
291+
.collect();
292+
293+
match output {
294+
cli::OutputFormat::Json | cli::OutputFormat::Yaml => {
295+
let workflow_list: Vec<serde_json::Value> = cloud_workflows
296+
.into_iter()
297+
.map(|(name, description)| {
298+
serde_json::json!({
299+
"name": name,
300+
"description": description
301+
})
302+
})
303+
.collect();
304+
let output_format = match output {
305+
cli::OutputFormat::Json => output::OutputFormat::Json,
306+
cli::OutputFormat::Yaml => output::OutputFormat::Yaml,
307+
_ => output::OutputFormat::Table,
308+
};
309+
crate::output::print_output(
310+
serde_json::json!(workflow_list),
311+
output_format,
312+
None,
313+
)?;
314+
}
315+
_ => {
316+
println!("Available Cloud Workflows:");
317+
println!();
318+
for (name, description) in cloud_workflows {
319+
println!(" {} - {}", name, description);
320+
}
321+
}
322+
}
323+
Ok(())
324+
}
325+
SubscriptionSetup(args) => {
326+
let mut workflow_args = WorkflowArgs::new();
327+
workflow_args.insert("args", args);
328+
329+
let output_format = match output {
330+
cli::OutputFormat::Json => output::OutputFormat::Json,
331+
cli::OutputFormat::Yaml => output::OutputFormat::Yaml,
332+
cli::OutputFormat::Table | cli::OutputFormat::Auto => output::OutputFormat::Table,
333+
};
334+
335+
let context = WorkflowContext {
336+
conn_mgr: conn_mgr.clone(),
337+
profile_name: profile.map(String::from),
338+
output_format,
339+
wait_timeout: args.wait_timeout as u64,
340+
};
341+
342+
let registry = WorkflowRegistry::new();
343+
let workflow =
344+
registry
345+
.get("subscription-setup")
346+
.ok_or_else(|| RedisCtlError::ApiError {
347+
message: "Workflow not found".to_string(),
348+
})?;
349+
350+
let result = workflow
351+
.execute(context, workflow_args)
352+
.await
353+
.map_err(|e| RedisCtlError::ApiError {
354+
message: e.to_string(),
355+
})?;
356+
357+
if !result.success {
358+
return Err(RedisCtlError::ApiError {
359+
message: result.message,
360+
});
361+
}
362+
363+
// Print result as JSON/YAML if requested
364+
match output {
365+
cli::OutputFormat::Json | cli::OutputFormat::Yaml => {
366+
let result_json = serde_json::json!({
367+
"success": result.success,
368+
"message": result.message,
369+
"outputs": result.outputs,
370+
});
371+
crate::output::print_output(&result_json, output_format, None)?;
372+
}
373+
_ => {
374+
// Human output
375+
println!("{}", result.message);
376+
}
377+
}
378+
379+
Ok(())
380+
}
381+
}
382+
}
383+
271384
async fn handle_enterprise_workflow_command(
272385
conn_mgr: &ConnectionManager,
273386
profile: Option<&str>,
@@ -817,5 +930,6 @@ async fn execute_cloud_command(
817930
)
818931
.await
819932
}
933+
Workflow(workflow_cmd) => handle_cloud_workflow_command(conn_mgr, cli, workflow_cmd).await,
820934
}
821935
}

crates/redisctl/src/output.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ impl Default for OutputFormat {
1919
}
2020
}
2121

22+
impl OutputFormat {
23+
pub fn is_json(&self) -> bool {
24+
matches!(self, Self::Json)
25+
}
26+
27+
pub fn is_yaml(&self) -> bool {
28+
matches!(self, Self::Yaml)
29+
}
30+
}
31+
2232
pub fn print_output<T: Serialize>(
2333
data: T,
2434
format: OutputFormat,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod subscription_setup;

0 commit comments

Comments
 (0)