/* src/include/xce/cluster.h — cluster discovery and reconnect API (P2.5-1) * * xce_connect_cluster() returns endpoint_t* (cast to void*). * The SSL* is accessible as ((endpoint_t*)result)->ssl. */ #ifndef XCE_CLUSTER_H #define XCE_CLUSTER_H #include #include "client.h" /* xce_client_config_t, N_MAX_MASTERS */ #include "config.h" /* MAX_HA_CANDIDATES, XCE_MAXHOSTNAME_LEN */ /** * A single controller candidate. * name: logical name from "name=host:port" syntax (empty if omitted). * Matching against myconf->name allows multiple controllers on the same * dev host to distinguish themselves by name rather than IP address. */ typedef struct { char name[XCE_MAXNAME_LEN]; char hostname[XCE_MAXHOSTNAME_LEN]; int port; } xce_candidate_t; /** * Cluster connection configuration. * Populated by each caller (worker/scheduler/apiserver) from the --peers * CLI flag. libxce never reads config files. TLS is handled server-side * (encryption without client-side peer certificate verification). * * candidates_count must be in [1, MAX_HA_CANDIDATES]. */ typedef struct { xce_candidate_t candidates[MAX_HA_CANDIDATES]; int candidates_count; char identity[256]; /* "from" name sent to controller */ char key[256]; /* authentication key */ } xce_cluster_config_t; /** * Reconnect state machine states. */ typedef enum { XCE_RECONNECT_IDLE = 0, XCE_RECONNECT_CONNECTING, XCE_RECONNECT_BACKOFF, XCE_RECONNECT_CONNECTED, XCE_RECONNECT_FAILED } xce_reconnect_state_t; /** * Reconnect context (use xce_reconnect_* functions to operate). */ typedef struct { xce_cluster_config_t* config; xce_reconnect_state_t state; int attempt; int backoff_ms; /* current backoff delay in ms */ void* endpoint; /* endpoint_t* when CONNECTED */ } xce_reconnect_ctx_t; /* Redirect follow limit */ #define XCE_MAX_REDIRECTS 4 /* Backoff parameters — initial kept short so tests stay fast */ #define XCE_RECONNECT_INITIAL_BACKOFF_MS 100 #define XCE_RECONNECT_MAX_BACKOFF_MS 30000 #define XCE_RECONNECT_BACKOFF_MULTIPLIER 2 /* --- Public API --- */ /** * xce_cluster_config_validate() — validate a populated xce_cluster_config_t. * * Returns 0 on success, -EINVAL if: * - candidates_count < 1 or > MAX_HA_CANDIDATES * - any candidate has empty hostname or port <= 0 * - identity is empty */ int xce_cluster_config_validate(const xce_cluster_config_t* cfg); /** * xce_cluster_config_to_client_config() — convert a validated candidate list * into an xce_client_config_t's master fields (nmasters/masters/master_ports). * * Copies up to N_MAX_MASTERS candidates from cfg->candidates[] in order. * If cfg->candidates_count > N_MAX_MASTERS, copies the first N_MAX_MASTERS and * logs a WARN identifying the truncated count (mirrors xce_test's existing * clamp behavior). * * Does NOT set out->from / out->key — caller sets those separately. * Does NOT validate cfg — caller must have already called * xce_cluster_config_validate() successfully. * * Returns 0 on success, -EINVAL if cfg or out is NULL or cfg->candidates_count < 1. */ int xce_cluster_config_to_client_config(const xce_cluster_config_t* cfg, xce_client_config_t* out); /** * xce_connect_cluster() — connect to the cluster via candidate list. * * Iterates candidates in order, attempting a TLS connection to each. * Returns endpoint_t* (cast to void*) on success, NULL if all fail. */ void* xce_connect_cluster(xce_cluster_config_t* cfg); /** * xce_reconnect_init() — initialise a reconnect context. * Returns 0 on success, -EINVAL on bad arguments. */ int xce_reconnect_init(xce_reconnect_ctx_t* rctx, xce_cluster_config_t* cfg); /** * xce_reconnect_step() — drive one step of the reconnect state machine. * * Call in a loop until state == XCE_RECONNECT_CONNECTED or FAILED. * Sleeps backoff_ms in BACKOFF state (skipped when mock connect is set). * * Returns 0 to continue, -1 when in terminal state. */ int xce_reconnect_step(xce_reconnect_ctx_t* rctx); /** * xce_reconnect_fini() — release resources held by a reconnect context. */ void xce_reconnect_fini(xce_reconnect_ctx_t* rctx); /** * xce_parse_peers() — parse "--peers name=host:port,..." into cfg->candidates[]. * * Each token may be "name=host:port" or bare "host:port". * The name field is stored when present; it allows multiple daemons on the * same host to identify themselves by logical name rather than IP address. * * Returns 0 on success, -1 on empty/NULL input or any parse error. */ int xce_parse_peers(const char* peers_str, xce_cluster_config_t* cfg); /* Test seam: set to non-NULL to override the real connect (unit tests only) */ typedef void* (*xce_connect_fn_t)(const char* host, int port); void xce_cluster_set_connect_fn(xce_connect_fn_t fn); #endif /* XCE_CLUSTER_H */