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:
Dushnilin
2026-09-02 14:00:54 +03:00
parent 0f0eb90cdc
commit b82a151c87
5 changed files with 87 additions and 2 deletions
+2
View File
@@ -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'
+31 -1
View File
@@ -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 }
];
+38 -1
View File
@@ -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'
+12
View File
@@ -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'
+4
View File
@@ -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 ] ||