fix: close controller-peers-env-fallback validation gaps (bug-1909..1912)
- bug-1912: xce_parse_peers() ignores whitespace around entries and skips
blank ones, matching xceadmin's resolve_controllers(), so "a=h:1, b=h:2"
names the second candidate "b" (5 libxce tests + regression case).
- bug-1911: xcecd frees the -l log dir before the -h / --version returns
(regression case: no LeakSanitizer report, exit 0).
- bug-1910: both version scripts build the apiserver with -mod=vendor
(as the meson build does) instead of -mod=readonly, so they no longer
need a Go module cache.
- bug-1909: test-version-startup-consistency.sh accepts exactly the
dirty-content-fingerprint pairing ('<sha>-<8 hex>' in --version with
'<sha>-dirty' in the startup log), with self-test cases (e)/(f).
Both version scripts now exit 0 on a dirty tree with XCE_CONTROLLER_PEERS
set; regression script 35/35; xce-build all: C 127/0, scheduler 16/0,
xceadmin 364 passed, no ASAN reports.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
66520e6b1a
commit
ef6e562cde
7 changed files with 159 additions and 9 deletions
|
|
@ -11,6 +11,7 @@
|
|||
* Backoff sleep is skipped when s_connect_fn is set (i.e., in unit tests)
|
||||
* so tests do not need to wait for real timeouts.
|
||||
*/
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -35,6 +36,19 @@ int xce_parse_peers(const char* peers_str, xce_cluster_config_t* cfg)
|
|||
char* saveptr;
|
||||
char* tok = strtok_r(buf, ",", &saveptr);
|
||||
while (tok && cfg->candidates_count < MAX_HA_CANDIDATES) {
|
||||
/* bug-1912: ignore whitespace around each entry and skip entries
|
||||
* that are blank after trimming, as xceadmin's
|
||||
* resolve_controllers() does, so "a=h:1, b=h:2" names "b". */
|
||||
while (isspace((unsigned char)*tok))
|
||||
tok++;
|
||||
char* end = tok + strlen(tok);
|
||||
while (end > tok && isspace((unsigned char)end[-1]))
|
||||
*--end = '\0';
|
||||
if (*tok == '\0') {
|
||||
tok = strtok_r(NULL, ",", &saveptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Format: name=host:port OR host:port */
|
||||
char* eq = strchr(tok, '=');
|
||||
char* addr = eq ? eq + 1 : tok;
|
||||
|
|
|
|||
|
|
@ -420,6 +420,63 @@ static void test_find_identity_empty_name_matches_empty_hostname(void)
|
|||
printf("test_find_identity_empty_name_matches_empty_hostname: PASS\n");
|
||||
}
|
||||
|
||||
/* bug-1912: xce_parse_peers() trims whitespace around each comma-separated
|
||||
* entry and skips entries that are blank after trimming, matching xceadmin's
|
||||
* resolve_controllers(), so "a=h:1, b=h:2" names the second candidate "b". */
|
||||
static void test_parse_peers_trims_spaces_around_entries(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_parse_peers("a=h1:1, b=h2:2 ,\tc=h3:3 ", &cfg));
|
||||
TEST_ASSERT_EQUAL_INT(3, cfg.candidates_count);
|
||||
TEST_ASSERT_EQUAL_STRING("a", cfg.candidates[0].name);
|
||||
TEST_ASSERT_EQUAL_STRING("b", cfg.candidates[1].name);
|
||||
TEST_ASSERT_EQUAL_STRING("c", cfg.candidates[2].name);
|
||||
TEST_ASSERT_EQUAL_STRING("h1", cfg.candidates[0].hostname);
|
||||
TEST_ASSERT_EQUAL_STRING("h2", cfg.candidates[1].hostname);
|
||||
TEST_ASSERT_EQUAL_STRING("h3", cfg.candidates[2].hostname);
|
||||
TEST_ASSERT_EQUAL_INT(1, cfg.candidates[0].port);
|
||||
TEST_ASSERT_EQUAL_INT(2, cfg.candidates[1].port);
|
||||
TEST_ASSERT_EQUAL_INT(3, cfg.candidates[2].port);
|
||||
printf("test_parse_peers_trims_spaces_around_entries: PASS\n");
|
||||
}
|
||||
|
||||
static void test_parse_peers_trims_unnamed_entry(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_parse_peers(" localhost:9991 ", &cfg));
|
||||
TEST_ASSERT_EQUAL_INT(1, cfg.candidates_count);
|
||||
TEST_ASSERT_EQUAL_STRING("", cfg.candidates[0].name);
|
||||
TEST_ASSERT_EQUAL_STRING("localhost", cfg.candidates[0].hostname);
|
||||
TEST_ASSERT_EQUAL_INT(9991, cfg.candidates[0].port);
|
||||
printf("test_parse_peers_trims_unnamed_entry: PASS\n");
|
||||
}
|
||||
|
||||
static void test_parse_peers_skips_blank_entries(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_parse_peers("a=h1:1, ,b=h2:2", &cfg));
|
||||
TEST_ASSERT_EQUAL_INT(2, cfg.candidates_count);
|
||||
TEST_ASSERT_EQUAL_STRING("a", cfg.candidates[0].name);
|
||||
TEST_ASSERT_EQUAL_STRING("b", cfg.candidates[1].name);
|
||||
printf("test_parse_peers_skips_blank_entries: PASS\n");
|
||||
}
|
||||
|
||||
static void test_parse_peers_all_blank_rejected(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
TEST_ASSERT_EQUAL_INT(-1, xce_parse_peers(" , \t ", &cfg));
|
||||
TEST_ASSERT_EQUAL_INT(0, cfg.candidates_count);
|
||||
printf("test_parse_peers_all_blank_rejected: PASS\n");
|
||||
}
|
||||
|
||||
/* Unchanged behavior: an entry without a port is still a parse error. */
|
||||
static void test_parse_peers_entry_without_port_rejected(void)
|
||||
{
|
||||
xce_cluster_config_t cfg = {0};
|
||||
TEST_ASSERT_EQUAL_INT(-1, xce_parse_peers("a=h1:1, b=h2", &cfg));
|
||||
printf("test_parse_peers_entry_without_port_rejected: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Test group entry point ─────────────────────────────────────── */
|
||||
void test_cluster(void)
|
||||
{
|
||||
|
|
@ -450,4 +507,9 @@ void test_cluster(void)
|
|||
RUN_TEST(test_find_identity_first_duplicate_wins);
|
||||
RUN_TEST(test_find_identity_null_name);
|
||||
RUN_TEST(test_find_identity_empty_name_matches_empty_hostname);
|
||||
RUN_TEST(test_parse_peers_trims_spaces_around_entries);
|
||||
RUN_TEST(test_parse_peers_trims_unnamed_entry);
|
||||
RUN_TEST(test_parse_peers_skips_blank_entries);
|
||||
RUN_TEST(test_parse_peers_all_blank_rejected);
|
||||
RUN_TEST(test_parse_peers_entry_without_port_rejected);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue