diff --git a/src/scheduler/src/main.c b/src/scheduler/src/main.c index 837e984c..5b9765b0 100644 --- a/src/scheduler/src/main.c +++ b/src/scheduler/src/main.c @@ -327,6 +327,10 @@ int main(int argc, char* argv[]) sched_init(&sched, &config, cluster_cfg_ptr, scheduler_req_handler); + INFO("active job picker plugin: %s", sched.core.job_pick_plugin + ? sched.core.job_pick_plugin + : "(null)"); + /* Req 5 AC 10 (scheduler-core spec, task 9.1): stash the resolved * --record-dir value on the live xce_scheduler_t -- see scheduler.h's * field comment. Not consumed anywhere yet (task 9.2's job); this diff --git a/src/worker/src/resource_sync.c b/src/worker/src/resource_sync.c index d852463c..f535fd74 100644 --- a/src/worker/src/resource_sync.c +++ b/src/worker/src/resource_sync.c @@ -203,9 +203,29 @@ static xce_resval_t* collect_load_indices(int* count) return NULL; } - char hostname[256]; - if (gethostname(hostname, sizeof(hostname)) != 0) { - strcpy(hostname, "unknown"); + /* bug-1569: this worker's JOIN identity (the -n/--name value, e.g. + * "pc13v" -- xcecd_ctx.virtual_name, set from config->virutal_name in + * xcecd_init(), xcecd.c) is what the controller/scheduler key every + * OTHER per-worker resource value by (mark_host_res()/update_val_res(), + * worker.c; sched_get_worker(), core_events.c). Using gethostname() + * (the raw OS/container hostname, which is NOT the same value and can + * be identical across every worker in a multi-worker-per-host test + * setup, or simply a generic sandbox/container name unrelated to any + * configured worker identity) here instead made every periodic load- + * index report's .host wrong -- the controller forwards it to the + * scheduler unmodified (do_update_resvals(), opupd.c), which then + * fails sched_get_worker(core, rv->host) for the REAL worker and + * silently drops the value ("RESVALS_DELTA: unknown worker , + * skipping", core_events.c) -- live-confirmed: gethostname() here + * returned "xce" in this sandbox, not "pc13v". */ + const char* hostname = xcecd_ctx.virtual_name; + char hostname_buf[256]; + + if (hostname == NULL || hostname[0] == '\0') { + if (gethostname(hostname_buf, sizeof(hostname_buf)) != 0) { + strcpy(hostname_buf, "unknown"); + } + hostname = hostname_buf; } for (int i = 0; i < rescount; i++) { diff --git a/src/worker/testsuite/meson.build b/src/worker/testsuite/meson.build index ad63b024..e68ab141 100644 --- a/src/worker/testsuite/meson.build +++ b/src/worker/testsuite/meson.build @@ -26,6 +26,21 @@ test('worker controller_connector test', controller_connector_test, timeout: 60, env: ['ASAN_OPTIONS=detect_leaks=0']) +# bug-1569: collect_load_indices() (resource_sync.c) must tag every periodic +# resource report with xcecd_ctx.virtual_name (the worker's own join +# identity), not gethostname()'s raw OS/container hostname -- see +# test_resource_sync.c's own header comment for the full mechanism trace. +resource_sync_test = executable('resource_sync_test', + 'test_resource_sync.c', + include_directories: [worker_test_incdir], + dependencies: worker_test_deps, + install: false) + +test('worker resource_sync test', resource_sync_test, + suite: 'worker', + timeout: 60, + env: ['ASAN_OPTIONS=detect_leaks=0']) + # component-failover-adoption task 2.2: worker startup-ordering white-box # test. #include's xcecd.c directly (same technique as controller_connector_ # test above, but a separate binary/file -- xcecd.c and controller_connector.c diff --git a/src/worker/testsuite/test_resource_sync.c b/src/worker/testsuite/test_resource_sync.c new file mode 100644 index 00000000..23d15f4e --- /dev/null +++ b/src/worker/testsuite/test_resource_sync.c @@ -0,0 +1,138 @@ +/* + * test_resource_sync.c - regression test for bug-1569: collect_load_indices() + * (resource_sync.c) must tag every periodic resource value with the + * worker's own JOIN identity (xcecd_ctx.virtual_name -- the -n/--name value, + * set from config->virutal_name in xcecd_init(), xcecd.c), not + * gethostname()'s raw OS/container hostname. + * + * Why this matters: the controller forwards each report's .host field to + * the scheduler UNMODIFIED (do_update_resvals(), src/controller/opupd.c), + * which keys EVERY other per-worker resource value by this SAME join + * identity (mark_host_res()/update_val_res(), src/controller/worker.c; + * sched_get_worker(), src/scheduler/src/core_events.c). A mismatch here + * silently drops every periodic ("mem"/"r15s"/etc.) resource report for the + * real worker -- live-confirmed against two independent deployments in this + * sandbox: gethostname() returned a generic container hostname ("xce"), not + * the worker's configured "-n pc13v" identity, so the scheduler's own + * "unknown worker xce, skipping" WARN fired every ~15s (resource_sync's own + * LOAD_INDICES_INTERVAL_DEFAULT) and "info worker pc13v" never showed real + * mem/r15s/etc values despite xce_test's live `resval` query showing them + * correctly (a DIFFERENT, correctly-addressed query path). + * + * White-box #include of ../src/resource_sync.c (same convention this + * suite already establishes for controller_connector.c/xcecd.c). The 11 + * builtin load-index getters (node_info_get_15s/etc., real implementations + * in node_information.c, not linked here) are stubbed as trivial + * deterministic returns -- this test is about collect_load_indices()'s own + * .host-tagging logic, not the real /proc-reading getters' own values. + */ +#include +#include +#include + +#include + +#include "xcecd.h" + +/* Real globals this white-box TU's resource_sync.c references + * (xcecd_ctx.virtual_name) -- not linking xcecd.c itself (its own + * main()/full daemon init is out of scope for this narrow test), so a + * plain definition here satisfies the linker, matching this suite's own + * "provide just the global a white-box #include needs" idiom + * (test_xcecd_startup_order.c's g_dummy_subsystem-style stubs). */ +xcecd_context_t xcecd_ctx; + +/* Stubs for the 11 builtin load-index getters (node_information.c, not + * linked here) -- deterministic, non-NULL so collect_load_indices() never + * takes its own NULL-getter fallback path (out of scope for this test). */ +static char* stub_getter(void) +{ + return strdup("1.0"); +} + +char* node_info_get_15s(void) { return stub_getter(); } +char* node_info_get_1m(void) { return stub_getter(); } +char* node_info_get_15m(void) { return stub_getter(); } +char* node_info_get_ut(void) { return stub_getter(); } +char* node_info_get_pg(void) { return stub_getter(); } +char* node_info_get_ls(void) { return stub_getter(); } +char* node_info_get_it(void) { return stub_getter(); } +char* node_info_get_swap(void) { return stub_getter(); } +char* node_info_get_mem(void) { return stub_getter(); } +char* node_info_get_tmp(void) { return stub_getter(); } +char* node_info_get_io(void) { return stub_getter(); } + +/* Linker stub for controller_connector.c's real implementation -- only + * referenced by resource_sync_thread() (the background polling loop), + * never invoked by this test (which calls collect_load_indices() + * directly). */ +bool xcecd_send_load_indices(xce_resval_t** vals, int count) +{ + (void)vals; + (void)count; + return true; +} + +#include "../src/resource_sync.c" + +void setUp(void) { return; } +void tearDown(void) { return; } + +static void +test_collect_load_indices_tags_host_with_virtual_name_not_gethostname(void) +{ + xce_resval_t* vals; + int count = 0; + int i; + + memset(&xcecd_ctx, 0, sizeof(xcecd_ctx)); + xcecd_ctx.virtual_name = (char*)"pc13v"; + + vals = collect_load_indices(&count); + + TEST_ASSERT_NOT_NULL(vals); + TEST_ASSERT_TRUE(count > 0); + + for (i = 0; i < count; i++) { + TEST_ASSERT_NOT_NULL(vals[i].host); + TEST_ASSERT_EQUAL_STRING("pc13v", vals[i].host); + } + + free_resvals(vals, count); + + printf("test_collect_load_indices_tags_host_with_virtual_name_not_" + "gethostname: PASS\n"); +} + +static void +test_collect_load_indices_falls_back_to_gethostname_when_virtual_name_unset( + void) +{ + xce_resval_t* vals; + int count = 0; + char expected[256]; + + memset(&xcecd_ctx, 0, sizeof(xcecd_ctx)); + xcecd_ctx.virtual_name = NULL; + + TEST_ASSERT_EQUAL_INT(0, gethostname(expected, sizeof(expected))); + + vals = collect_load_indices(&count); + + TEST_ASSERT_NOT_NULL(vals); + TEST_ASSERT_TRUE(count > 0); + TEST_ASSERT_EQUAL_STRING(expected, vals[0].host); + + free_resvals(vals, count); + + printf("test_collect_load_indices_falls_back_to_gethostname_when_" + "virtual_name_unset: PASS\n"); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST(test_collect_load_indices_tags_host_with_virtual_name_not_gethostname); + RUN_TEST(test_collect_load_indices_falls_back_to_gethostname_when_virtual_name_unset); + return UNITY_END(); +}