fix(deploy): fail closed on postgres password mismatch

This commit is contained in:
root committed 2026-09-05 14:57:10 +00:00
1 parent d140a47f23
commit aea8cd7d60
8 files changed
+318 -21

No files matched your search

+9
View File
@@ -26,3 +26,12 @@ S node deployment.
The installer must stop on missing prerequisites, invalid public address,
occupied ports, failed image verification, failed schema migration, or failed
health checks. It must never continue with a partially initialized database.
PostgreSQL initialization and retries use the fixed application roles
`product_e` and `product_s`. Credentials are generated once and then read back
from `production.env`; a password mismatch is fail-closed. The installer never
logs in as `postgres`, changes a role password, removes volumes by default, or
silently continues after a database error. E's failed volume can only be
removed with `OVERCOOKED_RESET_FAILED_INSTALL=1` plus
`OVERCOOKED_CONFIRM_NO_BUSINESS_DATA=YES`; S database mismatches always require
manual recovery.
+10
View File
@@ -83,6 +83,16 @@ image. S uses only the schema migration assets published in this repository.
If either migration fails, the installer exits non-zero and does not start the
corresponding application service.
The fixed PostgreSQL roles are `product_e` and `product_s`; the installer never
uses a `postgres` superuser login. A retry reuses the passwords in the existing
mode-600 `production.env` and keeps named volumes. If a role password cannot be
authenticated, it stops with
`DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY` and does not delete data.
Only a failed E database volume may be reset, and only when both
`OVERCOOKED_RESET_FAILED_INSTALL=1` and
`OVERCOOKED_CONFIRM_NO_BUSINESS_DATA=YES` are explicitly supplied. No
`down -v` or broad volume cleanup is performed.
The installer also exports the approved Media Node image into
`/opt/overcooked/node-images/media-node.tar.gz`; S uses that archive when a
customer adds a node without a source checkout.
+5 -1
View File
@@ -1,6 +1,10 @@
3776581f5b8755afe0fbc3ea9121416504f117da83f2baa6214c315d2d473ef8 install.sh
e895daeae728dfc2a54bda1484847ee90465879c981b17b1ab2cfdebcacf2439 install.sh
e8cabc5a989d171206361e6ae8b4e0baf9bb9a611ac41106bbe42de0782e8975 compose/s.yml
203b6da918dee21dbfabbcf5466111bd92c7a8c9e754dad61e7e75e20513b880 compose/e.yml
c226c5e1feb5cd6948ee9b32897e77240e21793090d8f42234ca999450ec90b0 config/e-server.production.yaml
3e3dc775017577fad3b36b6c21036dfcb6e530793de4c18c825dda888f62e4e3 s-migrate.sh
07f432c281ba90565cd376b42967909b6ff2d19d04454a3ba4e3b184117d45af s-migrations.tar.gz
7845f54d517588953d91f7fbef8be26faccc4dc51b81b6d5fb2d699cd5e86bda test/static-check.sh
7fadbf1f62d2f972a5ae697daf75ff711e6d6bae257f45d0ed9928c9e79e4948 test/database-credentials.sh
e20cc9a6210525b6335b70a1567e35cae01054b1558d17f7593598e672574bda test/fake-docker.sh
306c6ca7407560340797866e077e053627ad409277d1b9da58106fce4cf717cb test/fake-curl.sh
+106 -19
View File
@@ -31,6 +31,8 @@ E_PLAYBACK_TICKET_SECRET=not-generated
E_PLAYBACK_LEASE_KEY=not-generated
E_STORAGE_CREDENTIAL_KEY=not-generated
E_PLUGIN_SECRET_KEY=not-generated
RESET_FAILED_INSTALL=${OVERCOOKED_RESET_FAILED_INSTALL:-0}
CONFIRM_NO_BUSINESS_DATA=${OVERCOOKED_CONFIRM_NO_BUSINESS_DATA:-}
die() { echo "overcooked-install: $*" >&2; exit 1; }
@@ -232,16 +234,46 @@ chown 1000:10001 "$CONTROL_PLANE_DIR/client.key"
chmod 755 "$CONTROL_PLANE_DIR"
secret() { openssl rand -hex 32; }
S_POSTGRES_PASSWORD=$(secret); S_VALKEY_PASSWORD=$(secret)
S_INTERNAL_SHARED_SECRET=$(secret); S_ACCESS_TOKEN_SECRET=$(secret); S_SETUP_TOKEN=$(secret)
E_POSTGRES_PASSWORD=$(secret); E_PLAYBACK_TICKET_SECRET=$(secret); E_PLAYBACK_LEASE_KEY=$(secret)
E_STORAGE_CREDENTIAL_KEY=$(secret); E_PLUGIN_SECRET_KEY=$(secret)
# Re-runs must reuse credentials that initialized existing database volumes.
read_existing_secret() {
secret_name=$1
secret_count=$(awk -v key="$secret_name" 'index($0, key "=") == 1 { count++ } END { print count + 0 }' "$ENV_FILE")
[ "$secret_count" = 1 ] || { echo "production.env 中缺少或重复的 $secret_name" >&2; return 1; }
secret_value=$(awk -v key="$secret_name" 'index($0, key "=") == 1 { print substr($0, length(key) + 2); exit }' "$ENV_FILE")
case "$secret_value" in ''|*[!0-9A-Fa-f]*) echo "production.env 中的 $secret_name 不是安全的 hex 值" >&2; return 1 ;; esac
[ "${#secret_value}" = 64 ] || { echo "production.env 中的 $secret_name 长度无效" >&2; return 1; }
printf '%s\n' "$secret_value"
}
read_existing_secret_alias() {
primary_name=$1; legacy_name=$2
primary_count=$(awk -v key="$primary_name" 'index($0, key "=") == 1 { count++ } END { print count + 0 }' "$ENV_FILE")
legacy_count=$(awk -v key="$legacy_name" 'index($0, key "=") == 1 { count++ } END { print count + 0 }' "$ENV_FILE")
if [ "$primary_count" = 1 ] && [ "$legacy_count" = 0 ]; then
read_existing_secret "$primary_name"
elif [ "$primary_count" = 0 ] && [ "$legacy_count" = 1 ]; then
read_existing_secret "$legacy_name"
else
echo "production.env 中的 $primary_name/$legacy_name 缺少、重复或同时存在" >&2
return 1
fi
}
if [ -f "$ENV_FILE" ]; then
for name in S_POSTGRES_PASSWORD S_VALKEY_PASSWORD S_INTERNAL_SHARED_SECRET S_ACCESS_TOKEN_SECRET S_SETUP_TOKEN E_POSTGRES_PASSWORD E_PLAYBACK_TICKET_SECRET E_PLAYBACK_LEASE_KEY E_STORAGE_CREDENTIAL_KEY E_PLUGIN_SECRET_KEY; do
value=$(sed -n "s/^${name}=//p" "$ENV_FILE" | head -n 1)
[ -n "$value" ] && eval "$name=\"$value\""
done
# Re-runs must reuse every generated credential that initialized existing
# volumes. Do not execute arbitrary production.env content.
S_POSTGRES_PASSWORD=$(read_existing_secret S_POSTGRES_PASSWORD) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 S_POSTGRES_PASSWORD"
S_VALKEY_PASSWORD=$(read_existing_secret S_VALKEY_PASSWORD) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 S_VALKEY_PASSWORD"
S_INTERNAL_SHARED_SECRET=$(read_existing_secret S_INTERNAL_SHARED_SECRET) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 S_INTERNAL_SHARED_SECRET"
S_ACCESS_TOKEN_SECRET=$(read_existing_secret S_ACCESS_TOKEN_SECRET) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 S_ACCESS_TOKEN_SECRET"
S_SETUP_TOKEN=$(read_existing_secret S_SETUP_TOKEN) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 S_SETUP_TOKEN"
E_POSTGRES_PASSWORD=$(read_existing_secret_alias PRODUCT_E_POSTGRES_PASSWORD E_POSTGRES_PASSWORD) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 Product E 数据库密码"
E_PLAYBACK_TICKET_SECRET=$(read_existing_secret_alias PRODUCT_E_PLAYBACK_TICKET_SECRET E_PLAYBACK_TICKET_SECRET) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 Product E 播放票据密钥"
E_PLAYBACK_LEASE_KEY=$(read_existing_secret_alias PRODUCT_E_PLAYBACK_LEASE_KEY E_PLAYBACK_LEASE_KEY) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 Product E 租约密钥"
E_STORAGE_CREDENTIAL_KEY=$(read_existing_secret_alias PRODUCT_E_STORAGE_CREDENTIAL_KEY E_STORAGE_CREDENTIAL_KEY) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 Product E 存储凭据密钥"
E_PLUGIN_SECRET_KEY=$(read_existing_secret_alias PRODUCT_E_PLUGIN_SECRET_KEY E_PLUGIN_SECRET_KEY) || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法安全读取 Product E 插件密钥"
else
S_POSTGRES_PASSWORD=$(secret); S_VALKEY_PASSWORD=$(secret)
S_INTERNAL_SHARED_SECRET=$(secret); S_ACCESS_TOKEN_SECRET=$(secret); S_SETUP_TOKEN=$(secret)
E_POSTGRES_PASSWORD=$(secret); E_PLAYBACK_TICKET_SECRET=$(secret); E_PLAYBACK_LEASE_KEY=$(secret)
E_STORAGE_CREDENTIAL_KEY=$(secret); E_PLUGIN_SECRET_KEY=$(secret)
fi
INSTANCE_SUFFIX=$(hostname 2>/dev/null | tr -cd '[:alnum:]' | cut -c1-24); [ -n "$INSTANCE_SUFFIX" ] || INSTANCE_SUFFIX=customer
@@ -363,10 +395,14 @@ chmod 644 "$ROOT/node-images/media-node.tar.gz"
wait_for_compose_health() {
compose_file=$1; project=$2; service=$3; attempt=1
while [ "$attempt" -le 60 ]; do
container=$(docker compose --env-file "$ENV_FILE" -f "$compose_file" -p "$project" ps -q "$service" 2>/dev/null || true)
if ! container=$(docker compose --env-file "$ENV_FILE" -f "$compose_file" -p "$project" ps -q "$service" 2>/dev/null); then
die "$service 容器状态查询失败"
fi
status=''
if [ -n "$container" ]; then
status=$(docker inspect -f '{{.State.Health.Status}}' "$container" 2>/dev/null || true)
if ! status=$(docker inspect -f '{{.State.Health.Status}}' "$container" 2>/dev/null); then
die "$service 容器健康状态查询失败"
fi
fi
[ "$status" = healthy ] && return 0
[ "$status" = unhealthy ] && die "$service 容器健康检查失败"
@@ -376,15 +412,66 @@ wait_for_compose_health() {
die "$service 容器健康检查超时"
}
docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" up -d postgres valkey
# A previous interrupted install may have initialized the volume with a
# different generated role password. Reconcile it through the local Postgres
# socket before running migrations; no data is dropped.
docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" exec -T postgres \
psql -U postgres -d postgres -v ON_ERROR_STOP=1 \
-c "ALTER ROLE product_e WITH PASSWORD '$E_POSTGRES_PASSWORD';" >/dev/null
reset_e_postgres_volume() {
compose_file=$1; project=$2
database_label=E; expected_volume=e-postgres
[ "$RESET_FAILED_INSTALL" = 1 ] && [ "$CONFIRM_NO_BUSINESS_DATA" = YES ] || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: $database_label 数据库密码不匹配;如确认卷中无业务数据,请同时设置 OVERCOOKED_RESET_FAILED_INSTALL=1 OVERCOOKED_CONFIRM_NO_BUSINESS_DATA=YES"
container=$(docker compose --env-file "$ENV_FILE" -f "$compose_file" -p "$project" ps -q postgres 2>/dev/null || true)
[ -n "$container" ] || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 找不到 $database_label postgres 容器"
volume=$(docker inspect -f '{{range .Mounts}}{{if eq .Destination "/var/lib/postgresql/data"}}{{.Name}}{{end}}{{end}}' "$container" 2>/dev/null || true)
case "$volume" in ''|*[!A-Za-z0-9_.-]*) die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法确认 $database_label 数据卷" ;; esac
volume_project=$(docker volume inspect -f '{{index .Labels "com.docker.compose.project"}}' "$volume" 2>/dev/null || true)
volume_service=$(docker volume inspect -f '{{index .Labels "com.docker.compose.volume"}}' "$volume" 2>/dev/null || true)
[ "$volume_project" = "$project" ] && [ "$volume_service" = "$expected_volume" ] || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: $database_label 数据卷标签不匹配,拒绝删除"
echo "已收到显式无业务数据确认,将删除失败安装的 $database_label PostgreSQL 卷:$volume" >&2
docker compose --env-file "$ENV_FILE" -f "$compose_file" -p "$project" rm -sf postgres
docker volume rm "$volume" >/dev/null || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 无法删除 $database_label 数据卷 $volume"
}
check_e_database_role() {
database_output=''
if database_output=$(printf 'postgres:5432:product_e:product_e:%s\n' "$E_POSTGRES_PASSWORD" | docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" exec -T postgres sh -ceu 'umask 077; cat > /tmp/.overcooked-pgpass; PGPASSFILE=/tmp/.overcooked-pgpass psql -U product_e -d product_e -v ON_ERROR_STOP=1 -Atqc "SELECT current_user"; status=$?; rm -f /tmp/.overcooked-pgpass; exit "$status"' 2>&1); then
[ "$database_output" = product_e ] || { echo "$database_output" | redact_logs >&2; return 1; }
return 0
fi
echo "$database_output" | redact_logs >&2
return 1
}
check_s_database_role() {
database_output=''
if database_output=$(printf 'postgres:5432:product_s:product_s:%s\n' "$S_POSTGRES_PASSWORD" | docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" exec -T postgres sh -ceu 'umask 077; cat > /tmp/.overcooked-pgpass; PGPASSFILE=/tmp/.overcooked-pgpass psql -U product_s -d product_s -v ON_ERROR_STOP=1 -Atqc "SELECT current_user"; status=$?; rm -f /tmp/.overcooked-pgpass; exit "$status"' 2>&1); then
[ "$database_output" = product_s ] || { echo "$database_output" | redact_logs >&2; return 1; }
return 0
fi
echo "$database_output" | redact_logs >&2
return 1
}
prepare_e_database() {
docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" up -d postgres valkey
wait_for_compose_health "$E_COMPOSE" "$E_PROJECT" postgres
if check_e_database_role; then return 0; fi
if [ "$RESET_FAILED_INSTALL" = 1 ] && [ "$CONFIRM_NO_BUSINESS_DATA" = YES ]; then
reset_e_postgres_volume "$E_COMPOSE" "$E_PROJECT"
docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" up -d postgres
wait_for_compose_health "$E_COMPOSE" "$E_PROJECT" postgres
check_e_database_role || die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: 重置后仍无法使用固定 product_e 角色认证"
return 0
fi
die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: product_e 角色密码不匹配;未删除数据库卷"
}
prepare_s_database() {
docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" up -d postgres valkey
wait_for_compose_health "$S_COMPOSE" "$S_PROJECT" postgres
if check_s_database_role; then return 0; fi
die "DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY: product_s 角色密码不匹配;未删除数据库卷"
}
prepare_e_database
docker compose --env-file "$ENV_FILE" -f "$E_COMPOSE" -p "$E_PROJECT" run --rm --no-deps e-server migrate -config /etc/overcooked-e/config.yaml
docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" up -d postgres valkey
prepare_s_database
docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" run --rm --no-deps s-migrate
docker compose --env-file "$ENV_FILE" -f "$S_COMPOSE" -p "$S_PROJECT" up -d s-control control-proxy webui
wait_for_compose_health "$S_COMPOSE" "$S_PROJECT" control-proxy
+100
View File
@@ -0,0 +1,100 @@
#!/bin/sh
set -eu
root=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
fake_bin=$(mktemp -d /tmp/overcooked-installer-fakes.XXXXXX)
test_root=$(mktemp -d /tmp/overcooked-installer-db-test.XXXXXX)
trap 'rm -rf "$fake_bin" "$test_root"' EXIT
cp "$root/test/fake-docker.sh" "$fake_bin/docker"
cp "$root/test/fake-curl.sh" "$fake_bin/curl"
chmod 755 "$fake_bin/docker" "$fake_bin/curl"
run_install() {
mode=$1
output=$2
printf '%s\n' "$mode" > "$test_root/db-state"
: > "$test_root/docker.log"
set +e
PATH="$fake_bin:$PATH" \
FAKE_DOCKER_LOG="$test_root/docker.log" \
FAKE_DB_STATE="$test_root/db-state" \
OVERCOOKED_ROOT="$test_root/root" \
OVERCOOKED_SERVER_IP=203.0.113.10 \
OVERCOOKED_API_PORT=29880 \
OVERCOOKED_WEB_PORT=29883 \
OVERCOOKED_E_PORT=29896 \
OVERCOOKED_AUTH_PUBLIC_KEYS='{"auth-1":"PUBLIC_KEY"}' \
OVERCOOKED_ACTIVATION_CODE=TEST-CODE \
"$root/install.sh" > "$output" 2>&1
status=$?
set -e
if [ "$status" -ne 0 ]; then
cat "$output" >&2
fi
return "$status"
}
first_output=$test_root/first.out
run_install ok "$first_output"
first_e_password=$(sed -n 's/^PRODUCT_E_POSTGRES_PASSWORD=//p' "$test_root/root/production.env")
first_s_password=$(sed -n 's/^S_POSTGRES_PASSWORD=//p' "$test_root/root/production.env")
[ "${#first_e_password}" = 64 ]
[ "${#first_s_password}" = 64 ]
[ "$(stat -c '%a' "$test_root/root/production.env")" = 600 ]
! rg -n 'volume rm|down .*-[^ ]*v' "$test_root/docker.log"
second_output=$test_root/second.out
run_install ok "$second_output"
second_e_password=$(sed -n 's/^PRODUCT_E_POSTGRES_PASSWORD=//p' "$test_root/root/production.env")
second_s_password=$(sed -n 's/^S_POSTGRES_PASSWORD=//p' "$test_root/root/production.env")
[ "$first_e_password" = "$second_e_password" ]
[ "$first_s_password" = "$second_s_password" ]
! rg -n 'volume rm|down .*-[^ ]*v' "$test_root/docker.log"
echo "MALICIOUS=\$(touch $test_root/marker)" >> "$test_root/root/production.env"
third_output=$test_root/third.out
run_install ok "$third_output"
[ ! -e "$test_root/marker" ]
mismatch_output=$test_root/mismatch.out
if run_install mismatch "$mismatch_output"; then
echo 'password mismatch unexpectedly succeeded' >&2
exit 1
fi
rg -q 'DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY' "$mismatch_output"
! rg -n '^volume rm|down .*-[^ ]*v' "$test_root/docker.log"
[ "$first_e_password" = "$(sed -n 's/^PRODUCT_E_POSTGRES_PASSWORD=//p' "$test_root/root/production.env")" ]
wrong_role_output=$test_root/wrong-role.out
if run_install wrong-role "$wrong_role_output"; then
echo 'wrong database role unexpectedly succeeded' >&2
exit 1
fi
rg -q 'DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY' "$wrong_role_output"
! rg -n '^volume rm|down .*-[^ ]*v' "$test_root/docker.log"
s_mismatch_output=$test_root/s-mismatch.out
if run_install s-mismatch "$s_mismatch_output"; then
echo 'S password mismatch unexpectedly succeeded' >&2
exit 1
fi
rg -q 'DATABASE_PASSWORD_MISMATCH_NEEDS_MANUAL_RECOVERY' "$s_mismatch_output"
! rg -n '^volume rm|down .*-[^ ]*v' "$test_root/docker.log"
reset_output=$test_root/reset.out
printf 'reset-once\n' > "$test_root/db-state"
PATH="$fake_bin:$PATH" \
FAKE_DOCKER_LOG="$test_root/docker.log" \
FAKE_DB_STATE="$test_root/db-state" \
OVERCOOKED_ROOT="$test_root/root" \
OVERCOOKED_SERVER_IP=203.0.113.10 \
OVERCOOKED_API_PORT=29880 \
OVERCOOKED_WEB_PORT=29883 \
OVERCOOKED_E_PORT=29896 \
OVERCOOKED_AUTH_PUBLIC_KEYS='{"auth-1":"PUBLIC_KEY"}' \
OVERCOOKED_ACTIVATION_CODE=TEST-CODE \
OVERCOOKED_RESET_FAILED_INSTALL=1 \
OVERCOOKED_CONFIRM_NO_BUSINESS_DATA=YES \
"$root/install.sh" > "$reset_output" 2>&1
rg -q '已收到显式无业务数据确认' "$reset_output"
rg -q '^volume rm fake-e-postgres$' "$test_root/docker.log"
echo 'database credential retry/reset tests passed'
+2
View File
@@ -0,0 +1,2 @@
#!/bin/sh
exit 0
+81
View File
@@ -0,0 +1,81 @@
#!/bin/sh
set -eu
: "${FAKE_DOCKER_LOG:?}"
: "${FAKE_DB_STATE:?}"
printf '%s\n' "$*" >> "$FAKE_DOCKER_LOG"
if [ "${1:-}" = compose ]; then
shift
args=$*
case "$args" in
*' version'*) exit 0 ;;
*' ps -q postgres'*) printf 'fake-postgres\n'; exit 0 ;;
*' ps -q control-proxy'*) printf 'fake-control-proxy\n'; exit 0 ;;
*' exec '*product_e*)
if [ "$(cat "$FAKE_DB_STATE")" = mismatch ]; then
printf 'FATAL: password authentication failed for user product_e\n' >&2
exit 2
fi
if [ "$(cat "$FAKE_DB_STATE")" = reset-once ]; then
printf 'FATAL: password authentication failed for user product_e\n' >&2
exit 2
fi
if [ "$(cat "$FAKE_DB_STATE")" = wrong-role ]; then
printf 'product_s\n'
exit 0
fi
printf 'product_e\n'; exit 0 ;;
*' exec '*product_s*)
if [ "$(cat "$FAKE_DB_STATE")" = wrong-role ]; then
printf 'product_e\n'
exit 0
fi
if [ "$(cat "$FAKE_DB_STATE")" = s-mismatch ]; then
printf 'FATAL: password authentication failed for user product_s\n' >&2
exit 2
fi
printf 'product_s\n'; exit 0 ;;
*) exit 0 ;;
esac
fi
case "${1:-}" in
network|pull|image) exit 0 ;;
save)
output=''
previous=''
for argument do
if [ "$previous" = -o ]; then output=$argument; fi
previous=$argument
done
[ -n "$output" ] && : > "$output"
exit 0
;;
inspect)
format=${3:-}
case "$format" in
*State.Health.Status*) printf 'healthy\n' ;;
*var/lib/postgresql/data*) printf 'fake-e-postgres\n' ;;
*) printf '\n' ;;
esac
exit 0
;;
volume)
case "${2:-}" in
inspect)
case "${4:-}" in
*com.docker.compose.project*) printf 'overcooked-e\n' ;;
*com.docker.compose.volume*) printf 'e-postgres\n' ;;
esac
exit 0
;;
rm)
printf 'ok\n' > "$FAKE_DB_STATE"
exit 0
;;
esac
;;
esac
exit 0
+5 -1
View File
@@ -16,11 +16,15 @@ test -f compose/s.yml
test -f compose/e.yml
test -f config/e-server.production.yaml
test -f s-migrations.tar.gz
test "$(find s-migrations -type f -name '*.up.sql' | wc -l)" -gt 0
test "$(tar -tzf s-migrations.tar.gz | awk '/\.up\.sql$/ { count++ } END { print count + 0 }')" -gt 0
test -x test/database-credentials.sh
sha256sum -c SHA256SUMS >/dev/null
! rg -n '^([[:space:]]*)build:' compose
! rg -n 'overcooked-auth|overcooked-auth-webui' compose install.sh
! rg -n 'S_TURNSTILE_(SITE|SECRET)_KEY: .*\\?required' compose
! rg -n 'S_INTERNAL_TLS_(ADDR|CERT_FILE|KEY_FILE|CLIENT_CA_FILE)' compose install.sh
! rg -n 'psql[[:space:]]+-U[[:space:]]+postgres|(^|[^[:alnum:]_])eval([[:space:]]|$)' install.sh
rg -n 'psql[[:space:]]+-U[[:space:]]+product_e|psql[[:space:]]+-U[[:space:]]+product_s' install.sh >/dev/null
rg -n 'control-proxy|PRODUCT_E_CONTROL_PLANE_TLS_' compose install.sh >/dev/null
./test/database-credentials.sh
echo 'static customer installer checks passed'