feat(controller-peers-env-fallback): fall back to XCE_CONTROLLER_PEERS in the controller and name the value's source (task 2.3)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-03 06:14:25 +00:00
parent 25e2807355
commit 64d3429cd8
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
3 changed files with 154 additions and 14 deletions

View file

@ -84,7 +84,11 @@ static struct argp_option options[] = {
{ "myname", 'n', "STR", 0, "The name of controller" },
{ "peers", OPT_PEERS, "PEERS", 0,
"HA peer list: comma-separated host:port pairs ordered by priority "
"(e.g. ctrl01:9990,ctrl02:9990). Index 0 = highest priority (primary)." },
"(e.g. ctrl01:9990,ctrl02:9990). Index 0 = highest priority (primary). "
"When omitted, the XCE_CONTROLLER_PEERS environment variable (same "
"format) is used instead; with that variable unset or empty the "
"controller runs standalone, so unset or empty it for one invocation "
"to opt out (e.g. XCE_CONTROLLER_PEERS= xce_controller ...)." },
{ "conf-keep-n", OPT_CONF_KEEP_N, "N", 0,
"Versioned config files to retain (default: 2)" },
{ "conf-stale-timeout", OPT_CONF_STALE_TIMEOUT, "SECS", 0,
@ -458,12 +462,24 @@ int main(int argc, char* argv[])
return -1;
}
if (arguments.peers_str) {
if (xce_parse_peers(arguments.peers_str, &myconf.peers_cfg) != 0) {
/* Req 1.1-1.3, 1.7: an explicit --peers wins; otherwise a non-empty
* XCE_CONTROLLER_PEERS supplies the list (unset or empty = standalone).
* peers_src names where the list came from in every message about it
* (Req 1.4). */
const char* peers_str = arguments.peers_str;
int peers_from_env = 0;
if (peers_str == NULL) {
peers_str = xce_controller_peers_env();
peers_from_env = (peers_str != NULL);
}
const char* peers_src = peers_from_env ? "XCE_CONTROLLER_PEERS" : "--peers";
if (peers_str) {
if (xce_parse_peers(peers_str, &myconf.peers_cfg) != 0) {
fprintf(stderr,
"xce_controller: --peers parse error: expected "
"xce_controller: %s parse error: expected "
"host:port,... got: %s\n",
arguments.peers_str);
peers_src, peers_str);
mylog_fini();
return 1;
}
@ -471,27 +487,29 @@ int main(int argc, char* argv[])
/* Req 4.1: reject even-N peer counts (cannot form safe majority) */
if (validate_peer_count(myconf.peers_cfg.candidates_count) != 0) {
fprintf(stderr,
"xce_controller: --peers: even peer count (%d) is not "
"xce_controller: %s: even peer count (%d) is not "
"allowed; Raft quorum requires an odd number of nodes "
"(1, 3, 5, ...)\n",
myconf.peers_cfg.candidates_count);
peers_src, myconf.peers_cfg.candidates_count);
mylog_fini();
return 1;
}
strncpy(myconf.peers_cfg.identity, arguments.myname,
sizeof(myconf.peers_cfg.identity) - 1);
INFO("HA mode: %d controller candidates, identity=%s",
myconf.peers_cfg.candidates_count, myconf.peers_cfg.identity);
INFO("HA mode: %d controller candidates, identity=%s%s",
myconf.peers_cfg.candidates_count, myconf.peers_cfg.identity,
peers_from_env ? " (from XCE_CONTROLLER_PEERS)" : "");
for (int i = 0; i < myconf.peers_cfg.candidates_count; i++) {
if (myconf.peers_cfg.candidates[i].hostname[0] == '\0' ||
myconf.peers_cfg.candidates[i].port == 0) {
WARN("--peers: candidate %d has empty host or port 0 — "
"check peer string", i);
WARN("%s: candidate %d has empty host or port 0 — "
"check peer string", peers_src, i);
}
}
} else {
INFO("No --peers flag; running as single-node primary");
INFO("No --peers flag and XCE_CONTROLLER_PEERS unset or empty; "
"running as single-node primary");
}
/* Req 4.2: cert pre-flight — fail early with a clear path in the message */