fixed migration for old instances
This commit is contained in:
parent
d37ecf6071
commit
a69aa0a975
10 changed files with 463 additions and 86 deletions
|
|
@ -31,16 +31,21 @@ services:
|
|||
- "-c"
|
||||
- "track_io_timing=on"
|
||||
environment:
|
||||
POSTGRES_DB: telesrv
|
||||
POSTGRES_USER: telesrv
|
||||
POSTGRES_PASSWORD: telesrv
|
||||
# Only take effect when Postgres bootstraps a brand-new, empty data
|
||||
# volume; a pre-existing volume ignores these and keeps whatever
|
||||
# role/database name it was originally initialized with. See
|
||||
# deploy/migrate-db-naming.ps1 for renaming an existing telesrv-named
|
||||
# role/database in-place.
|
||||
POSTGRES_DB: owpengram
|
||||
POSTGRES_USER: owpengram
|
||||
POSTGRES_PASSWORD: owpengram
|
||||
TZ: UTC
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U telesrv -d telesrv"]
|
||||
test: ["CMD-SHELL", "pg_isready -U owpengram -d owpengram"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 10
|
||||
|
|
|
|||
126
deploy/migrate-db-naming.ps1
Normal file
126
deploy/migrate-db-naming.ps1
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<#
|
||||
One-time, opt-in migration of the Postgres role/database name inside an
|
||||
existing data volume from "telesrv" to "owpengram".
|
||||
|
||||
docker-compose.yml's POSTGRES_USER/POSTGRES_DB env vars only take effect when
|
||||
Postgres bootstraps a brand-new, empty data volume -- they're silently
|
||||
ignored against a pre-existing volume, so changing the compose file alone
|
||||
does nothing for an install that already has data on disk. This script talks
|
||||
to the already-running Postgres container directly (run it after "docker
|
||||
compose up" plus a readiness wait, not before) and renames the role/database
|
||||
in place via ALTER ROLE / ALTER DATABASE.
|
||||
|
||||
The existing password is left untouched -- renaming doesn't change it. Only
|
||||
the TELESRV_POSTGRES_DSN line in .env is patched (never any other line, and
|
||||
never if that line was already customized away from the plain telesrv
|
||||
defaults) so the app can still connect afterwards.
|
||||
|
||||
Prints nothing when there's nothing to do. Prompts interactively at most
|
||||
once; the decision is cached in a state file next to .secrets/.public_ip, so
|
||||
this is never asked again. Declining is permanent: the install keeps the
|
||||
"telesrv" role/database name forever.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$ContainerName = "owpengram-postgres",
|
||||
[string]$EnvFile = ".env",
|
||||
[string]$StateFile = ".db_naming"
|
||||
)
|
||||
|
||||
function Write-Info([string]$Message) {
|
||||
[Console]::Error.WriteLine($Message)
|
||||
}
|
||||
|
||||
function Test-PgRoleExists([string]$Role) {
|
||||
$out = docker exec $ContainerName psql -U telesrv -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='$Role'" 2>$null
|
||||
return ($LASTEXITCODE -eq 0) -and ($out -match '1')
|
||||
}
|
||||
|
||||
$state = $null
|
||||
if (Test-Path $StateFile) {
|
||||
$state = (Get-Content $StateFile -Raw -ErrorAction SilentlyContinue)
|
||||
if ($state) { $state = $state.Trim() }
|
||||
}
|
||||
if ($state -eq "owpengram" -or $state -eq "telesrv") {
|
||||
exit 0
|
||||
}
|
||||
|
||||
$oldExists = Test-PgRoleExists "telesrv"
|
||||
$newExists = Test-PgRoleExists "owpengram"
|
||||
|
||||
if (-not $oldExists -or $newExists) {
|
||||
# Nothing to migrate: either already "owpengram"-named (fresh install or
|
||||
# already renamed), or connecting as "telesrv" didn't work at all (custom
|
||||
# credentials already in place) -- leave it alone either way.
|
||||
Set-Content -Path $StateFile -Value "owpengram" -NoNewline
|
||||
exit 0
|
||||
}
|
||||
|
||||
if ([Console]::IsInputRedirected) {
|
||||
Write-Info "[cfg] Postgres role/database still named 'telesrv' but running non-interactively; keeping old naming for now"
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Info ""
|
||||
Write-Info "== Postgres role/database naming =="
|
||||
Write-Info "The Postgres role and database inside this install's data volume are still"
|
||||
Write-Info "named 'telesrv' (renaming docker-compose.yml alone doesn't affect existing"
|
||||
Write-Info "data). Renaming them to 'owpengram' keeps everything consistent. Recommended."
|
||||
$reply = Read-Host "Rename Postgres role/database from 'telesrv' to 'owpengram' now? [Y/n]"
|
||||
|
||||
if ($reply -match '^[Nn]') {
|
||||
Set-Content -Path $StateFile -Value "telesrv" -NoNewline
|
||||
Write-Info "[cfg] keeping 'telesrv' Postgres role/database naming (won't ask again)"
|
||||
exit 0
|
||||
}
|
||||
|
||||
Write-Info "[cfg] renaming Postgres role/database: telesrv -> owpengram"
|
||||
|
||||
# Postgres refuses "ALTER ROLE <session user> RENAME" (session user cannot be
|
||||
# renamed), so telesrv can't rename itself. Do the database rename directly
|
||||
# as telesrv, then use a short-lived second superuser to rename the telesrv
|
||||
# role, then drop that temporary role from the now-renamed "owpengram" role's
|
||||
# own session. Each step is a separate connection/transaction on purpose:
|
||||
# combining the ALTER DATABASE and ALTER ROLE in one multi-statement call
|
||||
# rolls the database rename back too when the role rename fails (Postgres
|
||||
# treats a semicolon-separated -c string as one implicit transaction).
|
||||
docker exec $ContainerName psql -U telesrv -d postgres -c "ALTER DATABASE telesrv RENAME TO owpengram; CREATE ROLE _telesrv_migrate SUPERUSER LOGIN PASSWORD 'telesrv_migrate';" *> $null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Info "[ERROR] failed to rename Postgres database / create temporary migration role"
|
||||
exit 1
|
||||
}
|
||||
docker exec $ContainerName psql -U _telesrv_migrate -d owpengram -c "ALTER ROLE telesrv RENAME TO owpengram;" *> $null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Info "[ERROR] failed to rename Postgres role"
|
||||
exit 1
|
||||
}
|
||||
docker exec $ContainerName psql -U owpengram -d owpengram -c "DROP ROLE _telesrv_migrate;" *> $null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Info "[WARN] renamed role/database but failed to drop the temporary _telesrv_migrate role - remove it by hand"
|
||||
}
|
||||
|
||||
Set-Content -Path $StateFile -Value "owpengram" -NoNewline
|
||||
Write-Info "[ok] Postgres role/database renamed to 'owpengram'"
|
||||
|
||||
if (Test-Path $EnvFile) {
|
||||
$lines = Get-Content $EnvFile
|
||||
$pattern = '^TELESRV_POSTGRES_DSN=postgres://telesrv:([^@]*)@([^/]+)/telesrv(\?.*)?$'
|
||||
$patched = $false
|
||||
$newLines = foreach ($line in $lines) {
|
||||
if (-not $patched -and $line -match $pattern) {
|
||||
$patched = $true
|
||||
"TELESRV_POSTGRES_DSN=postgres://owpengram:$($Matches[1])@$($Matches[2])/owpengram$($Matches[3])"
|
||||
} else {
|
||||
$line
|
||||
}
|
||||
}
|
||||
if ($patched) {
|
||||
Set-Content -Path $EnvFile -Value $newLines
|
||||
Write-Info "[ok] updated the TELESRV_POSTGRES_DSN line in $EnvFile (only that one line)"
|
||||
} else {
|
||||
Write-Info "[WARN] TELESRV_POSTGRES_DSN in $EnvFile doesn't match the plain telesrv defaults - update it by hand:"
|
||||
Write-Info " TELESRV_POSTGRES_DSN=postgres://owpengram:<your-password>@<host>:<port>/owpengram?sslmode=disable"
|
||||
}
|
||||
} else {
|
||||
Write-Info "[WARN] $EnvFile not found - if you create one, point TELESRV_POSTGRES_DSN at the 'owpengram' role/database"
|
||||
}
|
||||
117
deploy/migrate-db-naming.sh
Normal file
117
deploy/migrate-db-naming.sh
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
#!/usr/bin/env bash
|
||||
# Bash equivalent of migrate-db-naming.ps1 for start-server.sh (Linux/macOS
|
||||
# hosts, no PowerShell dependency). Keep both in sync; start-server.bat uses
|
||||
# the .ps1 version instead.
|
||||
#
|
||||
# One-time, opt-in migration of the Postgres role/database name inside an
|
||||
# existing data volume from "telesrv" to "owpengram".
|
||||
#
|
||||
# docker-compose.yml's POSTGRES_USER/POSTGRES_DB env vars only take effect
|
||||
# when Postgres bootstraps a brand-new, empty data volume -- they're silently
|
||||
# ignored against a pre-existing volume, so changing the compose file alone
|
||||
# does nothing for an install that already has data on disk. This script
|
||||
# talks to the already-running Postgres container directly (run it after
|
||||
# "docker compose up" plus a readiness wait, not before) and renames the
|
||||
# role/database in place via ALTER ROLE / ALTER DATABASE.
|
||||
#
|
||||
# The existing password is left untouched -- renaming doesn't change it. Only
|
||||
# the TELESRV_POSTGRES_DSN line in .env is patched (never any other line, and
|
||||
# never if that line was already customized away from the plain telesrv
|
||||
# defaults) so the app can still connect afterwards.
|
||||
#
|
||||
# Prints nothing when there's nothing to do. Prompts interactively at most
|
||||
# once; the decision is cached in a state file next to .secrets/.public_ip, so
|
||||
# this is never asked again. Declining is permanent: the install keeps the
|
||||
# "telesrv" role/database name forever.
|
||||
set -euo pipefail
|
||||
|
||||
CONTAINER_NAME="${1:-owpengram-postgres}"
|
||||
ENV_FILE="${2:-.env}"
|
||||
STATE_FILE="${3:-.db_naming}"
|
||||
|
||||
info() { echo "$@" >&2; }
|
||||
|
||||
pg_role_exists() {
|
||||
local role="$1"
|
||||
local out
|
||||
out="$(docker exec "$CONTAINER_NAME" psql -U telesrv -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='${role}'" 2>/dev/null || true)"
|
||||
[[ "$out" == *1* ]]
|
||||
}
|
||||
|
||||
state=""
|
||||
[[ -f "$STATE_FILE" ]] && state="$(tr -d '[:space:]' < "$STATE_FILE")"
|
||||
if [[ "$state" == "owpengram" || "$state" == "telesrv" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
old_exists=false
|
||||
new_exists=false
|
||||
pg_role_exists "telesrv" && old_exists=true
|
||||
pg_role_exists "owpengram" && new_exists=true
|
||||
|
||||
if [[ "$old_exists" == false || "$new_exists" == true ]]; then
|
||||
# Nothing to migrate: either already "owpengram"-named (fresh install or
|
||||
# already renamed), or connecting as "telesrv" didn't work at all (custom
|
||||
# credentials already in place) -- leave it alone either way.
|
||||
echo -n "owpengram" > "$STATE_FILE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -t 0 ]]; then
|
||||
info "[cfg] Postgres role/database still named 'telesrv' but running non-interactively; keeping old naming for now"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
info ""
|
||||
info "== Postgres role/database naming =="
|
||||
info "The Postgres role and database inside this install's data volume are still"
|
||||
info "named 'telesrv' (renaming docker-compose.yml alone doesn't affect existing"
|
||||
info "data). Renaming them to 'owpengram' keeps everything consistent. Recommended."
|
||||
read -rp "Rename Postgres role/database from 'telesrv' to 'owpengram' now? [Y/n] " reply
|
||||
|
||||
if [[ "$reply" =~ ^[Nn] ]]; then
|
||||
echo -n "telesrv" > "$STATE_FILE"
|
||||
info "[cfg] keeping 'telesrv' Postgres role/database naming (won't ask again)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
info "[cfg] renaming Postgres role/database: telesrv -> owpengram"
|
||||
|
||||
# Postgres refuses "ALTER ROLE <session user> RENAME" (session user cannot be
|
||||
# renamed), so telesrv can't rename itself. Do the database rename directly
|
||||
# as telesrv, then use a short-lived second superuser to rename the telesrv
|
||||
# role, then drop that temporary role from the now-renamed "owpengram" role's
|
||||
# own session. Each step is a separate connection/transaction on purpose:
|
||||
# combining the ALTER DATABASE and ALTER ROLE in one multi-statement call
|
||||
# rolls the database rename back too when the role rename fails (Postgres
|
||||
# treats a semicolon-separated -c string as one implicit transaction).
|
||||
if ! docker exec "$CONTAINER_NAME" psql -U telesrv -d postgres -c \
|
||||
"ALTER DATABASE telesrv RENAME TO owpengram; CREATE ROLE _telesrv_migrate SUPERUSER LOGIN PASSWORD 'telesrv_migrate';" \
|
||||
>/dev/null 2>&1; then
|
||||
info "[ERROR] failed to rename Postgres database / create temporary migration role"
|
||||
exit 1
|
||||
fi
|
||||
if ! docker exec "$CONTAINER_NAME" psql -U _telesrv_migrate -d owpengram -c \
|
||||
"ALTER ROLE telesrv RENAME TO owpengram;" >/dev/null 2>&1; then
|
||||
info "[ERROR] failed to rename Postgres role"
|
||||
exit 1
|
||||
fi
|
||||
if ! docker exec "$CONTAINER_NAME" psql -U owpengram -d owpengram -c \
|
||||
"DROP ROLE _telesrv_migrate;" >/dev/null 2>&1; then
|
||||
info "[WARN] renamed role/database but failed to drop the temporary _telesrv_migrate role - remove it by hand"
|
||||
fi
|
||||
|
||||
echo -n "owpengram" > "$STATE_FILE"
|
||||
info "[ok] Postgres role/database renamed to 'owpengram'"
|
||||
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
if grep -qE '^TELESRV_POSTGRES_DSN=postgres://telesrv:[^@]*@[^/]+/telesrv(\?.*)?$' "$ENV_FILE"; then
|
||||
sed -i -E 's#^TELESRV_POSTGRES_DSN=postgres://telesrv:([^@]*)@([^/]+)/telesrv(\?.*)?$#TELESRV_POSTGRES_DSN=postgres://owpengram:\1@\2/owpengram\3#' "$ENV_FILE"
|
||||
info "[ok] updated the TELESRV_POSTGRES_DSN line in ${ENV_FILE} (only that one line)"
|
||||
else
|
||||
info "[WARN] TELESRV_POSTGRES_DSN in ${ENV_FILE} doesn't match the plain telesrv defaults - update it by hand:"
|
||||
info " TELESRV_POSTGRES_DSN=postgres://owpengram:<your-password>@<host>:<port>/owpengram?sslmode=disable"
|
||||
fi
|
||||
else
|
||||
info "[WARN] ${ENV_FILE} not found - if you create one, point TELESRV_POSTGRES_DSN at the 'owpengram' role/database"
|
||||
fi
|
||||
105
deploy/migrate-docker-naming.sh
Normal file
105
deploy/migrate-docker-naming.sh
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#!/usr/bin/env bash
|
||||
# Bash equivalent of migrate-docker-naming.ps1 for start-server.sh (Linux/macOS
|
||||
# hosts, no PowerShell dependency). Keep both in sync; start-server.bat uses
|
||||
# the .ps1 version instead.
|
||||
#
|
||||
# Resolves (and, once, offers to migrate) the Docker project/container/volume
|
||||
# naming used by deploy/docker-compose.yml.
|
||||
#
|
||||
# The project used to be named "telesrv" in Docker (containers telesrv-postgres
|
||||
# / telesrv-redis, volumes telesrv_pgdata / telesrv_redisdata). It's now
|
||||
# "owpengram" by default, so a gramsrv instance running on the same machine
|
||||
# can't be confused with this one at a glance in `docker ps` / Docker Desktop.
|
||||
#
|
||||
# Existing installs may still have telesrv_* volumes with real data in them.
|
||||
# This script asks, at most once, whether to copy that data over to owpengram_*
|
||||
# naming. The answer is cached in a state file next to .secrets/.public_ip:
|
||||
# - accepted -> volumes are copied (old telesrv_* volumes are left in place,
|
||||
# untouched, as a backup), state becomes "owpengram", never asked again.
|
||||
# - declined -> state becomes "telesrv", the install keeps the old naming
|
||||
# forever, never asked again.
|
||||
# - fresh install (no telesrv_* volumes found) -> silently uses "owpengram",
|
||||
# nothing to migrate.
|
||||
#
|
||||
# Prints exactly two lines to stdout: the resolved project name, then the
|
||||
# resolved container/volume prefix. Everything else (prompts, progress) goes
|
||||
# to stderr, so a caller can safely capture just stdout.
|
||||
set -euo pipefail
|
||||
|
||||
COMPOSE_FILE="${1:-deploy/docker-compose.yml}"
|
||||
STATE_FILE="${2:-.docker_naming}"
|
||||
|
||||
info() { echo "$@" >&2; }
|
||||
result() { printf '%s\n%s\n' "$1" "$2"; }
|
||||
|
||||
volume_exists() { docker volume inspect "$1" >/dev/null 2>&1; }
|
||||
|
||||
state=""
|
||||
[[ -f "$STATE_FILE" ]] && state="$(tr -d '[:space:]' < "$STATE_FILE")"
|
||||
|
||||
if [[ "$state" == "owpengram" ]]; then
|
||||
result "owpengram" "owpengram"
|
||||
exit 0
|
||||
fi
|
||||
if [[ "$state" == "telesrv" ]]; then
|
||||
result "telesrv" "telesrv"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
old_pg=false
|
||||
old_redis=false
|
||||
volume_exists "telesrv_pgdata" && old_pg=true
|
||||
volume_exists "telesrv_redisdata" && old_redis=true
|
||||
|
||||
if [[ "$old_pg" == false && "$old_redis" == false ]]; then
|
||||
echo -n "owpengram" > "$STATE_FILE"
|
||||
result "owpengram" "owpengram"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ! -t 0 ]]; then
|
||||
info "[cfg] old 'telesrv_*' Docker volumes found but running non-interactively; keeping old naming for now"
|
||||
result "telesrv" "telesrv"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
info ""
|
||||
info "== Docker container/volume naming =="
|
||||
info "Found existing Docker volumes named 'telesrv_*' (from before the project was"
|
||||
info "renamed to OwpenGram). Renaming them to 'owpengram_*' avoids confusing this"
|
||||
info "server with a gramsrv instance running on the same machine. Recommended."
|
||||
read -rp "Migrate Docker containers/volumes from 'telesrv' to 'owpengram' naming now? [Y/n] " reply
|
||||
|
||||
if [[ "$reply" =~ ^[Nn] ]]; then
|
||||
echo -n "telesrv" > "$STATE_FILE"
|
||||
info "[cfg] keeping 'telesrv' Docker naming (won't ask again)"
|
||||
result "telesrv" "telesrv"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
info "[cfg] migrating Docker volumes: telesrv_* -> owpengram_*"
|
||||
|
||||
TELESRV_DOCKER_PROJECT=telesrv TELESRV_DOCKER_PREFIX=telesrv \
|
||||
docker compose -f "$COMPOSE_FILE" -p telesrv stop postgres redis >/dev/null 2>&1 || true
|
||||
|
||||
if [[ "$old_pg" == true ]]; then
|
||||
docker volume create owpengram_pgdata >/dev/null
|
||||
if ! docker run --rm -v telesrv_pgdata:/from -v owpengram_pgdata:/to alpine sh -c "cp -a /from/. /to/"; then
|
||||
info "[ERROR] failed to copy pgdata volume"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [[ "$old_redis" == true ]]; then
|
||||
docker volume create owpengram_redisdata >/dev/null
|
||||
if ! docker run --rm -v telesrv_redisdata:/from -v owpengram_redisdata:/to alpine sh -c "cp -a /from/. /to/"; then
|
||||
info "[ERROR] failed to copy redisdata volume"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
TELESRV_DOCKER_PROJECT=telesrv TELESRV_DOCKER_PREFIX=telesrv \
|
||||
docker compose -f "$COMPOSE_FILE" -p telesrv down >/dev/null 2>&1 || true
|
||||
|
||||
echo -n "owpengram" > "$STATE_FILE"
|
||||
info "[ok] migration complete - old 'telesrv_*' volumes were left in place, untouched, as a backup"
|
||||
result "owpengram" "owpengram"
|
||||
Loading…
Add table
Add a link
Reference in a new issue