fix(controller): resource hooks only carry values the controller accepted (bug-1583)

do_update_resvals() fired the AFTER_EVENT HOOK_DETAIL_RESOURCE_UPDATE with
the raw client xce_resval_list_t. update_resval_list() rejects unknown
resource names (-ENOENT) but only logged it, so both listeners -- the
scheduler push and the apiserver store (apiserver_resource_hook_fun ->
handler.go) -- received names the controller had rejected, and the
apiserver stored them.

- update_resval_list() takes an optional `applied` view (caller-sized,
  borrowed pointers) and fills it with just the accepted items.
- do_update_resvals() hands that view to the AFTER_EVENT hook, and skips
  it when nothing was applied. The request still owns every item.

New test_update_resval_list_reports_only_applied_items (RED before: both
items reported). Controller suite 1237/0. Live /xce-test-resources with
mem=5 totally-fake-resource-xyz=1: mem MATCH at controller, scheduler and
apiserver; the fake name is rejected at injection and appears 0 times in
scheduler.log / apiserver.log (verdict FAIL by design, for the rejected
name).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-09-27 03:19:17 +00:00
parent 054a1416bc
commit 70360ccfe8
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
4 changed files with 78 additions and 7 deletions

View file

@ -1,6 +1,7 @@
#include "config.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -31,14 +32,30 @@ static int do_update_resvals(cluster_t* c, endpoint_t* ep,
} else {
xce_update_data_t* updatemsg = request->body.updatemsg;
xce_resval_list_t* resvals = updatemsg->data.resvals;
xce_resval_list_t applied = { .count = 0, .vals = NULL };
/* bug-1583: the AFTER_EVENT listeners (scheduler push,
* apiserver store) must only hear about the values this
* controller actually accepted -- not the raw client list,
* which may name resources it rejected. `applied` borrows
* pointers into `resvals`; nothing changes ownership. */
if ((resvals != NULL) && (resvals->count > 0)) {
applied.vals = (xce_resval_t**)calloc(
(size_t)resvals->count, sizeof(xce_resval_t*));
}
xce_run_hooks(&(c->hooksys), XCE_CONTROLLER_HOOK_RESOURCE,
HOOK_BEFORE_EVENT, HOOK_DETAIL_RESOURCE_UPDATE, c,
resvals);
rc = update_resval_list(c, w, resvals);
xce_run_hooks(&(c->hooksys), XCE_CONTROLLER_HOOK_RESOURCE,
HOOK_AFTER_EVENT, HOOK_DETAIL_RESOURCE_UPDATE, c,
resvals);
rc = update_resval_list(c, w, resvals,
(applied.vals != NULL) ? &applied
: NULL);
if (applied.count > 0) {
xce_run_hooks(&(c->hooksys), XCE_CONTROLLER_HOOK_RESOURCE,
HOOK_AFTER_EVENT,
HOOK_DETAIL_RESOURCE_UPDATE, c, &applied);
}
free(applied.vals);
}
}
}