test(build-time-versioning): add version/startup-log consistency check
Adds src/tools/test-version-startup-consistency.sh (same house style as task 6.1's test-build-version-regression.sh), which builds xce_scheduler and xce_apiserver for real, starts each daemon, and asserts the commit id and build time reported by --version/-version are identical to the values in the daemon's own startup log -- proving Requirement 2.2's guarantee that the two can never disagree for the same artifact. apiserver's build routes around two pre-existing, unrelated sandbox blockers (bug-1597's missing CGO env in `make build-server`, and go_build's hardcoded GOPROXY hanging in this sandbox) via a direct `go build -mod=readonly -ldflags` invocation that computes commit_id/build_time identically to go_build and injects them the same way; only the orchestration layer differs, not what main.go (task 4.4, unmodified) consumes. Verified independently by review, including live reproduction of both build-path blockers. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
176f0cf835
commit
79941a9f42
2 changed files with 504 additions and 3 deletions
|
|
@ -114,9 +114,10 @@
|
|||
- _Requirements: 4.1, 4.2, 4.3, 5.1, 5.2_
|
||||
- _Depends: 2.1, 3.2, 3.3_
|
||||
|
||||
- [ ] 6.2 Version/startup-log consistency test
|
||||
- For one C daemon (controller or scheduler) and one Go binary (apiserver), capture the startup log's version line and separately run `--version`/`-version`, asserting the two report byte-identical commit id and build time
|
||||
- Observable: a passing test proves `--version` and the startup log can never disagree for the same artifact
|
||||
- [x] 6.2 Version/startup-log consistency test
|
||||
- New standalone script `src/tools/test-version-startup-consistency.sh` (same house style as task 6.1's `test-build-version-regression.sh`) builds `xce_scheduler` and `xce_apiserver` for real, starts each daemon, greps its startup log, and asserts the commit-id/build-time VALUES (not whole-line text, since the C daemon's plain-text log line and apiserver's structured zap-JSON fields are differently shaped by design) are identical between `--version`/`-version` output and the startup log
|
||||
- apiserver's build routes around two pre-existing, unrelated, independently-reproduced sandbox blockers -- `make build-server`'s missing CGO env (bug-1597) and `xce-build.sh apiserver`'s `go_build`-hardcoded `GOPROXY=goproxy.cn` hanging in this sandbox (confirmed live by review via orphaned processes) -- via a direct `go build -mod=readonly -ldflags` invocation computing `commit_id`/`build_time` with the identical logic and injecting them via the identical `-X` shape `go_build` itself uses; only the orchestration layer differs, not what's injected or how task 4.4's unmodified `main.go` consumes it
|
||||
- Observable confirmed live (independently re-run in full by both implementer and review, real builds + real daemon startups each time): scheduler and apiserver both PASS with matching, real commit id + build time; a demonstration block proves the check correctly rejects a commit-id mismatch, a build-time mismatch, and an empty field on either side
|
||||
- _Requirements: 2.2_
|
||||
- _Depends: 3.2, 4.4_
|
||||
|
||||
|
|
|
|||
500
src/tools/test-version-startup-consistency.sh
Executable file
500
src/tools/test-version-startup-consistency.sh
Executable file
|
|
@ -0,0 +1,500 @@
|
|||
#!/usr/bin/env bash
|
||||
# test-version-startup-consistency.sh — build-time-versioning task 6.2
|
||||
# (.kiro/specs/build-time-versioning/tasks.md, Requirement 2.2: "The system
|
||||
# shall present the same commit identifier and build timestamp values
|
||||
# through the --version flag and through the startup log line ... for the
|
||||
# same build artifact, so the two never disagree.")
|
||||
#
|
||||
# Unlike task 6.1 (test-build-version-regression.sh), which checks a
|
||||
# binary's version info against a live `git rev-parse` (build-vs-source-tree
|
||||
# consistency), THIS script checks a binary's version info against ITSELF:
|
||||
# the exact same running artifact's --version/-version output must report
|
||||
# the identical commit id and build time as the version fields it logs at
|
||||
# INFO level when it starts up. A daemon that composed its --version string
|
||||
# once but its startup log from a stale/different computation (or vice
|
||||
# versa) would slip past 6.1 entirely (6.1 only ever inspects --version) --
|
||||
# this is the check that specifically guards the "two never disagree"
|
||||
# half of Requirement 2.2.
|
||||
#
|
||||
# Components checked (one C daemon, one Go binary, per the task's own
|
||||
# scope -- controller/scheduler both already share the identical
|
||||
# xce_compose_version() mechanism per task 3.2, so either would equally
|
||||
# prove the C side; scheduler is chosen to match task 6.1's precedent and
|
||||
# because it reaches its startup INFO line without needing an actual
|
||||
# reachable controller):
|
||||
# - xce_scheduler (C daemon, task 3.2)
|
||||
# - xce_apiserver (Go binary, task 4.4)
|
||||
#
|
||||
# What "byte-identical" means here, precisely (the task's own text asks
|
||||
# the implementation to state its interpretation): the two captures are
|
||||
# NOT compared as whole strings/lines -- --version's output and the
|
||||
# startup log's line are, by design (design.md's Components and
|
||||
# Interfaces section), differently-shaped text: a composed
|
||||
# "<prefix><commit> built <build_time>" string for the C daemon's
|
||||
# --version vs. that same string embedded mid-sentence in a larger INFO
|
||||
# log line; a plain "xce-apiserver <commit> built <build_time>" line for
|
||||
# apiserver's -version vs. structured zap JSON fields
|
||||
# ("gitCommit"/"buildTime") on its startup log line. What Requirement 2.2
|
||||
# actually constrains is the two DATA VALUES (commit id, build time) --
|
||||
# so this script extracts just those two field values from each capture
|
||||
# and asserts the extracted VALUES are byte-identical between --version
|
||||
# and the startup log, for each binary. Surrounding text differing is
|
||||
# expected and not a failure.
|
||||
#
|
||||
# Build path notes:
|
||||
# - xce_scheduler is built via the exact same real `/xce-build`-equivalent
|
||||
# invocation task 6.1 established: `nix develop <src> --command bash
|
||||
# src/tools/xce-build.sh scheduler <profile> <build dir>`.
|
||||
# - xce_apiserver's two documented real build paths (src/frontend/
|
||||
# Makefile's build-server target, and apiserver/meson.build's go_build-
|
||||
# driven custom_target invoked via `xce-build.sh apiserver`) are BOTH
|
||||
# pre-existing-broken/unusable in this environment, independently of
|
||||
# this task: build-server lacks CGO_CFLAGS/CGO_LDFLAGS entirely
|
||||
# (bug-1597, confirmed still present, unmodified here per this
|
||||
# task's constraints) and go_build (src/tools/go_build) hardcodes
|
||||
# GOPROXY=https://goproxy.cn plus a fresh-every-invocation
|
||||
# GOCACHE/GOPATH under meson's own $TMPDIR sandbox -- confirmed via
|
||||
# live reproduction (established but stalled TCP connection to
|
||||
# goproxy.cn:443, zero CPU progress for 8-10 minutes across three
|
||||
# separate attempts) that this specific combination hangs in this
|
||||
# sandbox even though goproxy.cn itself is directly reachable outside
|
||||
# that fresh-cache/custom-GOPROXY combination. Per bug-1597's own
|
||||
# documented workaround (its "fix" field), this script instead invokes
|
||||
# `go build` directly with the same -ldflags shape go_build/Makefile
|
||||
# use and the same CGO_CFLAGS/CGO_LDFLAGS pkg-config derivation
|
||||
# test-backend/apiserver's meson.build already use, against the
|
||||
# default (already-populated) module cache -- functionally the same
|
||||
# real build (same source, same compiler, same ldflags injection
|
||||
# mechanism), just not routed through the specific wrapper script that
|
||||
# hangs in this sandbox. Neither go_build nor the Makefile is modified.
|
||||
# `go build` is invoked with `-mod=readonly` explicitly (confirmed live:
|
||||
# without it, this build silently ADDED ~65 unrelated indirect-
|
||||
# dependency /go.mod checksum lines to the committed
|
||||
# src/frontend/apiserver/go.sum as a side effect of resolving the full
|
||||
# module graph -- go.sum can still be extended under readonly mode's
|
||||
# default in some Go versions, but never go.mod itself; -mod=readonly
|
||||
# makes any genuinely-missing checksum a loud build failure instead of
|
||||
# a silent repo mutation, which is what this task's "only ADDS a new
|
||||
# check" constraint requires. The build still succeeds under
|
||||
# -mod=readonly against this repo's current go.sum, so nothing was
|
||||
# actually missing -- the extra entries were superfluous).
|
||||
#
|
||||
# Usage:
|
||||
# bash src/tools/test-version-startup-consistency.sh [--keep-artifacts]
|
||||
#
|
||||
# Exit code is the sole pass/fail signal (0 = pass, non-zero = a real
|
||||
# --version/startup-log disagreement or a demonstration inconsistency).
|
||||
# Two real builds (scheduler + apiserver's C lib dependency) plus two real
|
||||
# daemon startups happen -- budget a few minutes of wall-clock time.
|
||||
#
|
||||
# Why this file lives here: same convention as test-build-version-
|
||||
# regression.sh / test-commit-durability.sh / test-heartbeat-tolerance.sh
|
||||
# -- a standalone, CLI-runnable, real-build-and-real-process check for a
|
||||
# cross-cutting concern, deliberately NOT a meson test() (real builds +
|
||||
# real daemon startups are too expensive for every `meson test` run).
|
||||
#
|
||||
# Revalidation triggers:
|
||||
# - SCHED_VERSION_PREFIX below must match the literal prefix passed to
|
||||
# xce_compose_version() in scheduler/src/main.c (task 3.2).
|
||||
# - APISERVER_VERSION_PREFIX below must match the literal
|
||||
# "xce-apiserver " format string in
|
||||
# src/frontend/apiserver/cmd/main.go's -version branch (task 4.4).
|
||||
# - APISERVER_LOG_ANCHOR below must match the literal
|
||||
# "********** Initialize Logger done!" log message in the same file.
|
||||
set -euo pipefail
|
||||
|
||||
# ── paths ───────────────────────────────────────────────────────────────────
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
WORKTREE="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SRC_DIR="$WORKTREE/src"
|
||||
|
||||
SCHED_VERSION_PREFIX="xce v0.0.0.1-"
|
||||
APISERVER_VERSION_PREFIX="xce-apiserver "
|
||||
APISERVER_LOG_ANCHOR="Initialize Logger done!"
|
||||
|
||||
BUILD_DIR_SCHED="versionstartup.sched"
|
||||
BUILD_DIR_APILIB="versionstartup.apilib"
|
||||
|
||||
KEEP_ARTIFACTS=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--keep-artifacts) KEEP_ARTIFACTS=1 ;;
|
||||
*)
|
||||
echo "Unknown argument: $arg" >&2
|
||||
echo "Usage: $0 [--keep-artifacts]" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── colour / pass-fail helpers (matches test-build-version-regression.sh /
|
||||
# test-heartbeat-tolerance.sh) ───────────────────────────────────────────────
|
||||
green() { printf '\033[32m%s\033[0m\n' "$*"; }
|
||||
red() { printf '\033[31m%s\033[0m\n' "$*"; }
|
||||
yellow() { printf '\033[33m%s\033[0m\n' "$*"; }
|
||||
|
||||
trap 'red "FATAL: command failed (line $LINENO). See output above."' ERR
|
||||
|
||||
SCHED_PID=""
|
||||
API_PID=""
|
||||
WORKDIR=""
|
||||
|
||||
cleanup() {
|
||||
if [ -n "$SCHED_PID" ]; then
|
||||
kill -9 "$SCHED_PID" 2>/dev/null || true
|
||||
wait "$SCHED_PID" 2>/dev/null || true
|
||||
fi
|
||||
if [ -n "$API_PID" ]; then
|
||||
kill -9 "$API_PID" 2>/dev/null || true
|
||||
wait "$API_PID" 2>/dev/null || true
|
||||
fi
|
||||
if [ "$KEEP_ARTIFACTS" = 1 ]; then
|
||||
yellow "--keep-artifacts given: leaving $SRC_DIR/$BUILD_DIR_SCHED, $SRC_DIR/$BUILD_DIR_APILIB, and $WORKDIR in place."
|
||||
return 0
|
||||
fi
|
||||
for d in "$SRC_DIR/$BUILD_DIR_SCHED" "$SRC_DIR/$BUILD_DIR_APILIB"; do
|
||||
[ -d "$d" ] && rm -rf "$d"
|
||||
done
|
||||
[ -n "$WORKDIR" ] && [ -d "$WORKDIR" ] && rm -rf "$WORKDIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
WORKDIR="$(mktemp -d "${TMPDIR:-/tmp}/xce-version-startup-check.XXXXXX")"
|
||||
|
||||
# ── field extraction ─────────────────────────────────────────────────────
|
||||
# extract_composed_fields TEXT PREFIX
|
||||
# TEXT is either a raw --version string, or a larger line of text (e.g. a
|
||||
# full log line) that CONTAINS "<PREFIX><commit> built <build_time>"
|
||||
# somewhere in it. Prints "<commit>\t<build_time>" on stdout (may contain
|
||||
# empty fields); returns 1 if PREFIX is not found at all -- a stronger
|
||||
# failure than a merely-empty field.
|
||||
extract_composed_fields() {
|
||||
local text="$1" prefix="$2" rest commit build_time
|
||||
rest="${text##*"$prefix"}"
|
||||
if [ "$rest" = "$text" ]; then
|
||||
return 1
|
||||
fi
|
||||
rest="$(printf '%s' "$rest" | tr -d '\r\n')"
|
||||
commit="${rest%% built *}"
|
||||
build_time="${rest#* built }"
|
||||
printf '%s\t%s\n' "$commit" "$build_time"
|
||||
}
|
||||
|
||||
# extract_zap_fields LOGLINE
|
||||
# apiserver's startup log line carries the two values as structured zap
|
||||
# JSON fields ("gitCommit": "...", "buildTime": "...") rather than a
|
||||
# composed string. Prints "<commit>\t<build_time>"; returns 1 if either
|
||||
# field is absent. Deliberately POSIX BRE `sed` (not `grep -P`/PCRE) --
|
||||
# this script's own real (non-interactive) invocation environment resolves
|
||||
# plain `grep` to a BusyBox build with no -P support (confirmed live: an
|
||||
# interactive shell alias shadows `grep` with a PCRE-capable binary, but
|
||||
# that alias is interactive-shell-only and is NOT inherited by `bash
|
||||
# thisscript.sh` run as its own process) -- BusyBox `sed` supports POSIX
|
||||
# BRE including [[:space:]] and -n/p, which is sufficient here and portable
|
||||
# to any POSIX sed.
|
||||
extract_zap_fields() {
|
||||
local logline="$1" commit build_time
|
||||
commit="$(printf '%s' "$logline" | sed -n 's/.*"gitCommit":[[:space:]]*"\([^"]*\)".*/\1/p')"
|
||||
build_time="$(printf '%s' "$logline" | sed -n 's/.*"buildTime":[[:space:]]*"\([^"]*\)".*/\1/p')"
|
||||
if [ -z "$commit" ] || [ -z "$build_time" ]; then
|
||||
return 1
|
||||
fi
|
||||
printf '%s\t%s\n' "$commit" "$build_time"
|
||||
}
|
||||
|
||||
# check_consistency LABEL VERSION_COMMIT VERSION_BUILD_TIME LOG_COMMIT LOG_BUILD_TIME
|
||||
# The exact comparison Requirement 2.2 requires: both the commit id AND the
|
||||
# build time must be identical (non-empty on both sides, and equal) between
|
||||
# the --version/-version capture and the startup-log capture. This is the
|
||||
# single function both the real per-binary checks below AND
|
||||
# demonstrate_detection_capability() (the discriminating-power proof)
|
||||
# exercise, so the demonstration genuinely proves this exact logic's
|
||||
# behaviour.
|
||||
check_consistency() {
|
||||
local label="$1" v_commit="$2" v_build="$3" l_commit="$4" l_build="$5"
|
||||
local ok=1
|
||||
|
||||
if [ -z "$v_commit" ] || [ -z "$l_commit" ]; then
|
||||
red "FAIL [$label]: commit id missing from one or both captures (--version='$v_commit' startup-log='$l_commit')"
|
||||
ok=0
|
||||
elif [ "$v_commit" != "$l_commit" ]; then
|
||||
red "FAIL [$label]: commit id MISMATCH -- --version reports '$v_commit', startup log reports '$l_commit'. These must never disagree (Requirement 2.2)."
|
||||
ok=0
|
||||
fi
|
||||
|
||||
if [ -z "$v_build" ] || [ -z "$l_build" ]; then
|
||||
red "FAIL [$label]: build time missing from one or both captures (--version='$v_build' startup-log='$l_build')"
|
||||
ok=0
|
||||
elif [ "$v_build" != "$l_build" ]; then
|
||||
red "FAIL [$label]: build time MISMATCH -- --version reports '$v_build', startup log reports '$l_build'. These must never disagree (Requirement 2.2)."
|
||||
ok=0
|
||||
fi
|
||||
|
||||
if [ "$ok" = 1 ]; then
|
||||
green "PASS [$label]: --version and startup log agree -- commit='$v_commit' build_time='$v_build'"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Proves check_consistency's comparison logic itself would catch a genuine
|
||||
# disagreement, since (unlike task 6.1) there is no prior broken
|
||||
# implementation to revert against for a real RED phase: 3.2/4.4 already
|
||||
# guarantee --version and the startup log are read from the identical
|
||||
# in-memory value, so a live mismatch cannot currently be produced. Instead,
|
||||
# feed the SAME function deliberately-mismatched commit id / build time
|
||||
# pairs and confirm both are rejected, plus a sanity check that genuinely
|
||||
# matching real captures are still accepted.
|
||||
demonstrate_detection_capability() {
|
||||
local real_commit="$1" real_build="$2"
|
||||
|
||||
yellow "=== DEMONSTRATION: proving check_consistency() catches a real --version/startup-log disagreement ==="
|
||||
|
||||
yellow "--- (a) commit id mismatch (build time matches) ---"
|
||||
if check_consistency "DEMO-commit-mismatch" "$real_commit" "$real_build" "deadbeef0000" "$real_build"; then
|
||||
red "DEMONSTRATION FAILED: incorrectly PASSED a commit id mismatch."
|
||||
exit 1
|
||||
fi
|
||||
green "Confirmed: a commit id mismatch is correctly rejected."
|
||||
|
||||
yellow "--- (b) build time mismatch (commit id matches) ---"
|
||||
if check_consistency "DEMO-buildtime-mismatch" "$real_commit" "$real_build" "$real_commit" "1999-01-01T00:00:00Z"; then
|
||||
red "DEMONSTRATION FAILED: incorrectly PASSED a build time mismatch."
|
||||
exit 1
|
||||
fi
|
||||
green "Confirmed: a build time mismatch is correctly rejected."
|
||||
|
||||
yellow "--- (c) one side empty (the degenerate case a naive equality check could miss if BOTH sides were empty) ---"
|
||||
if check_consistency "DEMO-empty-side" "$real_commit" "$real_build" "" "$real_build"; then
|
||||
red "DEMONSTRATION FAILED: incorrectly PASSED an empty commit id on one side."
|
||||
exit 1
|
||||
fi
|
||||
green "Confirmed: an empty field on one side is correctly rejected."
|
||||
|
||||
yellow "--- (d) sanity: genuinely matching real values must PASS ---"
|
||||
if ! check_consistency "DEMO-sanity-real-match" "$real_commit" "$real_build" "$real_commit" "$real_build"; then
|
||||
red "DEMONSTRATION FAILED: incorrectly FAILED genuinely matching real values."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
green "=== DEMONSTRATION complete: commit/build-time mismatches and missing fields are rejected; a genuine match is accepted ==="
|
||||
}
|
||||
|
||||
overall_pass=1
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
# xce_scheduler (C daemon)
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
yellow "=== [scheduler] nix develop $SRC_DIR --command bash $SRC_DIR/tools/xce-build.sh scheduler debug $BUILD_DIR_SCHED ==="
|
||||
nix develop "$SRC_DIR" --command bash "$SRC_DIR/tools/xce-build.sh" scheduler debug "$BUILD_DIR_SCHED"
|
||||
|
||||
SCHED_BIN="$SRC_DIR/$BUILD_DIR_SCHED/scheduler/xce_scheduler"
|
||||
if [ ! -x "$SCHED_BIN" ]; then
|
||||
red "FATAL: expected binary not found/executable at $SCHED_BIN after a successful-looking build."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sched_version_raw="$("$SCHED_BIN" --version 2>&1)"
|
||||
yellow "[scheduler] --version raw output: [$sched_version_raw]"
|
||||
|
||||
sched_log="$WORKDIR/scheduler.log"
|
||||
rm -f "$sched_log"
|
||||
"$SCHED_BIN" --logfile "$sched_log" >"$WORKDIR/scheduler.stdout" 2>&1 &
|
||||
SCHED_PID=$!
|
||||
|
||||
sched_log_line=""
|
||||
waited=0
|
||||
while [ "$waited" -lt 20 ]; do
|
||||
if [ -f "$sched_log" ] && grep -qF -- "$SCHED_VERSION_PREFIX" "$sched_log" 2>/dev/null; then
|
||||
sched_log_line="$(grep -F -- "$SCHED_VERSION_PREFIX" "$sched_log" | tail -1)"
|
||||
break
|
||||
fi
|
||||
sleep 0.5
|
||||
waited=$((waited + 1))
|
||||
done
|
||||
kill -9 "$SCHED_PID" 2>/dev/null || true
|
||||
wait "$SCHED_PID" 2>/dev/null || true
|
||||
SCHED_PID=""
|
||||
|
||||
if [ -z "$sched_log_line" ]; then
|
||||
red "FAIL [scheduler]: startup log never produced a line containing '$SCHED_VERSION_PREFIX' within 10s. Log contents:"
|
||||
cat "$sched_log" 2>/dev/null >&2 || true
|
||||
overall_pass=0
|
||||
sched_v_commit=""; sched_v_build=""
|
||||
else
|
||||
yellow "[scheduler] startup log line: [$sched_log_line]"
|
||||
|
||||
if ! sched_v_fields="$(extract_composed_fields "$sched_version_raw" "$SCHED_VERSION_PREFIX")"; then
|
||||
red "FAIL [scheduler]: --version output did not contain the expected prefix '$SCHED_VERSION_PREFIX' -- raw: [$sched_version_raw]"
|
||||
overall_pass=0
|
||||
sched_v_commit=""; sched_v_build=""
|
||||
else
|
||||
sched_v_commit="${sched_v_fields%%$'\t'*}"
|
||||
sched_v_build="${sched_v_fields#*$'\t'}"
|
||||
fi
|
||||
|
||||
if ! sched_l_fields="$(extract_composed_fields "$sched_log_line" "$SCHED_VERSION_PREFIX")"; then
|
||||
red "FAIL [scheduler]: startup log line did not contain the expected prefix '$SCHED_VERSION_PREFIX' -- line: [$sched_log_line]"
|
||||
overall_pass=0
|
||||
sched_l_commit=""; sched_l_build=""
|
||||
else
|
||||
sched_l_commit="${sched_l_fields%%$'\t'*}"
|
||||
sched_l_build="${sched_l_fields#*$'\t'}"
|
||||
fi
|
||||
|
||||
if [ -n "${sched_v_commit:-}" ] || [ -n "${sched_l_commit:-}" ]; then
|
||||
if ! check_consistency "scheduler" "$sched_v_commit" "$sched_v_build" "$sched_l_commit" "$sched_l_build"; then
|
||||
overall_pass=0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
# xce_apiserver (Go binary)
|
||||
# ══════════════════════════════════════════════════════════════════════════
|
||||
yellow "=== [apiserver] building lib dependency: nix develop $SRC_DIR --command bash $SRC_DIR/tools/xce-build.sh lib debug $BUILD_DIR_APILIB ==="
|
||||
nix develop "$SRC_DIR" --command bash "$SRC_DIR/tools/xce-build.sh" lib debug "$BUILD_DIR_APILIB"
|
||||
|
||||
APILIB_DIR="$SRC_DIR/$BUILD_DIR_APILIB"
|
||||
if [ ! -f "$APILIB_DIR/lib/libxce.a" ]; then
|
||||
red "FATAL: expected $APILIB_DIR/lib/libxce.a not found after 'lib' build."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
API_COMMIT_ID="$(git -C "$SRC_DIR" rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
||||
if [ -n "$(git -C "$SRC_DIR" status --porcelain 2>/dev/null)" ]; then
|
||||
API_COMMIT_ID="${API_COMMIT_ID}-dirty"
|
||||
fi
|
||||
API_BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
|
||||
yellow "=== [apiserver] go build (direct invocation, see header comment re: bug-1597 / go_build's goproxy.cn hang in this sandbox) -- commit_id=$API_COMMIT_ID build_time=$API_BUILD_TIME ==="
|
||||
APISERVER_BIN="$APILIB_DIR/frontend/apiserver/xce_apiserver"
|
||||
mkdir -p "$(dirname "$APISERVER_BIN")"
|
||||
nix develop "$SRC_DIR" --command bash -c "
|
||||
set -e
|
||||
cd '$SRC_DIR/frontend/apiserver'
|
||||
CGO_CFLAGS=\"-g \$(pkg-config --cflags librtti c-capnproto openssl tinylog) -I$SRC_DIR/include -I$APILIB_DIR/lib\"
|
||||
CGO_LDFLAGS=\"-g \$(pkg-config --libs librtti c-capnproto openssl tinylog) -lm -lrt -L$APILIB_DIR/lib -lxce\"
|
||||
GOPROXY=off CGO_ENABLED=1 CGO_CFLAGS=\"\$CGO_CFLAGS\" CGO_LDFLAGS=\"\$CGO_LDFLAGS\" \
|
||||
go build -mod=readonly -gcflags 'all=-N -l' -ldflags \"-X main.GitCommit=$API_COMMIT_ID -X main.BuildTime=$API_BUILD_TIME\" \
|
||||
-o $APISERVER_BIN ./cmd/main.go
|
||||
"
|
||||
|
||||
if [ ! -x "$APISERVER_BIN" ]; then
|
||||
red "FATAL: expected binary not found/executable at $APISERVER_BIN after a successful-looking build."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
api_version_raw="$("$APISERVER_BIN" -version 2>&1)"
|
||||
yellow "[apiserver] -version raw output: [$api_version_raw]"
|
||||
|
||||
# Minimal topdir so apiserver reaches its startup INFO log line without a
|
||||
# real controller cluster: config/{auth.yaml,rbac.yaml} copied verbatim
|
||||
# (same reasoning as deploy-ha.sh -- functional auth/RBAC is not required,
|
||||
# only enough for InitAuthProvider()/LoadRBACConfig() to load without
|
||||
# erroring), app.yaml with its trailing `backend:` section stripped (same
|
||||
# deploy-ha.sh technique) so main.go boots in permanent stub mode -- no
|
||||
# RegisterToBackend attempt, no real controller needed -- and a
|
||||
# freshly-generated logger.yaml pointing outputPath at this run's own
|
||||
# WORKDIR. This is the identical lightweight setup deploy-ha.sh already
|
||||
# uses for its own apiserver instance (src/tools/deploy-ha.sh:337-421),
|
||||
# minus the backend re-population deploy-ha.sh does for ITS OWN (different)
|
||||
# purpose of live resource-value propagation testing.
|
||||
APISERVER_SRC_CONFIG="$SRC_DIR/frontend/apiserver/config"
|
||||
API_TOPDIR="$WORKDIR/apiserver_topdir"
|
||||
mkdir -p "$API_TOPDIR/config"
|
||||
cp "$APISERVER_SRC_CONFIG/auth.yaml" "$API_TOPDIR/config/auth.yaml"
|
||||
cp "$APISERVER_SRC_CONFIG/rbac.yaml" "$API_TOPDIR/config/rbac.yaml"
|
||||
sed '/^backend:/,$d' "$APISERVER_SRC_CONFIG/app.yaml" > "$API_TOPDIR/config/app.yaml"
|
||||
cat > "$API_TOPDIR/config/logger.yaml" << EOF
|
||||
logging:
|
||||
level: "debug"
|
||||
encoding: console
|
||||
outputPath: "$WORKDIR/apiserver.log"
|
||||
file:
|
||||
maxSize: 100
|
||||
maxBackups: 10
|
||||
maxAge: 14
|
||||
compress: true
|
||||
encoderConfig:
|
||||
messageKey: "message"
|
||||
levelKey: "level"
|
||||
timeKey: "timestamp"
|
||||
nameKey: "logger"
|
||||
callerKey: "caller"
|
||||
functionKey: "func"
|
||||
stacktraceKey: "stacktrace"
|
||||
timeEncoder: "ISO8601"
|
||||
levelEncoder: "capital"
|
||||
EOF
|
||||
|
||||
api_log="$WORKDIR/apiserver.log"
|
||||
rm -f "$api_log"
|
||||
"$APISERVER_BIN" -d "$API_TOPDIR" >"$WORKDIR/apiserver.stdout" 2>&1 &
|
||||
API_PID=$!
|
||||
|
||||
api_log_line=""
|
||||
waited=0
|
||||
while [ "$waited" -lt 20 ]; do
|
||||
if [ -f "$api_log" ] && grep -qF -- "$APISERVER_LOG_ANCHOR" "$api_log" 2>/dev/null; then
|
||||
api_log_line="$(grep -F -- "$APISERVER_LOG_ANCHOR" "$api_log" | head -1)"
|
||||
break
|
||||
fi
|
||||
sleep 0.5
|
||||
waited=$((waited + 1))
|
||||
done
|
||||
kill -9 "$API_PID" 2>/dev/null || true
|
||||
wait "$API_PID" 2>/dev/null || true
|
||||
API_PID=""
|
||||
|
||||
if [ -z "$api_log_line" ]; then
|
||||
red "FAIL [apiserver]: startup log never produced a line containing '$APISERVER_LOG_ANCHOR' within 10s. Log contents:"
|
||||
cat "$api_log" 2>/dev/null >&2 || true
|
||||
cat "$WORKDIR/apiserver.stdout" 2>/dev/null >&2 || true
|
||||
overall_pass=0
|
||||
api_v_commit=""; api_v_build=""
|
||||
else
|
||||
yellow "[apiserver] startup log line: [$api_log_line]"
|
||||
|
||||
if ! api_v_fields="$(extract_composed_fields "$api_version_raw" "$APISERVER_VERSION_PREFIX")"; then
|
||||
red "FAIL [apiserver]: -version output did not contain the expected prefix '$APISERVER_VERSION_PREFIX' -- raw: [$api_version_raw]"
|
||||
overall_pass=0
|
||||
api_v_commit=""; api_v_build=""
|
||||
else
|
||||
api_v_commit="${api_v_fields%%$'\t'*}"
|
||||
api_v_build="${api_v_fields#*$'\t'}"
|
||||
fi
|
||||
|
||||
if ! api_l_fields="$(extract_zap_fields "$api_log_line")"; then
|
||||
red "FAIL [apiserver]: startup log line did not contain both gitCommit/buildTime zap fields -- line: [$api_log_line]"
|
||||
overall_pass=0
|
||||
api_l_commit=""; api_l_build=""
|
||||
else
|
||||
api_l_commit="${api_l_fields%%$'\t'*}"
|
||||
api_l_build="${api_l_fields#*$'\t'}"
|
||||
fi
|
||||
|
||||
if [ -n "${api_v_commit:-}" ] || [ -n "${api_l_commit:-}" ]; then
|
||||
if ! check_consistency "apiserver" "$api_v_commit" "$api_v_build" "$api_l_commit" "$api_l_build"; then
|
||||
overall_pass=0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── demonstration (discriminating-power proof) ──────────────────────────────
|
||||
if [ -n "${sched_v_commit:-}" ] && [ -n "${sched_v_build:-}" ]; then
|
||||
demonstrate_detection_capability "$sched_v_commit" "$sched_v_build"
|
||||
elif [ -n "${api_v_commit:-}" ] && [ -n "${api_v_build:-}" ]; then
|
||||
demonstrate_detection_capability "$api_v_commit" "$api_v_build"
|
||||
else
|
||||
red "FATAL: neither binary produced usable real values to demonstrate the comparison logic against."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$overall_pass" = 1 ]; then
|
||||
green "=== ALL CHECKS PASS: xce_scheduler and xce_apiserver each report byte-identical commit id + build time between --version/-version and their startup log ==="
|
||||
exit 0
|
||||
else
|
||||
red "=== INCONSISTENCY DETECTED: see FAIL line(s) above ==="
|
||||
exit 1
|
||||
fi
|
||||
Loading…
Add table
Add a link
Reference in a new issue