feat(controller-peers-env-fallback): fall back to XCE_CONTROLLER_PEERS in the worker (task 3.2)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-03 07:07:19 +00:00
parent 55193fcb4b
commit 05a7e6dd0c
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
3 changed files with 203 additions and 17 deletions

View file

@ -346,7 +346,7 @@ int main(int argc, char* argv[])
* (dirty-content-fingerprint task 3.2). */
static char version_display_buf[128];
xce_cluster_config_t cluster_cfg = {0};
int use_ha = 0; /* 1 when --peers given */
int use_ha = 0; /* 1 with a candidate list */
signal(SIGPIPE, SIG_IGN);
@ -443,7 +443,22 @@ int main(int argc, char* argv[])
default:
printf("Usage: %s [-n name] [-l log_dir] [-v] "
"[--peers ctrl-1=host:port,...] "
"[-h help]\n",
"[-h help]\n"
" --peers LIST HA controller candidate list: "
"[name=]host:port,...\n"
" When omitted, the XCE_CONTROLLER_PEERS "
"environment variable (same\n"
" format) is used instead; with that "
"variable unset or empty the\n"
" worker takes the single-controller path "
"(localhost:9990), so\n"
" unset or empty it for one invocation to "
"opt out\n"
" (e.g. XCE_CONTROLLER_PEERS= xcecd ...).\n"
" sudo drops XCE_CONTROLLER_PEERS (default "
"env_reset) unless it is\n"
" preserved, e.g. "
"sudo --preserve-env=XCE_CONTROLLER_PEERS xcecd ...\n",
argv[0]);
return 0;
}
@ -467,39 +482,60 @@ int main(int argc, char* argv[])
logdir = strdup("/tmp");
}
/* P2.5-5: populate and validate xce_cluster_config_t when --peers given */
/* P2.5-5: populate and validate xce_cluster_config_t when a candidate
* list is given. controller-peers-env-fallback Req 1.1-1.3, 1.7: an
* explicit --peers wins; otherwise a non-empty XCE_CONTROLLER_PEERS
* supplies the list (unset or empty = single-controller path).
* peers_src names where the list came from in every message about it
* (Req 1.4). */
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, &cluster_cfg) != 0) {
fprintf(stderr,
"xcecd: --peers parse error: expected "
"xcecd: %s parse error: expected "
"\"[name=]host:port,...\" got \"%s\"\n",
peers_str);
peers_src, peers_str);
FREEUP(logdir);
return 1;
}
strncpy(cluster_cfg.identity, config.virutal_name,
sizeof(cluster_cfg.identity) - 1);
/* identity comes from -n, which can be given as an empty string;
* xce_parse_peers() caps the candidate count itself, so an empty
* host, a port of 0 or an empty -n name is what validation rejects. */
if (xce_cluster_config_validate(&cluster_cfg) != 0) {
fprintf(stderr,
"xcecd: invalid cluster config (missing identity?). "
"Use -n.\n");
"xcecd: %s: invalid cluster config (empty host or port "
"0, or empty -n name?) in \"%s\"\n",
peers_src, peers_str);
FREEUP(logdir);
return 1;
}
if (cluster_cfg.candidates_count > N_MAX_MASTERS) {
fprintf(stderr,
"xcecd: WARN: --peers has %d candidates; "
"xcecd: WARN: %s has %d candidates; "
"only <=%d validated for HA\n",
cluster_cfg.candidates_count, N_MAX_MASTERS);
peers_src, cluster_cfg.candidates_count, N_MAX_MASTERS);
}
/* The version stays last on the line, as on the scheduler's. */
XCECD_LOG(XCE_LOG_INFO,
"HA cluster config: %d candidate(s), identity=%s, "
"HA cluster config: %d candidate(s), identity=%s%s, "
"version %s",
cluster_cfg.candidates_count, cluster_cfg.identity,
peers_from_env ? " (from XCE_CONTROLLER_PEERS)" : "",
version_buf);
use_ha = 1;
} else {
XCECD_LOG(XCE_LOG_INFO,
"No --peers flag; using single-controller path "
"(host=%s port=%d), version %s",
"No --peers flag and XCE_CONTROLLER_PEERS unset or empty; "
"using single-controller path (host=%s port=%d), "
"version %s",
config.remote_host, config.remote_port, version_buf);
}
/* P2.2 fix (component-failover-adoption, task 2.2, Req 1.4): assign
@ -509,7 +545,8 @@ int main(int argc, char* argv[])
* this was assigned directly onto cc_ctx AFTER xcecd_start() returned,
* which was already too late -- the first join had already run against
* an empty (zero-initialized) cluster_cfg, silently ignoring --peers on
* startup. When no --peers was given, cluster_cfg stays {0}
* startup. With no candidate list (no --peers, no XCE_CONTROLLER_PEERS),
* cluster_cfg stays {0}
* (candidates_count == 0), which is exactly the "no HA list configured"
* case the synthesize-single-candidate path already handles. */
config.cluster_cfg = cluster_cfg;