Skip to main content

palladium_cli/commands/
cluster.rs

1use clap::{Args, Subcommand};
2use schemars::JsonSchema;
3use serde::Serialize;
4
5use crate::client::{ControlPlaneClient, Endpoint};
6use crate::output;
7use crate::CliResult;
8
9#[derive(Subcommand, Debug, Serialize, JsonSchema)]
10#[serde(rename_all = "kebab-case")]
11pub enum ClusterCommand {
12    /// Show cluster status.
13    Status(ClusterStatusArgs),
14    /// List cluster members.
15    Members(ClusterMembersArgs),
16    /// Join a cluster using seed node addresses.
17    Join(ClusterJoinArgs),
18    /// Leave the cluster.
19    Leave(ClusterLeaveArgs),
20    /// Show gossip configuration and peer count.
21    GossipStatus(ClusterGossipStatusArgs),
22    /// Set local member metadata.
23    SetMeta(ClusterSetMetaArgs),
24}
25
26#[derive(Args, Debug, Serialize, JsonSchema)]
27#[serde(rename_all = "kebab-case")]
28pub struct ClusterStatusArgs {
29    /// Output in JSON format.
30    #[arg(long)]
31    pub json: bool,
32}
33
34#[derive(Args, Debug, Serialize, JsonSchema)]
35#[serde(rename_all = "kebab-case")]
36pub struct ClusterMembersArgs {
37    /// Output in JSON format.
38    #[arg(long)]
39    pub json: bool,
40}
41
42#[derive(Args, Debug, Serialize, JsonSchema)]
43#[serde(rename_all = "kebab-case")]
44pub struct ClusterJoinArgs {
45    /// Seed node address (host:port). Repeat for multiple seeds.
46    #[arg(long)]
47    pub seed: Vec<String>,
48}
49
50#[derive(Args, Debug, Serialize, JsonSchema)]
51#[serde(rename_all = "kebab-case")]
52pub struct ClusterLeaveArgs {
53    /// Output in JSON format.
54    #[arg(long)]
55    pub json: bool,
56}
57
58#[derive(Args, Debug, Serialize, JsonSchema)]
59#[serde(rename_all = "kebab-case")]
60pub struct ClusterGossipStatusArgs {
61    /// Output in JSON format.
62    #[arg(long)]
63    pub json: bool,
64}
65
66#[derive(Args, Debug, Serialize, JsonSchema)]
67#[serde(rename_all = "kebab-case")]
68pub struct ClusterSetMetaArgs {
69    /// Metadata key.
70    #[arg(long)]
71    pub key: String,
72    /// Metadata value.
73    #[arg(long)]
74    pub value: String,
75    /// Output in JSON format.
76    #[arg(long)]
77    pub json: bool,
78}
79
80pub fn run(cmd: ClusterCommand, endpoint: &Endpoint) -> CliResult {
81    match cmd {
82        ClusterCommand::Status(args) => run_status(&args, endpoint),
83        ClusterCommand::Members(args) => run_members(&args, endpoint),
84        ClusterCommand::Join(args) => run_join(&args, endpoint),
85        ClusterCommand::Leave(args) => run_leave(&args, endpoint),
86        ClusterCommand::GossipStatus(args) => run_gossip_status(&args, endpoint),
87        ClusterCommand::SetMeta(args) => run_set_meta(&args, endpoint),
88    }
89}
90
91fn run_status(args: &ClusterStatusArgs, endpoint: &Endpoint) -> CliResult {
92    let mut client = ControlPlaneClient::connect_endpoint(endpoint)?;
93    let result = client.call("cluster.status", serde_json::Value::Null)?;
94    if args.json {
95        println!("{}", serde_json::to_string_pretty(&result)?);
96    } else {
97        print!("{}", output::format_cluster_status(&result));
98    }
99    Ok(())
100}
101
102fn run_members(args: &ClusterMembersArgs, endpoint: &Endpoint) -> CliResult {
103    let mut client = ControlPlaneClient::connect_endpoint(endpoint)?;
104    let result = client.call("cluster.members", serde_json::Value::Null)?;
105    if args.json {
106        println!("{}", serde_json::to_string_pretty(&result)?);
107    } else {
108        let members = result.as_array().cloned().unwrap_or_default();
109        print!("{}", output::format_cluster_members(&members));
110    }
111    Ok(())
112}
113
114fn run_join(args: &ClusterJoinArgs, endpoint: &Endpoint) -> CliResult {
115    let mut client = ControlPlaneClient::connect_endpoint(endpoint)?;
116    let params = serde_json::json!({
117        "seed_nodes": args.seed,
118    });
119    let result = client.call("cluster.join", params)?;
120    println!("{}", serde_json::to_string_pretty(&result)?);
121    Ok(())
122}
123
124fn run_leave(args: &ClusterLeaveArgs, endpoint: &Endpoint) -> CliResult {
125    let mut client = ControlPlaneClient::connect_endpoint(endpoint)?;
126    let result = client.call("cluster.leave", serde_json::Value::Null)?;
127    if args.json {
128        println!("{}", serde_json::to_string_pretty(&result)?);
129    } else {
130        println!("Left cluster.");
131    }
132    Ok(())
133}
134
135fn run_gossip_status(_args: &ClusterGossipStatusArgs, endpoint: &Endpoint) -> CliResult {
136    let mut client = ControlPlaneClient::connect_endpoint(endpoint)?;
137    let result = client.call("cluster.gossip.status", serde_json::Value::Null)?;
138    println!("{}", serde_json::to_string_pretty(&result)?);
139    Ok(())
140}
141
142fn run_set_meta(args: &ClusterSetMetaArgs, endpoint: &Endpoint) -> CliResult {
143    let mut client = ControlPlaneClient::connect_endpoint(endpoint)?;
144    let result = client.call(
145        "cluster.member.set-meta",
146        serde_json::json!({"key": args.key, "value": args.value}),
147    )?;
148    if args.json {
149        println!("{}", serde_json::to_string_pretty(&result)?);
150    } else {
151        println!("Member metadata updated.");
152    }
153    Ok(())
154}