207 lines
7.4 KiB
Bash
207 lines
7.4 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
INSTALLER_SOURCE="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
TEST_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/ours-rp-installer-env-contract.XXXXXX")"
|
|
|
|
cleanup() {
|
|
rm -rf "$TEST_ROOT"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
fail() {
|
|
printf 'FAIL: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
assert_equals() {
|
|
local expected="$1"
|
|
local actual="$2"
|
|
local message="$3"
|
|
[[ "$actual" == "$expected" ]] || fail "$message (expected=$expected actual=$actual)"
|
|
}
|
|
|
|
assert_not_contains() {
|
|
local needle="$1"
|
|
local path="$2"
|
|
! grep -Fq "$needle" "$path" || fail "unexpected value in $path: $needle"
|
|
}
|
|
|
|
expect_exit() {
|
|
local expected_status="$1"
|
|
local output_path="$2"
|
|
shift 2
|
|
local actual_status
|
|
|
|
set +e
|
|
"$@" >"$output_path" 2>&1
|
|
actual_status=$?
|
|
set -e
|
|
[[ "$actual_status" == "$expected_status" ]] || {
|
|
cat "$output_path" >&2 || true
|
|
fail "unexpected exit status (expected=$expected_status actual=$actual_status)"
|
|
}
|
|
}
|
|
|
|
host_arch() {
|
|
case "$(uname -m)" in
|
|
x86_64) printf 'amd64\n' ;;
|
|
aarch64) printf 'arm64\n' ;;
|
|
*) fail "unsupported test host architecture: $(uname -m)" ;;
|
|
esac
|
|
}
|
|
|
|
env_value() {
|
|
local env_path="$1"
|
|
local key="$2"
|
|
(
|
|
# shellcheck disable=SC1090
|
|
source "$INSTALLER_SOURCE/scripts/common.sh"
|
|
env_file_value "$env_path" "$key"
|
|
)
|
|
}
|
|
|
|
create_fixture() {
|
|
local name="$1"
|
|
local fixture="$TEST_ROOT/$name"
|
|
local arch
|
|
arch="$(host_arch)"
|
|
|
|
mkdir -p "$fixture"
|
|
cp -a "$INSTALLER_SOURCE"/. "$fixture"/
|
|
rm -rf "$fixture/tests"
|
|
mkdir -p "$fixture/images" "$fixture/fake-bin"
|
|
sed -i \
|
|
-e "s|__PACKAGE_ARCH__|$arch|g" \
|
|
-e "s|__PACKAGE_PLATFORM__|linux/$arch|g" \
|
|
-e "s|__RUNTIME_IMAGE__|ours-rp-runtime-$arch:new|g" \
|
|
-e "s|__METRICS_IMAGE__|ours-rp-metrics-$arch:new|g" \
|
|
-e "s|__HOST_DATA_DIR__|$fixture/data|g" \
|
|
"$fixture/.env.example"
|
|
cat > "$fixture/PACKAGE-MANIFEST.env" <<EOF
|
|
PACKAGE_ARCH=$arch
|
|
PACKAGE_PLATFORM=linux/$arch
|
|
EOF
|
|
cat > "$fixture/fake-bin/docker" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
printf '%s\n' "$*" >> "$FAKE_DOCKER_LOG"
|
|
if [[ "${1:-}" == "compose" && "${2:-}" == "version" ]]; then
|
|
exit 0
|
|
fi
|
|
exit 99
|
|
EOF
|
|
chmod +x "$fixture/fake-bin/docker"
|
|
for command in jq rsync curl gzip tar; do
|
|
cat > "$fixture/fake-bin/$command" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
exit 0
|
|
EOF
|
|
chmod +x "$fixture/fake-bin/$command"
|
|
done
|
|
printf '%s\n' "$fixture"
|
|
}
|
|
|
|
run_upgrade() {
|
|
local fixture="$1"
|
|
local old_env="$2"
|
|
shift 2
|
|
(
|
|
cd "$fixture"
|
|
export PATH="$fixture/fake-bin:$PATH"
|
|
export FAKE_DOCKER_LOG="$fixture/fake-docker.log"
|
|
"$@" ./scripts/upgrade.sh --reuse-env-from "$old_env"
|
|
)
|
|
}
|
|
|
|
run_upgrade_without_reuse() {
|
|
local fixture="$1"
|
|
(
|
|
cd "$fixture"
|
|
export PATH="$fixture/fake-bin:$PATH"
|
|
export FAKE_DOCKER_LOG="$fixture/fake-docker.log"
|
|
./scripts/upgrade.sh
|
|
)
|
|
}
|
|
|
|
new_fixture_with_old_env() {
|
|
local name="$1"
|
|
local fixture
|
|
fixture="$(create_fixture "$name")"
|
|
cat > "$fixture/old.env" <<'EOF'
|
|
INTERVAL_SECS=120
|
|
RPKI_IMAGE=ours-rp-runtime-old:old
|
|
REMOVED_LEGACY_KEY=legacy-value
|
|
EOF
|
|
printf '%s\n' "$fixture"
|
|
}
|
|
|
|
fixture="$(create_fixture template-defaults)"
|
|
expect_exit 99 "$fixture/template.log" run_upgrade_without_reuse "$fixture"
|
|
assert_equals "600" "$(env_value "$fixture/.env" INTERVAL_SECS)" "template default must be retained without reuse"
|
|
|
|
fixture="$(new_fixture_with_old_env default-old)"
|
|
expect_exit 99 "$fixture/default.log" run_upgrade "$fixture" "$fixture/old.env"
|
|
assert_equals "120" "$(env_value "$fixture/.env" INTERVAL_SECS)" "old value must override template default"
|
|
assert_equals "ours-rp-runtime-$(host_arch):new" "$(env_value "$fixture/.env" RPKI_IMAGE)" "standard upgrade must retain package image"
|
|
assert_equals "10" "$(env_value "$fixture/.env" METRICS_POLL_SECS)" "template default must fill a key absent from old environment"
|
|
! grep -q '^REMOVED_LEGACY_KEY=' "$fixture/.env" || fail "removed old key was copied into target environment"
|
|
grep -q 'reused_keys=INTERVAL_SECS' "$fixture/default.log" || fail "reused key summary missing"
|
|
grep -q 'ignored_removed_old_keys=REMOVED_LEGACY_KEY' "$fixture/default.log" || fail "removed key summary missing"
|
|
grep -q 'template_only_keys=' "$fixture/default.log" || fail "template key summary missing"
|
|
|
|
fixture="$(new_fixture_with_old_env invocation-overrides)"
|
|
expect_exit 99 "$fixture/override.log" run_upgrade "$fixture" "$fixture/old.env" env INTERVAL_SECS=300 GRAFANA_ADMIN_PASSWORD='secret with space'
|
|
assert_equals "300" "$(env_value "$fixture/.env" INTERVAL_SECS)" "invocation value must override old value"
|
|
assert_equals "secret with space" "$(env_value "$fixture/.env" GRAFANA_ADMIN_PASSWORD)" "invocation value must preserve shell-sensitive content"
|
|
grep -q 'command_override_keys=INTERVAL_SECS,GRAFANA_ADMIN_PASSWORD' "$fixture/override.log" || fail "override key summary missing"
|
|
assert_not_contains 'secret with space' "$fixture/override.log"
|
|
|
|
fixture="$(new_fixture_with_old_env invocation-special-characters)"
|
|
special_password="O'Reilly"
|
|
special_password+=' $literal `tick` slash\'
|
|
expect_exit 99 "$fixture/special.log" run_upgrade "$fixture" "$fixture/old.env" env GRAFANA_ADMIN_PASSWORD="$special_password"
|
|
assert_equals "$special_password" "$(env_value "$fixture/.env" GRAFANA_ADMIN_PASSWORD)" "invocation value with dotenv-sensitive characters must round-trip"
|
|
assert_not_contains "$special_password" "$fixture/special.log"
|
|
|
|
fixture="$(create_fixture existing-target-env)"
|
|
cp "$fixture/.env.example" "$fixture/.env"
|
|
sed -i 's/^INTERVAL_SECS=.*/INTERVAL_SECS=180/' "$fixture/.env"
|
|
cat > "$fixture/old.env" <<'EOF'
|
|
INTERVAL_SECS=120
|
|
EOF
|
|
expect_exit 99 "$fixture/existing.log" run_upgrade "$fixture" "$fixture/old.env" env INTERVAL_SECS=300
|
|
assert_equals "300" "$(env_value "$fixture/.env" INTERVAL_SECS)" "invocation value must override an existing target environment"
|
|
|
|
fixture="$(new_fixture_with_old_env explicit-empty)"
|
|
expect_exit 1 "$fixture/empty.log" run_upgrade "$fixture" "$fixture/old.env" env INTERVAL_SECS=
|
|
[[ ! -s "$fixture/fake-docker.log" ]] || fail "explicit empty value reached Docker"
|
|
grep -q 'INTERVAL_SECS' "$fixture/empty.log" || fail "empty-value error did not identify key"
|
|
|
|
fixture="$(create_fixture empty-old-value)"
|
|
cat > "$fixture/old.env" <<'EOF'
|
|
INTERVAL_SECS=
|
|
EOF
|
|
expect_exit 1 "$fixture/old-empty.log" run_upgrade "$fixture" "$fixture/old.env"
|
|
[[ ! -s "$fixture/fake-docker.log" ]] || fail "empty reused value reached Docker"
|
|
grep -q 'INTERVAL_SECS' "$fixture/old-empty.log" || fail "empty reused value error did not identify key"
|
|
|
|
fixture="$(create_fixture empty-template)"
|
|
sed -i 's/^INTERVAL_SECS=.*/INTERVAL_SECS=/' "$fixture/.env.example"
|
|
cat > "$fixture/old.env" <<'EOF'
|
|
INTERVAL_SECS=120
|
|
EOF
|
|
expect_exit 1 "$fixture/template-empty.log" run_upgrade "$fixture" "$fixture/old.env"
|
|
[[ ! -s "$fixture/fake-docker.log" ]] || fail "empty template default reached Docker"
|
|
grep -q 'INTERVAL_SECS' "$fixture/template-empty.log" || fail "empty template error did not identify key"
|
|
|
|
fixture="$(new_fixture_with_old_env package-guard)"
|
|
arch="$(host_arch)"
|
|
other_arch="amd64"
|
|
[[ "$arch" == "amd64" ]] && other_arch="arm64"
|
|
expect_exit 1 "$fixture/arch.log" run_upgrade "$fixture" "$fixture/old.env" env PACKAGE_ARCH="$other_arch"
|
|
[[ ! -s "$fixture/fake-docker.log" ]] || fail "package architecture mismatch reached Docker"
|
|
grep -q 'mismatches manifest PACKAGE_ARCH' "$fixture/arch.log" || fail "package architecture guard did not reject override"
|
|
|
|
printf 'PASS: Docker installer upgrade environment contract\n'
|