feat(controller-peers-env-fallback): default the harness masters setting from XCE_CONTROLLER_PEERS (task 3.3)

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

View file

@ -259,7 +259,9 @@ delaymgr_t delaymgr;
static setting_t settings[] = {
/* HA controller list: comma-separated "name=host:port" or "host:port"
* entries (same syntax as the controller --peers flag), enabling the
* test client to fail over across controller candidates. */
* test client to fail over across controller candidates. main() replaces
* this built-in default with XCE_CONTROLLER_PEERS when that is set and
* non-empty (masters_default_from_env), before the rc file is loaded. */
{ (char*)"masters", (char*)NULL, (char*)"localhost:9990" },
{ (char*)"secret", (char*)NULL, (char*)"keepSecret" },
{ (char*)"myname", (char*)NULL, (char*)"localhost" },
@ -412,6 +414,11 @@ static const char USAGE_SET[] =
" regardless of TTY state, until +y\n"
" +y require confirmation before those\n"
" commands again (default)\n"
" masters default: $XCE_CONTROLLER_PEERS when it\n"
" is set and non-empty, else localhost:9990;\n"
" an explicit `set masters' wins. To opt\n"
" out, leave XCE_CONTROLLER_PEERS unset or\n"
" empty (e.g. XCE_CONTROLLER_PEERS= xce_test)\n"
"example: set masters ctrl1=localhost:9991,ctrl2=localhost:9992\n";
static const char USAGE_JOIN_WORKER_HEAD[] =
@ -1011,6 +1018,54 @@ static xce_client_config_t config;
* persists for the lifetime of the client context. */
static xce_cluster_config_t master_cfg;
/* The `masters` row of settings[] (the table every context shares). */
static setting_t* masters_setting(void)
{
int i;
for (i = 0; settings[i].key; i++) {
if (strcmp(settings[i].key, "masters") == 0) {
return &settings[i];
}
}
return NULL;
}
/* controller-peers-env-fallback (Req 1.2, 1.3, 1.7): at startup, before the
* rc file is loaded or any command runs, take the `masters` default from
* XCE_CONTROLLER_PEERS when it is set and non-empty; otherwise the built-in
* default stays. An explicit `set masters` (typed, scripted or in the rc
* file) still wins through get_setting()'s value-before-defval rule. The
* getenv() storage stays valid for the process lifetime and is only read. */
static void masters_default_from_env(void)
{
const char* env = xce_controller_peers_env();
setting_t* m = masters_setting();
if ((env != NULL) && (m != NULL)) {
m->defval = (char*)env;
}
}
/* Source note appended to enter_ctx()'s invalid-`masters` message (Req 1.4):
* an explicit `set masters` is blamed whenever one ran; otherwise the
* variable is named when it supplied the default; otherwise (the built-in
* default) the message is today's, with no note. */
static const char* masters_source_note(void)
{
setting_t* m = masters_setting();
if ((m != NULL) && (m->value != NULL)) {
return " (explicit `set masters')";
}
if (xce_controller_peers_env() != NULL) {
return " (default from XCE_CONTROLLER_PEERS)";
}
return "";
}
int enter_ctx(command_ctx_t* ctx)
{
char* masters = get_setting("masters");
@ -1023,8 +1078,8 @@ int enter_ctx(command_ctx_t* ctx)
memset(&master_cfg, 0, sizeof(master_cfg));
if ((masters == NULL) || (xce_parse_peers(masters, &master_cfg) != 0) ||
(master_cfg.candidates_count < 1)) {
fprintf(stderr, "enter_ctx: invalid 'masters' setting `%s'\n",
masters ? masters : "(null)");
fprintf(stderr, "enter_ctx: invalid 'masters' setting `%s'%s\n",
masters ? masters : "(null)", masters_source_note());
return -EINVAL;
}
@ -7061,6 +7116,10 @@ int main(int argc, char* argv[])
rl_attempted_completion_function = xcetest_complete;
}
/* controller-peers-env-fallback task 3.3: must run before the rc file
* (and every later command) so an explicit `set masters` there wins. */
masters_default_from_env();
/* xce-test-rc-file task 2.2: auto-load $XDG_CONFIG_HOME/xce_test/rc (or
* $HOME/.config/xce_test/rc) before the read loop starts, for both
* interactive and scripted sessions alike -- unless --norc was given.

View file

@ -96,8 +96,6 @@ unset XCE_CONTROLLER_PEERS
CONTROLLER_BIN=""
SCHEDULER_BIN=""
WORKER_BIN=""
# Set by preflight; the harness cases (task 3.3) are its only reader.
# shellcheck disable=SC2034
HARNESS_BIN=""
# component:relative-path:variable
@ -181,10 +179,13 @@ stop_all() {
# left in RUN_RC and <dir>/rc (timed out: 124 with coreutils timeout, 143
# with busybox timeout, 137 if it had to be SIGKILLed). `-k` rather than
# `--kill-after` because busybox timeout only accepts the short form.
# stdin is /dev/null, or the file named by RUN_STDIN when the caller sets it
# for this one call (`RUN_STDIN=<file> run_to_completion ...`) -- xce_test
# reads its script only from stdin.
run_to_completion() {
local dir="$1" secs="$2"; shift 2
local dir="$1" secs="$2" in="${RUN_STDIN:-/dev/null}"; shift 2
RUN_RC=0
timeout -k 2 "$secs" env "$@" </dev/null \
timeout -k 2 "$secs" env "$@" <"$in" \
>"$dir/stdout" 2>"$dir/stderr" || RUN_RC=$?
echo "$RUN_RC" >"$dir/rc"
}
@ -797,6 +798,92 @@ case_worker_help_names_variable() {
add_case case_worker_help_names_variable \
"worker: -h documents XCE_CONTROLLER_PEERS, the opt-out and that sudo drops it (1.5, 1.7)"
# ── Task 3.3 (Req 1.1-1.5, 1.7): the harness (xce_test) takes its `masters`
# default from XCE_CONTROLLER_PEERS; an explicit `set masters` wins. Always
# run with --norc so the developer's own rc file cannot change the result,
# with the script fed on stdin. A bare `set` prints every setting with no
# connection attempt. The connecting command (`client`) is only ever given an
# INVALID masters value: enter_ctx() rejects it before any connection, so no
# case talks to a controller.
# run_harness <dir> <script-text> <env-args...>
# Write <script-text> to <dir>/script and run `xce_test --norc` on it.
run_harness() {
local dir="$1" script="$2"; shift 2
printf '%s' "$script" >"$dir/script"
RUN_STDIN="$dir/script" run_to_completion "$dir" 10 "$@" "$HARNESS_BIN" --norc
}
HARNESS_INVALID_RE="enter_ctx: invalid 'masters' setting"
case_harness_env_default() {
local d; d="$(new_scratch harness-env-default)"
run_harness "$d" $'set\n' "XCE_CONTROLLER_PEERS=$ENV3"
assert_rc 0 || return 1
assert_grep "$d/stdout" "^masters +:<not set> default: $ENV3\$" "stdout" || return 1
}
add_case case_harness_env_default \
"harness: bare set shows XCE_CONTROLLER_PEERS as the masters default (1.2)"
case_harness_builtin_default() {
local d; d="$(new_scratch harness-builtin-default)"
run_harness "$d" $'set\n' -u XCE_CONTROLLER_PEERS
assert_rc 0 || return 1
assert_grep "$d/stdout" '^masters +:<not set> default: localhost:9990$' "stdout" || return 1
d="$(new_scratch harness-empty-default)"
run_harness "$d" $'set\n' "XCE_CONTROLLER_PEERS="
assert_rc 0 || return 1
assert_grep "$d/stdout" '^masters +:<not set> default: localhost:9990$' "stdout (empty variable)" || return 1
}
add_case case_harness_builtin_default \
"harness: unset or empty variable keeps the built-in masters default (1.3, 1.7)"
case_harness_explicit_wins() {
local d; d="$(new_scratch harness-explicit-wins)"
run_harness "$d" $'set masters x=localhost:7\nset\n' "XCE_CONTROLLER_PEERS=$ENV3"
assert_rc 0 || return 1
assert_grep "$d/stdout" "^masters +:x=localhost:7 default: $ENV3\$" "stdout" || return 1
}
add_case case_harness_explicit_wins \
"harness: explicit set masters is the value shown over the env default (1.1)"
case_harness_invalid_source() {
local d
# (a) explicit invalid value, variable unset
d="$(new_scratch harness-invalid-explicit)"
run_harness "$d" $'set masters bad\nclient\n' -u XCE_CONTROLLER_PEERS
assert_rc nonzero || return 1
assert_grep "$d/stderr" "$HARNESS_INVALID_RE \`bad' \(explicit \`set masters'\)" "stderr (a: explicit)" || return 1
# (b) no set masters, invalid variable
d="$(new_scratch harness-invalid-env)"
run_harness "$d" $'client\n' "XCE_CONTROLLER_PEERS=bad"
assert_rc nonzero || return 1
assert_grep "$d/stderr" "$HARNESS_INVALID_RE \`bad' \(default from XCE_CONTROLLER_PEERS\)" "stderr (b: env)" || return 1
# (c) explicit invalid value with a valid variable set: explicit is blamed
d="$(new_scratch harness-invalid-explicit-env)"
run_harness "$d" $'set masters bad\nclient\n' "XCE_CONTROLLER_PEERS=$ENV3"
assert_rc nonzero || return 1
assert_grep "$d/stderr" "$HARNESS_INVALID_RE \`bad' \(explicit \`set masters'\)" "stderr (c: explicit over env)" || return 1
assert_no_grep "$d/stderr" 'XCE_CONTROLLER_PEERS' "stderr (c: explicit over env)" || return 1
}
add_case case_harness_invalid_source \
"harness: invalid masters message names explicit set masters or XCE_CONTROLLER_PEERS (1.1, 1.4)"
case_harness_help_names_variable() {
local d; d="$(new_scratch harness-help)"
run_harness "$d" $'help set\n' -u XCE_CONTROLLER_PEERS
assert_rc 0 || return 1
tr '\n' ' ' <"$d/stdout" | tr -s ' ' >"$d/help.txt"
assert_grep "$d/help.txt" 'XCE_CONTROLLER_PEERS' "help set output" || return 1
assert_grep "$d/help.txt" 'unset or empty' "help set output" || return 1
assert_grep "$d/help.txt" 'localhost:9990' "help set output" || return 1
}
add_case case_harness_help_names_variable \
"harness: help set documents XCE_CONTROLLER_PEERS and the opt-out (1.5, 1.7)"
# ════════════════════════════════════════════════════════════════════════════
# main