#!/bin/bash

set -euo pipefail

PG_DATA=/var/lib/postgresql/data
PG_USER=puppetdb
PG_PASSWORD=puppetdb
PG_DB=puppetdb

PROJECT=puppet_env
NEW_PG_VOLUME=${PROJECT}_puppetdb_postgres15
MIGRATION_NETWORK=${PROJECT}_default
OLD_PG_CONTAINER=$(docker compose ps --all --format '{{.Name}} {{.Service}}' | awk '$2 == "postgres" { print $1; exit }')
NEW_PG_CONTAINER=${PROJECT}_migrate_postgres
NEW_PG_IMAGE=${DOCKER_REGISTRY:-dockerregistry.artix.su:2443}/postgres:15.3-local

log() {
    echo "`date '+%Y-%m-%d %H:%M:%S'` $1: $2"
}

WAIT_ATTEMPTS=120
wait_postgres() {
    local container=$1
    for ((i = 0; i < WAIT_ATTEMPTS; i++)); do
        # проверка по tcp: во время первичной инициализации postgres слушает только unix socket
        docker exec -e PGPASSWORD=$PG_PASSWORD $container psql -h 127.0.0.1 -U $PG_USER -d $PG_DB -tAc 'select 1' > /dev/null 2>&1 && return 0
        sleep 1
    done
    log ERROR "postgres in container $container is not ready"
    docker logs --tail 30 $container
    return 1
}

psql_in() {
    local container=$1
    shift
    docker exec -i -e PGPASSWORD=$PG_PASSWORD $container psql -v ON_ERROR_STOP=1 -q -h 127.0.0.1 -U $PG_USER "$@"
}

table_counts() {
    psql_in $1 -d $PG_DB -tA <<'EOF'
select 'certnames', count(*) from certnames
union all select 'catalogs', count(*) from catalogs
union all select 'factsets', count(*) from factsets
union all select 'reports', count(*) from reports
order by 1;
EOF
}

cleanup() {
    local exit_code=$?

    docker rm -f "$NEW_PG_CONTAINER" > /dev/null 2>&1 || true

    if (( exit_code != 0 )); then
        log ERROR "Migration failed; restarting old services"
        docker compose start puppet puppetdb rsync_puppet || true
    fi

    return "$exit_code"
}

trap cleanup EXIT

# Останавливаем сервисы, кроме базы
docker compose stop puppet puppetdb rsync_puppet

# После запуска ранее остановленного стека PostgreSQL 9 может еще не принимать соединения.
log INFO "Wait for old postgres ($OLD_PG_CONTAINER)"
wait_postgres "$OLD_PG_CONTAINER"

log INFO "Pull postgres 15 ($NEW_PG_IMAGE)"
docker pull $NEW_PG_IMAGE > /dev/null 2>&1 || docker image inspect $NEW_PG_IMAGE > /dev/null

log INFO "Start postgres 15 ($NEW_PG_IMAGE) on $NEW_PG_VOLUME"
docker run -d --name $NEW_PG_CONTAINER --network $MIGRATION_NETWORK -v $NEW_PG_VOLUME:$PG_DATA \
        -e POSTGRES_USER=$PG_USER -e POSTGRES_PASSWORD=$PG_PASSWORD -e POSTGRES_DB=$PG_DB $NEW_PG_IMAGE > /dev/null
wait_postgres $NEW_PG_CONTAINER

log INFO "Recreate database $PG_DB in postgres 15"
psql_in $NEW_PG_CONTAINER -d postgres -c "drop database if exists $PG_DB" -c "create database $PG_DB owner $PG_USER" < /dev/null

log INFO "PG_DUMP"
docker exec -e PGPASSWORD=$PG_PASSWORD $NEW_PG_CONTAINER pg_dump -h $OLD_PG_CONTAINER -U $PG_USER -d $PG_DB \
        | psql_in $NEW_PG_CONTAINER -d $PG_DB > /dev/null

wait_postgres $NEW_PG_CONTAINER

log INFO "CREATE EXTENSION"
# pg_trgm рекомендован для puppetdb 7 (индексы для регулярных выражений в запросах)
psql_in $NEW_PG_CONTAINER -d $PG_DB -c "create extension if not exists pg_trgm" < /dev/null
log INFO "ANALYZE"
psql_in $NEW_PG_CONTAINER -d $PG_DB -c "analyze" < /dev/null > /dev/null

# Проверяем что основные таблицы мигрировали по кол-ву строк в них
OLD_COUNTS=`table_counts $OLD_PG_CONTAINER`
NEW_COUNTS=`table_counts $NEW_PG_CONTAINER`
log INFO "Rows in postgres 9.6: `echo $OLD_COUNTS`"
log INFO "Rows in postgres 15:  `echo $NEW_COUNTS`"
if [[ "$OLD_COUNTS" != "$NEW_COUNTS" ]]; then
    log ERROR "Row counts differ, migration failed"
    exit 1
fi
