fix: inject DNS defaults on forkop->tachyon migration, add LuCI E2E check
Fixes Tachyon startup abort when migrated config has no dns_server or bootstrap_dns_server (forkop/podkop did not require these fields). Changes: - migration.uc: add ensure_dns_server_defaults migration that injects 77.88.8.8 as fallback when either dns list is empty - default config: pre-mark orphan_section_interface_cleanup and ensure_dns_server_defaults as applied so fresh installs skip them - tests/config_migration.sh: regression tests for both missing-DNS cases and stable named-marker assertion for the new migration - tests/package_contract.sh: enforce that default config ships both new migration IDs in applied_migrations - tests/docker_e2e_test.sh: also install luci-app-tachyon and verify menu.d + acl.d files are present after install (LuCI menu bug class)
This commit is contained in:
@@ -5,6 +5,8 @@ config settings 'settings'
|
||||
list applied_migrations 'http_connection_urls'
|
||||
list applied_migrations 'dns_hosts_to_option'
|
||||
list applied_migrations 'global_hosts_to_section'
|
||||
list applied_migrations 'orphan_section_interface_cleanup'
|
||||
list applied_migrations 'ensure_dns_server_defaults'
|
||||
option dns_type 'udp'
|
||||
list dns_server '77.88.8.8'
|
||||
list bootstrap_dns_server '77.88.8.8'
|
||||
|
||||
@@ -1292,6 +1292,35 @@ function migrate_http_connection_urls(ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure both main and bootstrap DNS server lists have at least one entry.
|
||||
// forkop/podkop did not require bootstrap_dns_server, so it is often absent after
|
||||
// migration. The validator refuses to start Tachyon when either list is empty.
|
||||
// The default "77.88.8.8" matches the runtime fallback in singbox/dns.uc.
|
||||
function migrate_ensure_dns_server_defaults(ctx) {
|
||||
let settings = ctx.model.settings;
|
||||
let dns_server = list_option(settings, "dns_server");
|
||||
let has_dns = false;
|
||||
for (let v in dns_server) {
|
||||
if (as_string(v) != "") {
|
||||
has_dns = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_dns)
|
||||
set_list_option(ctx, settings, "dns_server", [ "77.88.8.8" ]);
|
||||
|
||||
let bootstrap = list_option(settings, "bootstrap_dns_server");
|
||||
let has_bootstrap = false;
|
||||
for (let v in bootstrap) {
|
||||
if (as_string(v) != "") {
|
||||
has_bootstrap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_bootstrap)
|
||||
set_list_option(ctx, settings, "bootstrap_dns_server", [ "77.88.8.8" ]);
|
||||
}
|
||||
|
||||
function migrate_dns_hosts_to_option(ctx) {
|
||||
let raw = object_or_empty(ctx.model.settings)["dns_hosts"];
|
||||
if (raw == null)
|
||||
@@ -1405,7 +1434,8 @@ const MIGRATIONS = [
|
||||
{ id: "http_connection_urls", run: migrate_http_connection_urls },
|
||||
{ id: "dns_hosts_to_option", run: migrate_dns_hosts_to_option },
|
||||
{ id: "global_hosts_to_section", run: migrate_global_hosts_to_section },
|
||||
{ id: "orphan_section_interface_cleanup", run: migrate_orphan_section_interfaces }
|
||||
{ id: "orphan_section_interface_cleanup", run: migrate_orphan_section_interfaces },
|
||||
{ id: "ensure_dns_server_defaults", run: migrate_ensure_dns_server_defaults }
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ grep -Fq '{ id: "enable_component_checks", run: migrate_enable_component_checks
|
||||
fail "component check migration must have a stable named marker"
|
||||
grep -Fq '{ id: "http_connection_urls", run: migrate_http_connection_urls }' "$MIGRATION" ||
|
||||
fail "HTTP connection URL migration must have a stable named marker"
|
||||
grep -Fq '{ id: "ensure_dns_server_defaults", run: migrate_ensure_dns_server_defaults }' "$MIGRATION" ||
|
||||
fail "DNS server defaults migration must have a stable named marker"
|
||||
grep -Fq 'release_at_most(ctx, "1.0.1")' "$MIGRATION" ||
|
||||
fail "release-specific migrations must use the source release only as a condition"
|
||||
grep -Fq 'release_at_most(ctx, "1.0.4")' "$MIGRATION" ||
|
||||
@@ -175,7 +177,7 @@ assert(JSON.stringify(config.settings.dns_server) === JSON.stringify(['9.9.9.9']
|
||||
assert(JSON.stringify(config.settings.bootstrap_dns_server) === JSON.stringify(['1.1.1.1']), 'legacy Bootstrap DNS scalar migrated to ordered list');
|
||||
assert(config.settings.config_version === '1.0.5', 'legacy config should be marked at the current schema version');
|
||||
assert(config.settings.component_update_check_enabled === '1', 'component update checks should be enabled during migration');
|
||||
assert(JSON.stringify(config.settings.applied_migrations) === JSON.stringify(['interface_sections', 'enable_component_checks', 'http_connection_urls', 'dns_hosts_to_option', 'global_hosts_to_section', 'orphan_section_interface_cleanup']), 'legacy config should record named migrations');
|
||||
assert(JSON.stringify(config.settings.applied_migrations) === JSON.stringify(['interface_sections', 'enable_component_checks', 'http_connection_urls', 'dns_hosts_to_option', 'global_hosts_to_section', 'orphan_section_interface_cleanup', 'ensure_dns_server_defaults']), 'legacy config should record named migrations');
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) {
|
||||
@@ -712,4 +714,39 @@ ucode -L "$TACHYON_LIB" "$MIGRATION" commit
|
||||
grep -Fxq 'commit tachyon' "$WORK_DIR/uci-commit.log" ||
|
||||
fail "commit mode must commit tachyon through core.uci"
|
||||
|
||||
|
||||
# Regression: forkop configs often lack bootstrap_dns_server.
|
||||
# The ensure_dns_server_defaults migration must inject a default so the validator
|
||||
# does not abort Tachyon startup with "At least one Bootstrap DNS server is required".
|
||||
cat >"$WORK_DIR/no_bootstrap.state" <<'EOF_UCI'
|
||||
tachyon.settings=settings
|
||||
tachyon.settings.dns_server=8.8.8.8
|
||||
EOF_UCI
|
||||
: >"$WORK_DIR/no_bootstrap.log"
|
||||
TACHYON_UCI_STATE_FILE="$WORK_DIR/no_bootstrap.state" \
|
||||
TACHYON_UCI_LOG_FILE="$WORK_DIR/no_bootstrap.log" \
|
||||
TACHYON_CONFIG_NAME="tachyon" \
|
||||
TACHYON_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
|
||||
ucode -L "$TACHYON_LIB" "$MIGRATION" migrate
|
||||
|
||||
grep -Fq 'tachyon.settings.bootstrap_dns_server=77.88.8.8' "$WORK_DIR/no_bootstrap.state" ||
|
||||
fail "ensure_dns_server_defaults must inject 77.88.8.8 when bootstrap_dns_server is missing"
|
||||
|
||||
# Regression: forkop configs with neither dns_server nor bootstrap_dns_server must
|
||||
# get both defaults injected.
|
||||
cat >"$WORK_DIR/no_dns.state" <<'EOF_UCI'
|
||||
tachyon.settings=settings
|
||||
EOF_UCI
|
||||
: >"$WORK_DIR/no_dns.log"
|
||||
TACHYON_UCI_STATE_FILE="$WORK_DIR/no_dns.state" \
|
||||
TACHYON_UCI_LOG_FILE="$WORK_DIR/no_dns.log" \
|
||||
TACHYON_CONFIG_NAME="tachyon" \
|
||||
TACHYON_INTERNAL_CONFIG_TRIGGER_GUARD="$WORK_DIR/internal-config-change" \
|
||||
ucode -L "$TACHYON_LIB" "$MIGRATION" migrate
|
||||
|
||||
grep -Fq 'tachyon.settings.dns_server=77.88.8.8' "$WORK_DIR/no_dns.state" ||
|
||||
fail "ensure_dns_server_defaults must inject 77.88.8.8 for missing dns_server"
|
||||
grep -Fq 'tachyon.settings.bootstrap_dns_server=77.88.8.8' "$WORK_DIR/no_dns.state" ||
|
||||
fail "ensure_dns_server_defaults must inject 77.88.8.8 for missing bootstrap_dns_server"
|
||||
|
||||
printf 'installer config migration checks passed\n'
|
||||
|
||||
@@ -142,6 +142,18 @@ sleep 5
|
||||
echo "=== Installing Tachyon IPK ==="
|
||||
"$DOCKER_BIN" exec "$CONTAINER_NAME" opkg install --force-reinstall /work/build-out/tachyon_1.0.0.ipk
|
||||
|
||||
echo "=== Installing LuCI app IPK ==="
|
||||
"$DOCKER_BIN" exec "$CONTAINER_NAME" opkg install --force-reinstall /work/build-out/luci-app-tachyon_1.0.0.ipk
|
||||
|
||||
echo "=== Verifying LuCI menu and ACL registration ==="
|
||||
"$DOCKER_BIN" exec "$CONTAINER_NAME" test -f /usr/share/luci/menu.d/luci-app-tachyon.json \
|
||||
|| { echo "FAIL: menu.d/luci-app-tachyon.json missing after install"; exit 1; }
|
||||
"$DOCKER_BIN" exec "$CONTAINER_NAME" test -f /usr/share/rpcd/acl.d/luci-app-tachyon.json \
|
||||
|| { echo "FAIL: acl.d/luci-app-tachyon.json missing after install"; exit 1; }
|
||||
"$DOCKER_BIN" exec "$CONTAINER_NAME" grep -q '"tachyon/tachyon"' /usr/share/luci/menu.d/luci-app-tachyon.json \
|
||||
|| { echo "FAIL: menu.d entry does not reference correct view path"; exit 1; }
|
||||
echo "OK: LuCI app registration files in place"
|
||||
|
||||
echo "=== Configuring Tachyon ==="
|
||||
"$DOCKER_BIN" exec "$CONTAINER_NAME" uci set tachyon.settings.enabled='1'
|
||||
"$DOCKER_BIN" exec "$CONTAINER_NAME" uci set tachyon.settings.log_level='debug'
|
||||
|
||||
@@ -76,6 +76,10 @@ grep -Fq "list applied_migrations 'dns_hosts_to_option'" "$TACHYON_CONFIG" ||
|
||||
fail "new installations must mark the dns_hosts to option migration as applied"
|
||||
grep -Fq "list applied_migrations 'global_hosts_to_section'" "$TACHYON_CONFIG" ||
|
||||
fail "new installations must mark the global hosts to section migration as applied"
|
||||
grep -Fq "list applied_migrations 'orphan_section_interface_cleanup'" "$TACHYON_CONFIG" ||
|
||||
fail "new installations must mark the orphan section interface cleanup migration as applied"
|
||||
grep -Fq "list applied_migrations 'ensure_dns_server_defaults'" "$TACHYON_CONFIG" ||
|
||||
fail "new installations must mark the DNS server defaults migration as applied"
|
||||
grep -Fq '/usr/lib/tachyon/config/migration.uc migrate' "$TACHYON_MAKEFILE" ||
|
||||
fail "OpenWrt package postinst must run configuration migrations"
|
||||
[ "$(grep -Fc '/usr/lib/tachyon/config/migration.uc migrate' "$BUILD_SCRIPT")" -ge 3 ] ||
|
||||
|
||||
Reference in New Issue
Block a user