feat(build-time-versioning): worker --version and startup log
Worker uses plain getopt_long(), not argp, so it has no free --version handling like controller/scheduler. Adds a long-only OPT_VERSION=257 option and a handling case that computes xce_compose_version() into a static buffer and returns before any startup work (check_user()/ xcecd_init/xcecd_start). Both startup INFO log branches (--peers and no-peers) extended to log the identical buffer. Live-verified: --version and both startup-log branches are byte-identical (captured via a real daemon run under sudo). Also updates bug-1519 (pre-existing, unrelated ASAN "Joining already joined thread" abort during worker shutdown): independently reproduced under real root during this task's review, refining its prior "never reproduced under real root" note -- not a userns/unshare artifact, still unfixed, does not affect this task's code path. Task 3.3/13 of build-time-versioning. This closes out the "Core: C daemon version display" phase (tasks 3.1-3.3). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
88649a0462
commit
bc5c669b94
3 changed files with 41 additions and 15 deletions
|
|
@ -40,10 +40,10 @@
|
|||
- _Boundary: controller/main.c, scheduler/main.c, controller/testsuite/test.c_
|
||||
- _Depends: 3.1_
|
||||
|
||||
- [ ] 3.3 (P) Worker: add --version and startup log line
|
||||
- Add a long-only `--version` option to worker's `getopt_long()` option table and a handling case that prints the same macro output and exits before any other startup work
|
||||
- Add the version string to worker's existing first startup INFO log line (its own `"HA cluster config..."` line)
|
||||
- Observable: `xcecd --version` prints commit id + build time and exits without starting the daemon; the startup log's first lines include the identical two values
|
||||
- [x] 3.3 (P) Worker: add --version and startup log line
|
||||
- Added `OPT_VERSION=257` (long-only, beyond ASCII range, no collision with `"n:l:vh"`/`OPT_PEERS=256`); handling case computes `xce_compose_version()` into a `static char version_buf[128]` and returns before any startup work (`check_user()`/logdir/`xcecd_init`/`xcecd_start`)
|
||||
- Both startup INFO log branches (`--peers` and no-`--peers`) extended to log the same buffer
|
||||
- Observable confirmed live (independently re-verified by review): `xcecd --version` prints and exits; both startup log branches (captured via a real `sudo` daemon run) are byte-identical to `--version` output
|
||||
- _Requirements: 1.1, 1.2, 2.1, 2.2, 3.1, 3.2, 4.3_
|
||||
- _Boundary: worker/main.c_
|
||||
- _Depends: 3.1_
|
||||
|
|
|
|||
|
|
@ -15027,7 +15027,7 @@
|
|||
"error_message": "==ERROR: AddressSanitizer: Joining already joined thread, aborting.",
|
||||
"file": "src/worker/src/main.c (xcecd daemon startup)",
|
||||
"root_cause": "UNRESOLVED / not root-caused. xcecd's root check (main.c:288 geteuid()!=0) was bypassed for sandbox testing via `unshare -r` (user-namespace fake root, euid=0 but not real root). Startup then immediately hit an ASAN double-pthread_join abort during/after the HA-cluster-config init log line (main.c:435), before any further logging. Never reproduced under real root -- unknown whether this is a genuine double-join bug in xcecd's daemonize/thread-spawn path (e.g. duplicate cleanup on an early-init error branch) or an artifact of unshare's user-namespace semantics (e.g. a syscall xcecd's threading depends on behaving differently under CLONE_NEWUSER, such as thread creation ordering or signal delivery). Blocked further component-failover-adoption Task 3.3 Item-3 E2E validation from getting a live worker registered, which in turn meant no real job ever produced a scheduler decision (picker/allocator never had a matching candidate to dispatch to or explicitly PEND against), so the scheduler's decision-publish/rejoin code path (task 3.2) could not be exercised via a fully live job in this sandbox.",
|
||||
"fix": "Not fixed -- out of scope for Task 3.3 (which validates scheduler HA reconnect, not worker daemon startup). Worked around by validating scheduler join/failover/reconnect behavior via direct socket-state inspection (ss/proc fd) instead of a full live job pipeline; the decision-publish retry+rejoin logic itself already has white-box unit coverage from task 3.2 (test_scheduler_decision_publish_retry.c) and task 3.3 item 2 (test_scheduler_backpressure.c). If a future session needs a live worker in this sandbox, retry under real root (not unshare -r) first to isolate whether this is userns-specific.",
|
||||
"fix": "Not fixed -- out of scope for Task 3.3 (which validates scheduler HA reconnect, not worker daemon startup). Worked around by validating scheduler join/failover/reconnect behavior via direct socket-state inspection (ss/proc fd) instead of a full live job pipeline; the decision-publish retry+rejoin logic itself already has white-box unit coverage from task 3.2 (test_scheduler_decision_publish_retry.c) and task 3.3 item 2 (test_scheduler_backpressure.c). If a future session needs a live worker in this sandbox, retry under real root (not unshare -r) first to isolate whether this is userns-specific. UPDATE (2026-08-20, build-time-versioning task 3.3 review): independently reproduced under REAL sudo root (not unshare -r) -- same signature, same timing (fires during later thread-join startup work, strictly AFTER the HA-cluster-config INFO log line, both with and without --peers). This refines/answers the prior 'never reproduced under real root' open question: it is NOT a userns/unshare-specific artifact -- the abort reproduces identically under genuine root. Still not root-caused/fixed; still does not block this session's version-display changes (main.c's new --version/log-line code runs entirely before the point where this abort fires).",
|
||||
"tags": [
|
||||
"asan",
|
||||
"double-join",
|
||||
|
|
@ -15039,8 +15039,8 @@
|
|||
"component-failover-adoption"
|
||||
],
|
||||
"related_bugs": [],
|
||||
"occurrences": 1,
|
||||
"last_seen": "2026-08-11T12:49:17.815Z"
|
||||
"occurrences": 2,
|
||||
"last_seen": "2026-08-20T11:16:00Z"
|
||||
},
|
||||
{
|
||||
"id": "bug-1520",
|
||||
|
|
|
|||
|
|
@ -304,7 +304,8 @@ static int check_user(void)
|
|||
}
|
||||
|
||||
/* Long-option identifiers (beyond ASCII range to avoid collisions) */
|
||||
#define OPT_PEERS 256
|
||||
#define OPT_PEERS 256
|
||||
#define OPT_VERSION 257
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
|
@ -313,6 +314,15 @@ int main(int argc, char* argv[])
|
|||
int verbose = 0;
|
||||
__attribute__((unused)) char worker_log[1024] = { 0 };
|
||||
const char* peers_str = NULL;
|
||||
/* build-time-versioning task 3.3: worker uses plain getopt_long(), not
|
||||
* argp, so there is no free argp_program_version auto-handling here
|
||||
* (unlike controller/main.c, scheduler/src/main.c). version_buf is
|
||||
* computed once, before argument parsing even starts, via the same
|
||||
* xce_compose_version() runtime helper those two use -- both the
|
||||
* --version case below and the startup INFO log lines further down
|
||||
* read this SAME buffer, never recomputing it independently, so the
|
||||
* two can never disagree (Requirement 2.2). */
|
||||
static char version_buf[128];
|
||||
xce_cluster_config_t cluster_cfg = {0};
|
||||
int use_ha = 0; /* 1 when --peers given */
|
||||
|
||||
|
|
@ -356,11 +366,19 @@ int main(int argc, char* argv[])
|
|||
char* logdir = NULL;
|
||||
|
||||
static struct option long_opts[] = {
|
||||
{ "peers", required_argument, NULL, OPT_PEERS },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
{ "peers", required_argument, NULL, OPT_PEERS },
|
||||
{ "version", no_argument, NULL, OPT_VERSION },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
/* Computed before argument parsing begins (mirrors controller/main.c's
|
||||
* and scheduler/src/main.c's "compute before argp_parse()" ordering) so
|
||||
* the OPT_VERSION case immediately below can print it, and so it is
|
||||
* ready by the time the startup INFO log lines run later in main(). */
|
||||
xce_compose_version(version_buf, sizeof(version_buf), "xce v0.0.1-",
|
||||
STR(VERSION_COMMIT_ID), STR(VERSION_BUILD_TIME));
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "n:l:vh", long_opts, NULL))
|
||||
!= -1) {
|
||||
switch (opt) {
|
||||
|
|
@ -381,6 +399,12 @@ int main(int argc, char* argv[])
|
|||
case OPT_PEERS:
|
||||
peers_str = optarg;
|
||||
break;
|
||||
case OPT_VERSION:
|
||||
/* Prints and exits before any other startup work (peer
|
||||
* parsing, check_user(), daemon init, etc.) -- same contract
|
||||
* as controller/scheduler's --version. */
|
||||
printf("%s\n", version_buf);
|
||||
return 0;
|
||||
case 'h':
|
||||
default:
|
||||
printf("Usage: %s [-n name] [-l log_dir] [-v] "
|
||||
|
|
@ -433,14 +457,16 @@ int main(int argc, char* argv[])
|
|||
cluster_cfg.candidates_count, N_MAX_MASTERS);
|
||||
}
|
||||
XCECD_LOG(XCE_LOG_INFO,
|
||||
"HA cluster config: %d candidate(s), identity=%s",
|
||||
cluster_cfg.candidates_count, cluster_cfg.identity);
|
||||
"HA cluster config: %d candidate(s), identity=%s, "
|
||||
"version %s",
|
||||
cluster_cfg.candidates_count, cluster_cfg.identity,
|
||||
version_buf);
|
||||
use_ha = 1;
|
||||
} else {
|
||||
XCECD_LOG(XCE_LOG_INFO,
|
||||
"No --peers flag; using single-controller path "
|
||||
"(host=%s port=%d)",
|
||||
config.remote_host, config.remote_port);
|
||||
"(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
|
||||
* cluster_cfg onto config BEFORE xcecd_init()/xcecd_start() run, so it
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue