fixed migration for old instances

This commit is contained in:
onysd 2026-07-28 00:04:47 +03:00
parent d37ecf6071
commit a69aa0a975
10 changed files with 463 additions and 86 deletions

View file

@ -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

View 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
View 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

View 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"