fix: close High findings #42 + #46 — public status DTO, split-infra resolver (#99)

* fix(api): allowlist public node status fields

* fix(ops): resolve split infrastructure containers exactly

* docs: add C5a mission handoff

---------

Co-authored-by: gadgethd <111318106+gadgethd@users.noreply.github.com>
This commit is contained in:
hermes-gadgetandgadgethd authored and GitHub committed 2026-09-30 23:19:31 +01:00
1 parent 27f1822e75
commit f445a24467
22 files changed
+615 -173

No files matched your search

+33 -19
View File
@@ -5,8 +5,18 @@ umask 077
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
project_dir="$(cd -- "${script_dir}/.." && pwd)"
app_project_name="${COMPOSE_PROJECT_NAME:-meshcore-analytics}"
infra_project_dir="${MESHCORE_INFRA_DIR:-${project_dir}/../meshcore-infra}"
if [ -n "${MESHCORE_INFRA_COMPOSE_FILE:-}" ]; then
infra_compose_file="$MESHCORE_INFRA_COMPOSE_FILE"
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-$(dirname -- "$infra_compose_file")}}"
else
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-${project_dir}/../meshcore-infra}}"
infra_compose_file="$infra_project_dir/docker-compose.yml"
fi
infra_project_name="${MESHCORE_INFRA_PROJECT_NAME:-meshcore-infra}"
export MESHCORE_INFRA_PROJECT_DIR="$infra_project_dir"
export MESHCORE_INFRA_COMPOSE_FILE="$infra_compose_file"
export MESHCORE_INFRA_PROJECT_NAME="$infra_project_name"
source "$script_dir/lib/infra-compose.sh"
output_dir="${BACKUP_OUTPUT_DIR:?BACKUP_OUTPUT_DIR must name an encrypted backup target}"
encryption_cert="${BACKUP_ENCRYPTION_CERT:?BACKUP_ENCRYPTION_CERT must name a public X.509 certificate}"
signing_key="${BACKUP_RECEIPT_SIGNING_KEY:?BACKUP_RECEIPT_SIGNING_KEY must name the offline receipt signing key}"
@@ -29,8 +39,8 @@ for path in "$encryption_cert" "$signing_key"; do
exit 66
}
done
test -f "$infra_project_dir/docker-compose.yml" || {
echo "meshcore-infra compose file is missing: $infra_project_dir/docker-compose.yml" >&2
test -f "$infra_compose_file" || {
echo "infrastructure Compose file is missing: $infra_compose_file" >&2
exit 66
}
openssl x509 -in "$encryption_cert" -noout >/dev/null
@@ -66,30 +76,27 @@ if (( (8#$env_mode & 8#077) != 0 )); then
echo "protected runtime configuration .env must not be group/world accessible" >&2
exit 66
fi
infra_env_file="$infra_project_dir/.env"
if [ ! -f "$infra_env_file" ] || [ -L "$infra_env_file" ]; then
echo "protected infrastructure configuration .env is missing or unsafe" >&2
exit 66
fi
infra_env_mode="$(stat -c '%a' "$infra_env_file")"
if (( (8#$infra_env_mode & 8#077) != 0 )); then
echo "protected infrastructure configuration .env must not be group/world accessible" >&2
exit 66
fi
app_compose=(
docker compose --project-directory "$project_dir"
-f "$project_dir/docker-compose.yml"
-f "$project_dir/docker-compose.live.yml"
--project-name "$app_project_name"
)
infra_compose=(
docker compose --project-directory "$infra_project_dir"
-f "$infra_project_dir/docker-compose.yml"
--project-name "$infra_project_name"
)
"${app_compose[@]}" config -q
"${infra_compose[@]}" config -q
meshcore_infra_compose config -q
declare -A service_container_ids=()
for service in timescaledb redis mosquitto; do
container_id="$("${infra_compose[@]}" ps -q "$service")"
test -n "$container_id" || {
echo "required service is not running: $service" >&2
exit 69
}
if [ "$(docker inspect "$container_id" --format '{{.State.Status}}')" != "running" ]; then
echo "required service is not running: $service" >&2
exit 69
fi
container_id="$(resolve_infra_container "$service")"
service_container_ids["$service"]="$container_id"
done
timescaledb_container="${service_container_ids[timescaledb]}"
@@ -190,8 +197,15 @@ tar -C "$project_dir" -czf "$payload_dir/configuration.tgz" \
logging \
backend/src/db/migrations backend/src/db/schema backend/src/db/owner-auth.sql \
scripts/backup.sh scripts/restore-drill.sh scripts/sync-latest.sh scripts/replace-container.sh \
scripts/lib/infra-compose.sh scripts/unify-networks.sh scripts/relabel-packets-per-chunk.sh \
scripts/bootstrap-mosquitto.sh vacuum-compressed-chunks.sh
tar -C "$infra_project_dir" -czf "$payload_dir/infra-configuration.tgz" \
infra_configuration_stage="$tmp_dir/infra-configuration"
mkdir -p "$infra_configuration_stage"
cp -- "$infra_project_dir/.env" "$infra_configuration_stage/.env"
cp -- "$infra_compose_file" "$infra_configuration_stage/docker-compose.yml"
cp -- "$infra_project_dir/README.md" "$infra_configuration_stage/README.md"
cp -- "$infra_project_dir/ROLLBACK.md" "$infra_configuration_stage/ROLLBACK.md"
tar -C "$infra_configuration_stage" -czf "$payload_dir/infra-configuration.tgz" \
.env docker-compose.yml README.md ROLLBACK.md
completed_at="$(date -u +%Y-%m-%dT%H:%M:%S.000Z)"
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
meshcore_infra_compose() {
local project_name="${MESHCORE_INFRA_PROJECT_NAME:-meshcore-infra}"
local project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-}}"
local compose_file="${MESHCORE_INFRA_COMPOSE_FILE:-}"
if [ -z "$compose_file" ]; then
if [ -z "$project_dir" ]; then
echo 'MESHCORE_INFRA_PROJECT_DIR or MESHCORE_INFRA_COMPOSE_FILE is required' >&2
return 64
fi
compose_file="$project_dir/docker-compose.yml"
fi
if [ -z "$project_dir" ]; then
project_dir="$(dirname -- "$compose_file")"
fi
if [ ! -f "$compose_file" ]; then
echo "infrastructure Compose file is missing: $compose_file" >&2
return 66
fi
docker compose \
--project-directory "$project_dir" \
--project-name "$project_name" \
-f "$compose_file" \
"$@"
}
resolve_infra_container() {
local service="${1:-}"
case "$service" in
timescaledb|redis|mosquitto) ;;
*)
echo "unsupported infrastructure service: ${service:-<empty>}" >&2
return 64
;;
esac
local raw_ids
if ! raw_ids="$(meshcore_infra_compose ps -q "$service")"; then
echo "could not resolve infrastructure service: $service" >&2
return 69
fi
local -a ids=()
local id
while IFS= read -r id; do
id="${id//$'\r'/}"
if [ -n "${id//[[:space:]]/}" ]; then
ids+=("$id")
fi
done <<<"$raw_ids"
if [ "${#ids[@]}" -ne 1 ]; then
echo "expected one infrastructure container for $service; found ${#ids[@]}" >&2
return 69
fi
local state
if ! state="$(docker inspect --format '{{.State.Status}}' "${ids[0]}")"; then
echo "could not inspect infrastructure container for $service" >&2
return 69
fi
if [ "$state" != 'running' ]; then
echo "infrastructure container for $service is not running" >&2
return 69
fi
printf '%s\n' "${ids[0]}"
}
+13 -2
View File
@@ -3,11 +3,22 @@
set -Eeuo pipefail
IFS=$'\n\t'
readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
readonly REPO_DIR="${NEWUSER_REPO_DIR:-/home/ben/ukmesh/meshcore-analytics}"
readonly ENV_FILE="${REPO_DIR}/.env"
readonly MOSQUITTO_CONTAINER="$(docker ps --format '{{.Names}}' | grep -E '^meshcore-infra-mosquitto-1$|mosquitto-1$' | head -1)"
if [ -n "${MESHCORE_INFRA_COMPOSE_FILE:-}" ]; then
infra_compose_file="$MESHCORE_INFRA_COMPOSE_FILE"
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-$(dirname -- "$infra_compose_file")}}"
else
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-$(dirname -- "$REPO_DIR")/meshcore-infra}}"
infra_compose_file="$infra_project_dir/docker-compose.yml"
fi
export MESHCORE_INFRA_PROJECT_DIR="$infra_project_dir"
export MESHCORE_INFRA_COMPOSE_FILE="$infra_compose_file"
source "$SCRIPT_DIR/lib/infra-compose.sh"
readonly MOSQUITTO_CONTAINER="$(resolve_infra_container mosquitto)"
readonly BACKEND_CONTAINER='meshcore-analytics-backend-1'
readonly TIMESCALEDB_CONTAINER="$(docker ps --format '{{.Names}}' | grep -E '^meshcore-infra-timescaledb-1$|timescaledb-1$' | head -1)"
readonly TIMESCALEDB_CONTAINER="$(resolve_infra_container timescaledb)"
readonly OWNER_DATABASE='meshcore_owner_auth'
readonly POSTGRES_USER='meshcore'
readonly BROKER_URL='wss://mqtt.ukmesh.com:443'
+1 -1
View File
@@ -6,7 +6,7 @@
"scripts": {
"generate-key": "tsx generate-observer-key.ts",
"register-observer": "tsx register-observer.ts",
"test": "bash test-generate-observer-key.sh && bash test-newuser.sh",
"test": "bash test-generate-observer-key.sh && bash test-infra-compose.sh && bash test-newuser.sh",
"typecheck": "tsc --noEmit --target ES2022 --module NodeNext --moduleResolution NodeNext --strict --types node generate-observer-key.ts register-observer.ts"
},
"dependencies": {
+15 -1
View File
@@ -3,11 +3,25 @@ set -Eeuo pipefail
cd "$(dirname "$0")/.."
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
project_dir="$(cd -- "$script_dir/.." && pwd)"
if [ -n "${MESHCORE_INFRA_COMPOSE_FILE:-}" ]; then
infra_compose_file="$MESHCORE_INFRA_COMPOSE_FILE"
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-$(dirname -- "$infra_compose_file")}}"
else
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-${project_dir}/../meshcore-infra}}"
infra_compose_file="$infra_project_dir/docker-compose.yml"
fi
export MESHCORE_INFRA_PROJECT_DIR="$infra_project_dir"
export MESHCORE_INFRA_COMPOSE_FILE="$infra_compose_file"
source "$script_dir/lib/infra-compose.sh"
log() {
printf '[%s] %s\n' "$(date -Is)" "$*"
}
PSQL=(docker compose exec -T timescaledb psql -U "${POSTGRES_USER:-meshcore}" -d "${POSTGRES_DB:-meshcore}" -v ON_ERROR_STOP=1 -X)
timescaledb_container="$(resolve_infra_container timescaledb)"
PSQL=(docker exec -i "$timescaledb_container" psql -U "${POSTGRES_USER:-meshcore}" -d "${POSTGRES_DB:-meshcore}" -v ON_ERROR_STOP=1 -X)
# `test` remains an isolated network. This maintenance operation only folds
# historical production labels into the unified UKMesh scope.
LEGACY_SQL="'teesside','northeast'"
+13 -1
View File
@@ -49,6 +49,16 @@ done
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
project_dir="$(cd -- "${script_dir}/.." && pwd)"
project_name="${COMPOSE_PROJECT_NAME:-meshcore-analytics}"
if [ -n "${MESHCORE_INFRA_COMPOSE_FILE:-}" ]; then
infra_compose_file="$MESHCORE_INFRA_COMPOSE_FILE"
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-$(dirname -- "$infra_compose_file")}}"
else
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-${project_dir}/../meshcore-infra}}"
infra_compose_file="$infra_project_dir/docker-compose.yml"
fi
export MESHCORE_INFRA_PROJECT_DIR="$infra_project_dir"
export MESHCORE_INFRA_COMPOSE_FILE="$infra_compose_file"
source "$script_dir/lib/infra-compose.sh"
release_dir="${RELEASE_STATUS_DIR:-/home/ben/meshcore-releases}"
cosign_key="${COSIGN_PUBLIC_KEY:-}"
cosign_identity_regexp="${COSIGN_CERTIFICATE_IDENTITY_REGEXP:-}"
@@ -127,6 +137,8 @@ if ! docker compose --project-name "$project_name" config --services | grep -Fxq
echo "unknown Compose service: $service" >&2
exit 65
fi
meshcore_infra_compose config -q
timescaledb_container="$(resolve_infra_container timescaledb)"
if [ "$(git rev-parse HEAD)" != "$source_revision" ]; then
echo "working checkout does not match the release source revision" >&2
exit 65
@@ -256,7 +268,7 @@ echo "Running the required migration job with the signed backend image..."
BACKEND_IMAGE="$backend_image" \
docker compose --project-name "$project_name" run --rm db-migrate
schema_version="$(
docker compose --project-name "$project_name" exec -T timescaledb \
docker exec -i "$timescaledb_container" \
psql -U "${POSTGRES_USER:-meshcore}" -d "${POSTGRES_DB:-meshcore}" -Atc \
"SELECT COALESCE(MAX(((regexp_match(name, '^([0-9]+)_'))[1])::int), 0) FROM schema_migrations"
)"
+2
View File
@@ -53,6 +53,8 @@ test "$actual_archive_sha" = "$expected_archive_sha" || {
exit 65
}
# This drill uses generated container names on a private network; all restore
# and smoke commands target those disposable containers directly.
run_id="meshcore-restore-$$"
network_name="${run_id}-network"
postgres_name="${run_id}-postgres"
+107
View File
@@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
test_root="$(mktemp -d)"
trap 'rm -rf -- "$test_root"' EXIT
mkdir -p "$test_root/bin" "$test_root/external-infra"
cat >"$test_root/external-infra/compose.production.yml" <<'YAML'
services:
timescaledb:
image: timescale/timescaledb:fixture
redis:
image: redis:fixture
mosquitto:
image: eclipse-mosquitto:fixture
YAML
cat >"$test_root/bin/docker" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' "$*" >>"$MOCK_DOCKER_LOG"
command_name="${1:-}"
shift || true
case "$command_name" in
compose)
while [ "$#" -gt 0 ]; do
case "$1" in
--project-directory|--project-name|-f)
shift 2
;;
config)
exit 0
;;
ps)
shift
test "${1:-}" = '-q'
service="${2:-}"
if [ "${MOCK_PS_OUTPUT+x}" = x ]; then
printf '%s\n' "$MOCK_PS_OUTPUT"
else
printf 'container-%s\n' "$service"
fi
exit 0
;;
*)
echo "unexpected mock Compose command: $*" >&2
exit 90
;;
esac
done
;;
inspect)
if [ "${MOCK_STATE:-running}" = 'running' ]; then
printf 'running\n'
else
printf '%s\n' "$MOCK_STATE"
fi
;;
*)
echo "unexpected mock Docker command: $command_name $*" >&2
exit 91
;;
esac
EOF
chmod 0755 "$test_root/bin/docker"
export PATH="$test_root/bin:$PATH"
export MOCK_DOCKER_LOG="$test_root/docker.log"
export MESHCORE_INFRA_PROJECT_DIR="$test_root/external-infra"
export MESHCORE_INFRA_COMPOSE_FILE="$test_root/external-infra/compose.production.yml"
export MESHCORE_INFRA_PROJECT_NAME='meshcore-infra-test'
source "$script_dir/lib/infra-compose.sh"
meshcore_infra_compose config -q
for service in timescaledb redis mosquitto; do
resolved="$(resolve_infra_container "$service")"
test "$resolved" = "container-$service"
grep -Fq -- \
"compose --project-directory $MESHCORE_INFRA_PROJECT_DIR --project-name $MESHCORE_INFRA_PROJECT_NAME -f $MESHCORE_INFRA_COMPOSE_FILE ps -q $service" \
"$MOCK_DOCKER_LOG"
done
unset MESHCORE_INFRA_PROJECT_DIR MESHCORE_INFRA_DIR
test "$(resolve_infra_container timescaledb)" = 'container-timescaledb'
grep -Fq -- \
"compose --project-directory $(dirname -- "$MESHCORE_INFRA_COMPOSE_FILE") --project-name $MESHCORE_INFRA_PROJECT_NAME -f $MESHCORE_INFRA_COMPOSE_FILE ps -q timescaledb" \
"$MOCK_DOCKER_LOG"
if MOCK_PS_OUTPUT=$'container-one\ncontainer-two' resolve_infra_container timescaledb >/dev/null 2>&1; then
echo 'ambiguous service resolution unexpectedly succeeded' >&2
exit 1
fi
if MOCK_PS_OUTPUT='' resolve_infra_container redis >/dev/null 2>&1; then
echo 'missing service resolution unexpectedly succeeded' >&2
exit 1
fi
if MOCK_STATE=exited resolve_infra_container mosquitto >/dev/null 2>&1; then
echo 'stopped service resolution unexpectedly succeeded' >&2
exit 1
fi
if resolve_infra_container backend >/dev/null 2>&1; then
echo 'unsupported service resolution unexpectedly succeeded' >&2
exit 1
fi
printf 'split infrastructure resolver dry-run tests passed\n'
+13 -4
View File
@@ -9,7 +9,8 @@ readonly NODE_C='C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C
test_root="$(mktemp -d)"
trap 'rm -rf -- "$test_root"' EXIT
mkdir -p "$test_root/bin" "$test_root/repo"
mkdir -p "$test_root/bin" "$test_root/repo" "$test_root/infra"
touch "$test_root/infra/docker-compose.yml"
printf 'OWNER_MQTT_USERNAME_MAP=alice=%s,bob=%s\n' "$NODE_A" "$NODE_C" \
>"$test_root/repo/.env"
@@ -20,9 +21,6 @@ set -Eeuo pipefail
printf 'ARGS %s\n' "$*" >>"$FAKE_DOCKER_LOG"
case ${1:-} in
ps)
printf '%s\n' meshcore-infra-mosquitto-1 meshcore-infra-timescaledb-1
;;
inspect)
if [[ $* == *State.Running* ]]; then
printf 'true\n'
@@ -31,6 +29,12 @@ case ${1:-} in
fi
;;
compose)
case "$*" in
*'ps -q mosquitto') printf 'infra-mosquitto-id\n' ;;
*'ps -q timescaledb') printf 'infra-timescaledb-id\n' ;;
*' up -d --no-deps backend') ;;
*) printf 'unexpected docker compose invocation: %s\n' "$*" >&2; exit 1 ;;
esac
;;
exec)
sql=''
@@ -60,6 +64,7 @@ export FAKE_DOCKER_LOG="$test_root/docker.log"
output="$test_root/output.log"
PATH="$test_root/bin:$PATH" \
NEWUSER_REPO_DIR="$test_root/repo" \
MESHCORE_INFRA_PROJECT_DIR="$test_root/infra" \
TMPDIR="$test_root" \
bash "$SCRIPT_DIR/newuser.sh" --link alice "$NODE_B" >"$output" 2>&1
@@ -70,6 +75,10 @@ grep -Fq 'Password: unchanged (existing credential)' "$output"
grep -Fq 'INSERT INTO owner_account_nodes' "$FAKE_DOCKER_LOG"
grep -Fq 'compose -f docker-compose.yml -f docker-compose.live.yml up -d --no-deps backend' \
"$FAKE_DOCKER_LOG"
grep -Fq "compose --project-directory $test_root/infra --project-name meshcore-infra -f $test_root/infra/docker-compose.yml ps -q mosquitto" \
"$FAKE_DOCKER_LOG"
grep -Fq "compose --project-directory $test_root/infra --project-name meshcore-infra -f $test_root/infra/docker-compose.yml ps -q timescaledb" \
"$FAKE_DOCKER_LOG"
if grep -Fq 'mosquitto_passwd -b' "$FAKE_DOCKER_LOG"; then
printf 'link flow unexpectedly changed the existing credential\n' >&2
exit 1
+76 -41
View File
@@ -61,46 +61,64 @@ command_name="${1:-}"
shift || true
case "$command_name" in
compose)
if [ "${1:-}" = "--project-name" ]; then
shift 2
fi
case "${1:-}" in
config)
if [ "${2:-}" = "--services" ]; then
printf '%s\n' backend db-migrate timescaledb
elif [ "${2:-}" != "-q" ]; then
printf 'services: {}\n'
fi
;;
ps)
printf 'current-backend\n'
;;
run)
test "${*: -1}" = "db-migrate"
;;
exec)
if [[ " $* " == *" timescaledb "* ]]; then
printf '30\n'
elif [[ "$*" == *"/readyz"* ]]; then
printf '{"status":"ready"}\n'
elif [[ "$*" == *"/metrics"* ]]; then
printf 'meshcore_process_start_time_seconds 1\n'
else
printf 'unexpected docker compose exec invocation: %s\n' "$*" >&2
exit 91
fi
;;
up)
if [ "${MOCK_FAIL_DESIRED_DEPLOY:-false}" = "true" ] \
&& [ "${BACKEND_IMAGE:-}" = "$MOCK_DESIRED_IMAGE" ]; then
exit 1
fi
;;
*)
printf 'unexpected docker compose invocation: %s\n' "$*" >&2
exit 92
;;
esac
project_name=''
while [ "$#" -gt 0 ]; do
case "$1" in
--project-name)
project_name="$2"
shift 2
;;
--project-directory|-f)
shift 2
;;
config)
if [ "${2:-}" = "--services" ]; then
printf '%s\n' backend db-migrate
elif [ "${2:-}" != "-q" ]; then
printf 'services: {}\n'
fi
exit 0
;;
ps)
shift
test "${1:-}" = '-q'
service="${2:-}"
if [ "$project_name" = 'meshcore-infra' ]; then
test "$service" = 'timescaledb'
printf 'infra-timescaledb\n'
else
printf 'current-backend\n'
fi
exit 0
;;
run)
test "${*: -1}" = "db-migrate"
exit 0
;;
exec)
if [[ "$*" == *"/readyz"* ]]; then
printf '{"status":"ready"}\n'
elif [[ "$*" == *"/metrics"* ]]; then
printf 'meshcore_process_start_time_seconds 1\n'
else
printf 'unexpected docker compose exec invocation: %s\n' "$*" >&2
exit 91
fi
exit 0
;;
up)
if [ "${MOCK_FAIL_DESIRED_DEPLOY:-false}" = "true" ] \
&& [ "${BACKEND_IMAGE:-}" = "$MOCK_DESIRED_IMAGE" ]; then
exit 1
fi
exit 0
;;
*)
printf 'unexpected docker compose invocation: %s\n' "$*" >&2
exit 92
;;
esac
done
;;
pull)
;;
@@ -124,6 +142,9 @@ case "$command_name" in
*'.State.Running'*)
printf 'true\n'
;;
*'.State.Status'*)
printf 'running\n'
;;
*'.State.Health'*)
printf 'healthy\n'
;;
@@ -137,6 +158,10 @@ case "$command_name" in
printf 'compat-container-id\n'
;;
exec)
if [[ "$*" == *"infra-timescaledb"*"psql"* ]]; then
printf '30\n'
exit 0
fi
if [ "${MOCK_COMPAT_READY:-true}" != "true" ]; then
exit 1
fi
@@ -178,12 +203,15 @@ run_case() {
trap 'rm -rf -- "$test_root"' RETURN
mkdir -p \
"${test_root}/project/scripts" \
"${test_root}/project/scripts/lib" \
"${test_root}/infra" \
"${test_root}/fake-bin" \
"${test_root}/releases"
cp "$replace_script" "${test_root}/project/scripts/replace-container.sh"
cp "${script_dir}/lib/infra-compose.sh" "${test_root}/project/scripts/lib/infra-compose.sh"
chmod 0755 "${test_root}/project/scripts/replace-container.sh"
printf 'services: {}\n' >"${test_root}/project/docker-compose.yml"
printf 'services: {}\n' >"${test_root}/infra/docker-compose.yml"
local now
now="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
@@ -228,6 +256,7 @@ run_case() {
RESTORE_RECEIPT_PATH="${test_root}/restore-receipt.json" \
RESTORE_RECEIPT_VERIFY_KEY="${test_root}/receipt-verify.pem" \
RELEASE_STATUS_DIR="${test_root}/releases" \
MESHCORE_INFRA_PROJECT_DIR="${test_root}/infra" \
COMPATIBILITY_TIMEOUT_SECONDS=1 \
"${test_root}/project/scripts/replace-container.sh" \
backend \
@@ -252,6 +281,12 @@ run_case() {
test "$(jq -r '.status' "$release_status")" = "$expected_status"
test "$(jq -r '.schema_version' "$release_status")" = "30"
test "$(jq -r '.prior_image' "$release_status")" = "$prior_image"
local resolver_line migration_line
resolver_line="$(grep -nF 'ps -q timescaledb' "${test_root}/docker.log" | cut -d: -f1)"
migration_line="$(grep -nF 'compose --project-name meshcore-analytics run --rm db-migrate' "${test_root}/docker.log" | cut -d: -f1)"
test "$resolver_line" -lt "$migration_line"
grep -Fq 'exec -i infra-timescaledb psql -U meshcore -d meshcore -Atc' \
"${test_root}/docker.log"
local up_count
up_count="$(
+16 -2
View File
@@ -1,10 +1,24 @@
#!/usr/bin/env bash
set -Eeuo pipefail
cd "$(dirname "$0")/.."
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
project_dir="$(cd -- "$script_dir/.." && pwd)"
cd "$project_dir"
if [ -n "${MESHCORE_INFRA_COMPOSE_FILE:-}" ]; then
infra_compose_file="$MESHCORE_INFRA_COMPOSE_FILE"
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-$(dirname -- "$infra_compose_file")}}"
else
infra_project_dir="${MESHCORE_INFRA_PROJECT_DIR:-${MESHCORE_INFRA_DIR:-${project_dir}/../meshcore-infra}}"
infra_compose_file="$infra_project_dir/docker-compose.yml"
fi
export MESHCORE_INFRA_PROJECT_DIR="$infra_project_dir"
export MESHCORE_INFRA_COMPOSE_FILE="$infra_compose_file"
source "$script_dir/lib/infra-compose.sh"
MODE="${1:-audit}"
PSQL=(docker compose exec -T timescaledb psql -U "${POSTGRES_USER:-meshcore}" -d "${POSTGRES_DB:-meshcore}" -v ON_ERROR_STOP=1 -X)
timescaledb_container="$(resolve_infra_container timescaledb)"
PSQL=(docker exec -i "$timescaledb_container" psql -U "${POSTGRES_USER:-meshcore}" -d "${POSTGRES_DB:-meshcore}" -v ON_ERROR_STOP=1 -X)
LEGACY_SQL="'teesside','northeast'"
RUN_ID="${NETWORK_UNIFICATION_RUN_ID:-network-unification-$(date -u +%Y%m%dT%H%M%SZ)}"