feat: implement handle_config_request(), fetch_config_from_primary(), handle_controller_join()

- handle_config_request(): primary-side handler sends up-to-date flag or
  version_tag; SHA-256 integrity check guards against in-place file changes;
  sha256/bytes fields left NULL pending Task 11 typemap shims
- fetch_config_from_primary(): standby fetch with exponential backoff retry
  (20 attempts, 500ms..5s); falls back to saved config via load_primary_config();
  do_one_config_fetch() stubbed (-ENOSYS) until Task 11 wires capnp over SSL
- handle_controller_join(): registers standby endpoint in ha.standby_endpoints
  via zarray_store() for OP_upgradeConfig fan-out; sends success reply
- Moved load_primary_config() from myconf.c to config_repl.c so the unit test
  binary (which links config_repl.c but not myconf.c) can resolve it
This commit is contained in:
Rongsong Shen 2026-05-13 17:46:56 +00:00
parent 93b8616f11
commit 8f4db308da
3 changed files with 331 additions and 122 deletions

View file

@ -152,119 +152,6 @@ cluster_config_t* get_default_conf(void* cl)
return cfg;
}
/* ── load_primary_config ────────────────────────── */
int load_primary_config(cluster_t *c)
{
myconf_t *myconf = c->myconf;
/* Build conf dir path: <workdir>/<name> */
char conf_dir[4096];
snprintf(conf_dir, sizeof(conf_dir), "%s/%s", myconf->workdir, myconf->name);
/* Resolve conf/current → versioned filename */
char fname[256] = {0};
int rc = conf_read_current_filename(conf_dir, fname, sizeof(fname));
if (rc != 0) {
ERROR("Primary has no config at '%s/conf/current' (rc=%d) — "
"provision the config before starting",
conf_dir, rc);
return -ENOENT;
}
/* Heap-allocate conffile so it survives past this stack frame */
free(myconf->conffile);
myconf->conffile = NULL;
if (asprintf(&myconf->conffile, "%s/conf/%s", conf_dir, fname) < 0)
return -ENOMEM;
/* Read raw bytes and check size */
uint8_t *bytes = NULL;
size_t blen = 0;
rc = conf_read_raw(myconf->conffile, &bytes, &blen);
if (rc != 0) {
ERROR("Primary config '%s' unreadable: %d", myconf->conffile, rc);
free(myconf->conffile);
myconf->conffile = NULL;
return rc;
}
/* Compute and cache SHA-256 */
SHA256(bytes, blen, myconf->config_sha256);
uint64_t tag;
memcpy(&tag, myconf->config_sha256, 8);
myconf->config_version_tag = be64toh(tag);
/* Free any prior decoded config before overwriting the pointer */
if (myconf->cluster_config) {
free_ClusterConfig(myconf->cluster_config);
free(myconf->cluster_config);
myconf->cluster_config = NULL;
}
/* Decode Cap'n Proto directly into myconf->cluster_config */
rc = load_cluster_config_from_file(myconf->conffile, &myconf->cluster_config);
if (rc != 0) {
ERROR("Primary config '%s' failed Cap'n Proto decode: %d",
myconf->conffile, rc);
free(bytes);
free(myconf->conffile);
myconf->conffile = NULL;
return rc;
}
if (myconf->cluster_config == NULL) {
ERROR("Primary config '%s' decoded to NULL", myconf->conffile);
free(bytes);
free(myconf->conffile);
myconf->conffile = NULL;
return -EINVAL;
}
free(bytes);
fill_default_cluster_config(myconf->cluster_config);
/* Check for reserved lost_and_found name */
if (config_contains_reserved_name(myconf->cluster_config)) {
ERROR("Config '%s' contains reserved name 'lost_and_found'",
myconf->conffile);
free_ClusterConfig(myconf->cluster_config);
free(myconf->cluster_config);
myconf->cluster_config = NULL;
free(myconf->conffile);
myconf->conffile = NULL;
return -EINVAL;
}
/* Default generation to 1 if not set */
if (myconf->cluster_config->config_generation == 0)
myconf->cluster_config->config_generation = 1;
myconf->config_generation = myconf->cluster_config->config_generation;
/* Check staleness */
myconf->conf_stale = conf_check_stale(conf_dir, myconf->conf_stale_timeout);
if (myconf->conf_stale)
WARN("Primary config appears stale (age > %lds)",
(long)myconf->conf_stale_timeout);
INFO("Primary config loaded: tag=%016" PRIx64 " gen=%" PRIu64,
myconf->config_version_tag, myconf->config_generation);
return 0;
}
/* ── fetch_config_from_primary ──────────────────── */
int fetch_config_from_primary(cluster_t *c)
{
(void)c;
/* Task 9: full implementation — connect to primary, send OP_replConfig,
* receive blob, verify SHA-256, call conf_save_config(), decode into
* myconf->cluster_config. */
ERROR("fetch_config_from_primary: not yet implemented (Task 9)");
return -ENOSYS;
}
/* ── myconf_fini ────────────────────────────────── */
void myconf_fini(myconf_t *myconf)