feat(scheduler-core): add scheduler admin opcode schema (task 7.4)
Adds SchedAdminAction (recordStart/recordStop/recordStatus), SchedAdminRequest (action/recordFile), SchedAdminReply (ok/error/ recording/file/eventCount/startedAt) to controller.capnp, matching requirements.md Req 5 AC 10 verbatim. Adds schedAdmin@42 to OPCode, schedadminmsg@31 to Request.body, schedadminreplymsg@19 to RequestReply.data -- the exact next-available ordinals, verified live before implementation and re-verified byte-identical against every existing value/member. Deliberately schema-only, per this task's own scope -- the controller dispatch logic (cluster.c's case OPCode_schedAdmin:) is task 10.1's job. cluster.c, src/frontend/, src/xceadmin/, src/scheduler/ all untouched. Round-trip tests cover all 3 SchedAdminAction values and both ok=true/ok=false SchedAdminReply cases, including an asymmetric ok=false/recording=true combination that rules out a Bool-field bit-packing mixup at the wire level. Closes out Group 7 (Foundation: Replay Wire Format) entirely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019dw5WnN3DXStaEaPYfACU1
This commit is contained in:
parent
bbb3972ae6
commit
5cd8687b32
4 changed files with 217 additions and 1 deletions
|
|
@ -1344,6 +1344,157 @@ static void test_snapshot_data_round_trip(void)
|
|||
printf("test_snapshot_data_round_trip: PASS\n");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Test 16: SchedAdminRequest round-trip (scheduler-core task 7.4, */
|
||||
/* Req 5 AC 10) -- encodes and decodes a SchedAdminRequest for each of */
|
||||
/* the three SchedAdminAction enum values (recordStart/recordStop/ */
|
||||
/* recordStatus), proving the plain enum-typed `action` field (mapped */
|
||||
/* to `int` in sched_admin_req_t, same convention as HostRole/ */
|
||||
/* JoinData.role) and the `recordFile` Text field round-trip field-for- */
|
||||
/* field. recordStart uses a non-empty recordFile; recordStop/ */
|
||||
/* recordStatus use an empty one (auto-generate / pure query), covering */
|
||||
/* both the populated and empty-string Text cases. Same encode_X_ptr/ */
|
||||
/* decode_X codec-bridge pattern as Test 9 (job_state_change_event_t) */
|
||||
/* since sched_admin_req_t is a fully-scalar struct plus one Text */
|
||||
/* field, no Data fields. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void sched_admin_request_round_trip_case(int action, const char *record_file)
|
||||
{
|
||||
sched_admin_req_t src;
|
||||
memset(&src, 0, sizeof(src));
|
||||
src.action = action;
|
||||
src.recordFile = (char *)record_file;
|
||||
|
||||
struct capn c;
|
||||
capn_init_malloc(&c);
|
||||
struct capn_segment *cs = capn_root(&c).seg;
|
||||
|
||||
SchedAdminRequest_ptr p;
|
||||
encode_SchedAdminRequest_ptr(cs, &p, &src);
|
||||
capn_setp(capn_root(&c), 0, p.p);
|
||||
|
||||
int64_t sz = capn_size(&c);
|
||||
TEST_ASSERT_TRUE(sz > 0);
|
||||
uint8_t *buf = (uint8_t *)malloc((size_t)sz);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
capn_write_mem(&c, buf, (size_t)sz, 0);
|
||||
capn_free(&c);
|
||||
|
||||
struct capn d;
|
||||
TEST_ASSERT_EQUAL_INT(0, capn_init_mem(&d, buf, (size_t)sz, 0));
|
||||
SchedAdminRequest_ptr dp;
|
||||
dp.p = capn_getp(capn_root(&d), 0, 1);
|
||||
struct SchedAdminRequest wire;
|
||||
read_SchedAdminRequest(&wire, dp);
|
||||
sched_admin_req_t dst;
|
||||
memset(&dst, 0, sizeof(dst));
|
||||
decode_SchedAdminRequest(&dst, &wire);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(action, (int)dst.action);
|
||||
TEST_ASSERT_EQUAL_STRING(record_file, dst.recordFile);
|
||||
|
||||
free_SchedAdminRequest(&dst);
|
||||
capn_free(&d);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void test_sched_admin_request_round_trip(void)
|
||||
{
|
||||
/* recordStart with a non-empty recordFile. */
|
||||
sched_admin_request_round_trip_case(SchedAdminAction_recordStart,
|
||||
"manual-session.replay");
|
||||
/* recordStop with an empty recordFile (stop takes no filename). */
|
||||
sched_admin_request_round_trip_case(SchedAdminAction_recordStop, "");
|
||||
/* recordStatus with an empty recordFile (status is a pure query). */
|
||||
sched_admin_request_round_trip_case(SchedAdminAction_recordStatus, "");
|
||||
|
||||
printf("test_sched_admin_request_round_trip: PASS\n");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Test 17: SchedAdminReply round-trip (scheduler-core task 7.4, */
|
||||
/* Req 5 AC 10) -- encodes and decodes a SchedAdminReply for both an */
|
||||
/* ok=true success case and an ok=false error case, each populating all */
|
||||
/* six fields (ok, error, recording, file, eventCount, startedAt) with */
|
||||
/* non-default values, proving every field -- including the two Bool */
|
||||
/* fields (ok, recording) packed into the same wire byte -- round-trips */
|
||||
/* independently regardless of the other's value. `error` is modelled */
|
||||
/* as a general diagnostic-message field (populated with a non-fatal */
|
||||
/* advisory even on success, an outright failure reason on error) so */
|
||||
/* both cases can legitimately carry a non-empty value. Same codec- */
|
||||
/* bridge pattern as Test 9/16. */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
static void sched_admin_reply_round_trip_case(int ok, const char *error,
|
||||
int recording, const char *file,
|
||||
uint64_t event_count,
|
||||
uint64_t started_at)
|
||||
{
|
||||
sched_admin_reply_t src;
|
||||
memset(&src, 0, sizeof(src));
|
||||
src.ok = ok;
|
||||
src.error = (char *)error;
|
||||
src.recording = recording;
|
||||
src.file = (char *)file;
|
||||
src.eventCount = event_count;
|
||||
src.startedAt = started_at;
|
||||
|
||||
struct capn c;
|
||||
capn_init_malloc(&c);
|
||||
struct capn_segment *cs = capn_root(&c).seg;
|
||||
|
||||
SchedAdminReply_ptr p;
|
||||
encode_SchedAdminReply_ptr(cs, &p, &src);
|
||||
capn_setp(capn_root(&c), 0, p.p);
|
||||
|
||||
int64_t sz = capn_size(&c);
|
||||
TEST_ASSERT_TRUE(sz > 0);
|
||||
uint8_t *buf = (uint8_t *)malloc((size_t)sz);
|
||||
TEST_ASSERT_NOT_NULL(buf);
|
||||
capn_write_mem(&c, buf, (size_t)sz, 0);
|
||||
capn_free(&c);
|
||||
|
||||
struct capn d;
|
||||
TEST_ASSERT_EQUAL_INT(0, capn_init_mem(&d, buf, (size_t)sz, 0));
|
||||
SchedAdminReply_ptr dp;
|
||||
dp.p = capn_getp(capn_root(&d), 0, 1);
|
||||
struct SchedAdminReply wire;
|
||||
read_SchedAdminReply(&wire, dp);
|
||||
sched_admin_reply_t dst;
|
||||
memset(&dst, 0, sizeof(dst));
|
||||
decode_SchedAdminReply(&dst, &wire);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(ok ? 1 : 0, dst.ok ? 1 : 0);
|
||||
TEST_ASSERT_EQUAL_STRING(error, dst.error);
|
||||
TEST_ASSERT_EQUAL_INT(recording ? 1 : 0, dst.recording ? 1 : 0);
|
||||
TEST_ASSERT_EQUAL_STRING(file, dst.file);
|
||||
TEST_ASSERT_EQUAL_UINT64(event_count, dst.eventCount);
|
||||
TEST_ASSERT_EQUAL_UINT64(started_at, dst.startedAt);
|
||||
|
||||
free_SchedAdminReply(&dst);
|
||||
capn_free(&d);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void test_sched_admin_reply_round_trip(void)
|
||||
{
|
||||
/* ok=true success case (e.g. a recordStatus reply): all six fields
|
||||
* non-default. */
|
||||
sched_admin_reply_round_trip_case(1, "warning: recording directory nearing disk quota",
|
||||
1, "session-0007.replay",
|
||||
12345ULL, 1700000000000000000ULL);
|
||||
|
||||
/* ok=false error case (e.g. a failed recordStart): all six fields
|
||||
* still non-default, proving the wire carries every field regardless
|
||||
* of `ok`. */
|
||||
sched_admin_reply_round_trip_case(0, "recording directory not writable",
|
||||
1, "session-0006.replay",
|
||||
999ULL, 1699999999000000000ULL);
|
||||
|
||||
printf("test_sched_admin_reply_round_trip: PASS\n");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Suite entry point */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -1368,4 +1519,6 @@ void test_e2e_capnp(void)
|
|||
RUN_TEST(test_preempt_decision_round_trip);
|
||||
RUN_TEST(test_replay_decision_dispatch_union_round_trip);
|
||||
RUN_TEST(test_snapshot_data_round_trip);
|
||||
RUN_TEST(test_sched_admin_request_round_trip);
|
||||
RUN_TEST(test_sched_admin_reply_round_trip);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue