Add automatic component update checks
This commit is contained in:
@@ -72,6 +72,14 @@ component_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" component_action sin
|
||||
[ "$component_out" = $'action\tcomponent-action\tsing_box\tupdate' ] ||
|
||||
fail "component_action must dispatch through components/action.uc with arguments"
|
||||
|
||||
component_check_cache_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" component_update_check_cache)"
|
||||
[ "$component_check_cache_out" = $'updates\tcomponent-update-check-cache\t\t' ] ||
|
||||
fail "component_update_check_cache must dispatch through components/updates.uc"
|
||||
|
||||
component_updates_out="$(PODKOP_LIB="$fake_lib" ucode "$PODKOP_BIN" component_updates_if_due)"
|
||||
[ "$component_updates_out" = $'updates\tcomponent-updates-if-due\t\t' ] ||
|
||||
fail "component_updates_if_due must dispatch through components/updates.uc"
|
||||
|
||||
rm -f "$fake_lib/service/lifecycle.uc"
|
||||
set +e
|
||||
PODKOP_TEST_DNS_RESTORE_MARKER="$WORK_DIR/dns-restore.marker" PODKOP_LIB="$fake_lib" \
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
PODKOP_LIB="$ROOT_DIR/podkop/files/usr/lib"
|
||||
UPDATES_UC="$PODKOP_LIB/components/updates.uc"
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$WORK_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
fake_lib="$WORK_DIR/lib"
|
||||
cache_dir="$WORK_DIR/cache"
|
||||
job_dir="$WORK_DIR/jobs"
|
||||
state_file="$job_dir/manual.json"
|
||||
output_file="$job_dir/manual.out"
|
||||
timestamp_file="$WORK_DIR/component-update-check.timestamp"
|
||||
mkdir -p \
|
||||
"$fake_lib/core" \
|
||||
"$fake_lib/config" \
|
||||
"$fake_lib/components" \
|
||||
"$fake_lib/service" \
|
||||
"$fake_lib/providers/zapret" \
|
||||
"$fake_lib/providers/zapret2" \
|
||||
"$fake_lib/providers/byedpi" \
|
||||
"$job_dir"
|
||||
|
||||
cat >"$fake_lib/core/uci.uc" <<'UCODE'
|
||||
function get_all(_config, section) {
|
||||
if (section == "settings") {
|
||||
return {
|
||||
component_update_check_enabled: getenv("TEST_COMPONENT_UPDATE_CHECK_ENABLED") || "0",
|
||||
component_update_check_interval: getenv("TEST_COMPONENT_UPDATE_CHECK_INTERVAL") || "1d"
|
||||
};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function section_objects(_config, _type) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return {
|
||||
get_all,
|
||||
section_objects
|
||||
};
|
||||
UCODE
|
||||
|
||||
cat >"$fake_lib/config/connections.uc" <<'UCODE'
|
||||
return {};
|
||||
UCODE
|
||||
|
||||
cat >"$fake_lib/components/action.uc" <<'UCODE'
|
||||
#!/usr/bin/env ucode
|
||||
let component = ARGV[1] || "podkop";
|
||||
let action = ARGV[2] || "check_update";
|
||||
print(sprintf("%J\n", {
|
||||
success: true,
|
||||
component,
|
||||
action,
|
||||
message: "Update is available",
|
||||
current_version: "1.0.0",
|
||||
latest_version: "1.1.0",
|
||||
release_url: "https://example.com/release",
|
||||
changed: 0,
|
||||
status: "outdated"
|
||||
}));
|
||||
UCODE
|
||||
|
||||
cat >"$fake_lib/providers/zapret/runtime.uc" <<'UCODE'
|
||||
#!/usr/bin/env ucode
|
||||
exit(1);
|
||||
UCODE
|
||||
cp "$fake_lib/providers/zapret/runtime.uc" "$fake_lib/providers/zapret2/runtime.uc"
|
||||
cp "$fake_lib/providers/zapret/runtime.uc" "$fake_lib/providers/byedpi/runtime.uc"
|
||||
|
||||
cat >"$fake_lib/service/state.uc" <<'UCODE'
|
||||
#!/usr/bin/env ucode
|
||||
exit(0);
|
||||
UCODE
|
||||
|
||||
updates_ucode() {
|
||||
TEST_COMPONENT_UPDATE_CHECK_ENABLED="${TEST_COMPONENT_UPDATE_CHECK_ENABLED:-1}" \
|
||||
TEST_COMPONENT_UPDATE_CHECK_INTERVAL="${TEST_COMPONENT_UPDATE_CHECK_INTERVAL:-1d}" \
|
||||
PODKOP_LIB="$fake_lib" \
|
||||
PODKOP_COMPONENT_UPDATE_CHECK_CACHE_DIR="$cache_dir" \
|
||||
PODKOP_COMPONENT_UPDATE_CHECK_STATE_FILE="$timestamp_file" \
|
||||
UPDATES_JOB_DIR="$job_dir" \
|
||||
ucode -L "$fake_lib" -L "$PODKOP_LIB" "$UPDATES_UC" "$@"
|
||||
}
|
||||
|
||||
updates_ucode component-action-worker "$state_file" "$output_file" zapret check_update
|
||||
[ -s "$cache_dir/zapret.json" ] ||
|
||||
fail "manual checks must be cached while automatic checks are enabled"
|
||||
|
||||
manual_cache="$(updates_ucode component-update-check-cache)"
|
||||
node -e '
|
||||
const value = JSON.parse(process.argv[1]);
|
||||
if (!value.enabled || value.results.length !== 1 ||
|
||||
value.results[0].component !== "zapret" ||
|
||||
value.results[0].status !== "outdated" ||
|
||||
value.results[0].latest_version !== "1.1.0") process.exit(1);
|
||||
' "$manual_cache" || fail "cached manual check response is invalid"
|
||||
|
||||
rm -rf "$cache_dir"
|
||||
TEST_COMPONENT_UPDATE_CHECK_ENABLED=0 updates_ucode \
|
||||
component-action-worker "$state_file" "$output_file" zapret check_update
|
||||
[ ! -e "$cache_dir/zapret.json" ] ||
|
||||
fail "manual checks must not be cached while automatic checks are disabled"
|
||||
|
||||
rm -f "$state_file" "$timestamp_file"
|
||||
updates_ucode component-updates-if-due
|
||||
[ -s "$cache_dir/podkop.json" ] ||
|
||||
fail "automatic checks must cache the Podkop Plus result"
|
||||
[ -s "$timestamp_file" ] ||
|
||||
fail "automatic checks must record their last run"
|
||||
|
||||
printf 'component update check cache regression checks passed\n'
|
||||
@@ -983,7 +983,9 @@ cat >"$WORK_DIR/reload-state-signatures.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
"list_update_enabled": "1",
|
||||
"update_interval": "6h"
|
||||
"update_interval": "6h",
|
||||
"component_update_check_enabled": "1",
|
||||
"component_update_check_interval": "2h"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
@@ -1096,6 +1098,8 @@ EOF_LIST_SIG
|
||||
cat >"$WORK_DIR/cron-signature.expected" <<'EOF_CRON_SIG'
|
||||
[settings.update_interval]
|
||||
6h
|
||||
[settings.component_update_check_interval]
|
||||
2h
|
||||
[lists.list_proxy.ports]
|
||||
443,80,443-444
|
||||
[lists.list_proxy.community_subnet_lists]
|
||||
|
||||
@@ -112,7 +112,9 @@ cat >"$WORK_DIR/cron-plan.json" <<'JSON'
|
||||
{
|
||||
"settings": {
|
||||
"list_update_enabled": "1",
|
||||
"update_interval": "30m"
|
||||
"update_interval": "30m",
|
||||
"component_update_check_enabled": "1",
|
||||
"component_update_check_interval": "2h"
|
||||
},
|
||||
"section": [
|
||||
{
|
||||
@@ -148,8 +150,8 @@ cat >"$WORK_DIR/cron-plan.json" <<'JSON'
|
||||
}
|
||||
JSON
|
||||
|
||||
assert_eq $'list\t*/30 * * * * /usr/bin/podkop list_update_if_due # list\nsubscription-error\tbad_sub\tbad\nsubscription\t*/45 * * * * /usr/bin/podkop subscription_update_if_due # subscription' \
|
||||
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan.json" /usr/bin/podkop '# list' '# subscription')" \
|
||||
assert_eq $'list\t*/30 * * * * /usr/bin/podkop list_update_if_due # list\nsubscription-error\tbad_sub\tbad\nsubscription\t*/45 * * * * /usr/bin/podkop subscription_update_if_due # subscription\ncomponent\t0 */2 * * * /usr/bin/podkop component_updates_if_due # component' \
|
||||
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan.json" /usr/bin/podkop '# list' '# subscription' '# component')" \
|
||||
"cron refresh plan"
|
||||
|
||||
grep -Fq 'fs.writefile(tmp, as_string(text)) == null' "$UPDATES_UC" ||
|
||||
@@ -173,7 +175,7 @@ cat >"$WORK_DIR/cron-plan-list-disabled.json" <<'JSON'
|
||||
JSON
|
||||
|
||||
assert_eq "list-disabled" \
|
||||
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-list-disabled.json" /usr/bin/podkop '# list' '# subscription')" \
|
||||
"$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-list-disabled.json" /usr/bin/podkop '# list' '# subscription' '# component')" \
|
||||
"disabled list cron plan"
|
||||
|
||||
cat >"$WORK_DIR/cron-plan-invalid-list.json" <<'JSON'
|
||||
@@ -194,7 +196,7 @@ cat >"$WORK_DIR/cron-plan-invalid-list.json" <<'JSON'
|
||||
JSON
|
||||
|
||||
status=0
|
||||
invalid_plan="$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-invalid-list.json" /usr/bin/podkop '# list' '# subscription')" || status="$?"
|
||||
invalid_plan="$(updates_ucode cron-refresh-plan-fixture "$WORK_DIR/cron-plan-invalid-list.json" /usr/bin/podkop '# list' '# subscription' '# component')" || status="$?"
|
||||
assert_eq 1 "$status" "invalid list cron plan status"
|
||||
assert_eq $'list-error\tbad' "$invalid_plan" "invalid list cron plan output"
|
||||
|
||||
@@ -202,9 +204,10 @@ cat >"$WORK_DIR/existing.cron" <<'CRON'
|
||||
0 1 * * * /bin/true # keep
|
||||
* * * * * /usr/bin/podkop list_update_if_due # list
|
||||
* * * * * /usr/bin/podkop subscription_update_if_due # subscription
|
||||
* * * * * /usr/bin/podkop component_updates_if_due # component
|
||||
CRON
|
||||
|
||||
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan.json" "$WORK_DIR/existing.cron" /usr/bin/podkop '# list' '# subscription' >"$WORK_DIR/cron-apply.json"
|
||||
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan.json" "$WORK_DIR/existing.cron" /usr/bin/podkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply.json"
|
||||
node - "$WORK_DIR/cron-apply.json" <<'JS'
|
||||
const fs = require("fs");
|
||||
const value = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
|
||||
@@ -212,6 +215,7 @@ const expected = [
|
||||
"0 1 * * * /bin/true # keep",
|
||||
"*/30 * * * * /usr/bin/podkop list_update_if_due # list",
|
||||
"*/45 * * * * /usr/bin/podkop subscription_update_if_due # subscription",
|
||||
"0 */2 * * * /usr/bin/podkop component_updates_if_due # component",
|
||||
""
|
||||
].join("\n");
|
||||
if (value.crontab !== expected) {
|
||||
@@ -221,14 +225,15 @@ if (value.crontab !== expected) {
|
||||
const messages = value.logs.map(item => item.message);
|
||||
if (!messages.includes("The cron job removed") ||
|
||||
!messages.includes("The cron job has been created: */30 * * * * /usr/bin/podkop list_update_if_due # list") ||
|
||||
!messages.includes("The subscription cron job has been created: */45 * * * * /usr/bin/podkop subscription_update_if_due # subscription")) {
|
||||
!messages.includes("The subscription cron job has been created: */45 * * * * /usr/bin/podkop subscription_update_if_due # subscription") ||
|
||||
!messages.includes("The component update check cron job has been created: 0 */2 * * * /usr/bin/podkop component_updates_if_due # component")) {
|
||||
console.error("unexpected cron apply logs", JSON.stringify(value.logs));
|
||||
process.exit(1);
|
||||
}
|
||||
JS
|
||||
|
||||
status=0
|
||||
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan-invalid-list.json" "$WORK_DIR/existing.cron" /usr/bin/podkop '# list' '# subscription' >"$WORK_DIR/cron-apply-invalid.json" || status="$?"
|
||||
updates_ucode refresh-cron-fixture "$WORK_DIR/cron-plan-invalid-list.json" "$WORK_DIR/existing.cron" /usr/bin/podkop '# list' '# subscription' '# component' >"$WORK_DIR/cron-apply-invalid.json" || status="$?"
|
||||
assert_eq 1 "$status" "invalid cron apply status"
|
||||
node - "$WORK_DIR/cron-apply-invalid.json" <<'JS'
|
||||
const fs = require("fs");
|
||||
|
||||
Reference in New Issue
Block a user