mirror of
https://github.com/absmach/magistrala.git
synced 2026-08-07 07:14:46 +00:00
NOISSUE - Improve migration scripts
Signed-off-by: dusan <borovcanindusan1@gmail.com>
This commit is contained in:
@@ -197,7 +197,7 @@ FILTERED_SERVICES = $(filter-out $(RUN_ADDON_ARGS), $(SERVICES))
|
||||
|
||||
all: $(SERVICES)
|
||||
|
||||
.PHONY: all $(SERVICES) dockers dockers_dev latest release provision_atom_tokens provision-atom-tokens run_latest run_latest_ci run_tls run_stable run_addons grpc_mtls_certs check_mtls check_certs test_api mocks
|
||||
.PHONY: all $(SERVICES) dockers dockers_dev latest release provision_atom_tokens provision-atom-tokens migrate_atom run_latest run_latest_ci run_tls run_stable run_addons grpc_mtls_certs check_mtls check_certs test_api mocks
|
||||
|
||||
clean:
|
||||
rm -rf ${BUILD_DIR}
|
||||
@@ -324,6 +324,15 @@ provision_atom_tokens:
|
||||
provision-atom-tokens:
|
||||
@:
|
||||
|
||||
# Migrate an old Magistrala (v0.30.0 / pre-Atom) deployment into Atom. Runs an
|
||||
# isolated, collision-free stack, seeds the Atom schema into the run_latest Atom
|
||||
# volume and loads the data. Default is a dry-run; pass args="--apply" to load,
|
||||
# args="--verify" to reconcile afterwards.
|
||||
# make migrate_atom # dry-run
|
||||
# make migrate_atom args="--apply" # perform the migration
|
||||
migrate_atom:
|
||||
DOCKER_PROJECT="$(DOCKER_PROJECT)" tools/atom-migration/migrate.sh $(args)
|
||||
|
||||
check_tls:
|
||||
ifeq ($(GRPC_TLS),true)
|
||||
@echo "gRPC TLS is enabled"
|
||||
|
||||
@@ -3,6 +3,110 @@
|
||||
Offline, idempotent migrator: Magistrala v0.30.0 (per-service Postgres) → Atom IAM
|
||||
(single Postgres). See [PLAN.md](./PLAN.md) for the full mapping and runbook.
|
||||
|
||||
## One-command migration (recommended)
|
||||
|
||||
If you just want to migrate an old deployment and bring it up with
|
||||
`make run_latest`, use the orchestrator — it handles all the port / volume /
|
||||
container-name collisions for you:
|
||||
|
||||
```bash
|
||||
make migrate_atom # dry-run: reads + validates, writes nothing
|
||||
make migrate_atom args="--apply" # perform the migration
|
||||
make migrate_atom args="--verify" # reconcile source vs Atom afterwards
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
# stop the old stack, then:
|
||||
make run_latest
|
||||
```
|
||||
|
||||
…and the new deployment serves the migrated data.
|
||||
|
||||
How it stays collision-free: [`migrate.sh`](./migrate.sh) +
|
||||
[`docker-compose.migrate.yaml`](./docker-compose.migrate.yaml) run everything in
|
||||
their own Compose project (`atommig`) on a private network, binding **no host
|
||||
ports** and using **no fixed container names**, so they never clash with a
|
||||
running Magistrala (old or `run_latest`) stack. It:
|
||||
|
||||
1. mounts the eight old per-service DB volumes
|
||||
(`magistrala_magistrala-<svc>-db-volume`) into throwaway Postgres containers
|
||||
(Postgres major version + data-dir layout auto-detected from the volume);
|
||||
2. brings up an Atom + Postgres on the **same** volume `make run_latest` mounts
|
||||
(`<DOCKER_PROJECT>_magistrala-atom-db-volume`), so Atom seeds its schema there
|
||||
and the migrated rows persist for the next `run_latest`;
|
||||
3. runs the migrator on that private network (reaching every DB by service name);
|
||||
4. tears the stack down, leaving every volume intact.
|
||||
|
||||
All migration volumes are declared `external`, so `down` can never destroy data.
|
||||
|
||||
Prerequisites: run this on the machine that hosted the old Magistrala compose
|
||||
stack. Stop that stack (`docker compose ... down`, **without** `-v`) so the
|
||||
per-service DB volumes are free but still present locally — the migrator mounts
|
||||
them directly. On `--apply`, the device-key CSV described below lands in
|
||||
`tools/atom-migration/report/`.
|
||||
|
||||
Env overrides: `SRC_VOL_PREFIX` (old volume prefix, default
|
||||
`magistrala_magistrala-`), `SRC_DB_USER` / `SRC_DB_PASS` (old Postgres creds,
|
||||
default `magistrala`), `DOCKER_PROJECT` (run_latest project; default derived like
|
||||
the Makefile), `MIGRATE_PROJECT` (isolated project name, default `atommig`).
|
||||
Pass `--keep` to leave the stack up for debugging.
|
||||
|
||||
### How volume names are resolved
|
||||
|
||||
Names are **derived by convention, not auto-discovered**. There are two sets.
|
||||
|
||||
**Source volumes (old deployment, read-only inputs).** Built from a prefix plus
|
||||
a fixed per-service suffix:
|
||||
|
||||
```
|
||||
<SRC_VOL_PREFIX><svc>-db-volume # svc ∈ domains users clients channels groups auth re reports
|
||||
```
|
||||
|
||||
`SRC_VOL_PREFIX` defaults to `magistrala_magistrala-`, i.e. old Compose project
|
||||
`magistrala` + Docker Compose's own `magistrala-` volume key. So
|
||||
`auth` → `magistrala_magistrala-auth-db-volume`. `migrate.sh` `docker volume
|
||||
inspect`s all eight up front and aborts loudly if any is missing. The same
|
||||
`${SRC_VOL_PREFIX}` feeds the `external` volume names in
|
||||
`docker-compose.migrate.yaml`, so the script and Compose always agree. If your
|
||||
old deployment used a different Compose project name, set `SRC_VOL_PREFIX`
|
||||
(e.g. `SRC_VOL_PREFIX=myproj_magistrala-`).
|
||||
|
||||
**Atom target volume (where migrated data is written).** Must equal exactly the
|
||||
volume `make run_latest` mounts, or the new stack would come up on a different,
|
||||
empty volume. `make run_latest` mounts `magistrala-atom-db-volume`, which Docker
|
||||
Compose prefixes with the project name `DOCKER_PROJECT`:
|
||||
|
||||
```
|
||||
<DOCKER_PROJECT>_magistrala-atom-db-volume
|
||||
```
|
||||
|
||||
`DOCKER_PROJECT` is itself derived from the git remote, replicating the Makefile
|
||||
formula:
|
||||
|
||||
```sh
|
||||
repo=$(git remote get-url origin | sed -E 's@.*/([^/]+)/([^/.]+)(\.git)?@\1_\2@') # owner_repo
|
||||
DOCKER_PROJECT=$(echo "$repo" | sed -E 's/[^a-zA-Z0-9]/_/g' | tr '[:upper:]' '[:lower:]')
|
||||
ATOM_TARGET_VOLUME="${DOCKER_PROJECT}_magistrala-atom-db-volume"
|
||||
```
|
||||
|
||||
`make migrate_atom` also passes `DOCKER_PROJECT="$(DOCKER_PROJECT)"` straight from
|
||||
the Makefile, so the two stay in lockstep even if the git derivation would differ.
|
||||
The target volume is created if it does not yet exist (so the schema-seed step can
|
||||
write to it); `make run_latest` then reuses the same name. With no usable git
|
||||
remote, or a remote that does not match the run_latest project, pass
|
||||
`DOCKER_PROJECT=` explicitly.
|
||||
|
||||
**Source Postgres layout** (mount point + `PGDATA` + image major version) is the
|
||||
one thing actually probed, not assumed: `migrate.sh` mounts the `users` source
|
||||
volume in a throwaway `alpine` container, locates `PG_VERSION`, and derives the
|
||||
mount path / `PGDATA` / `postgres:<major>-alpine` image from it (e.g. Postgres 18
|
||||
keeps data under `/var/lib/postgresql/<major>/docker`). This makes the tool work
|
||||
regardless of which Postgres version the old deployment ran.
|
||||
|
||||
The manual, lower-level steps below are still available if you need finer control.
|
||||
|
||||
## Build
|
||||
|
||||
Plain binary:
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
# Copyright (c) Abstract Machines
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Isolated, collision-free migration stack driven by migrate.sh.
|
||||
#
|
||||
# Everything here lives in its own Compose project (default: atommig) on its own
|
||||
# private network, binds NO host ports and uses NO fixed container_names, so it
|
||||
# can never collide with a running Magistrala (old or `make run_latest`) stack.
|
||||
#
|
||||
# * source DBs - eight Postgres containers that mount the OLD per-service
|
||||
# Magistrala volumes (external, read by the migrator).
|
||||
# * atom-db - Postgres mounting the NEW run_latest Atom volume, so data
|
||||
# written here is exactly what `make run_latest` later serves.
|
||||
# * atom - seeds/upgrades the Atom schema in that volume on boot.
|
||||
# * migrator - the atom-migration binary; run on demand by migrate.sh.
|
||||
#
|
||||
# Variables are supplied by migrate.sh (and docker/.env for the ATOM_* / atom
|
||||
# runtime settings). Source volume layout (mount point + PGDATA) is detected by
|
||||
# migrate.sh and injected, so this file works regardless of the Postgres major
|
||||
# version the old deployment used.
|
||||
|
||||
x-src-db: &src-db
|
||||
image: ${SRC_PG_IMAGE:-postgres:18-alpine}
|
||||
restart: "no"
|
||||
environment:
|
||||
POSTGRES_USER: ${SRC_DB_USER:-magistrala}
|
||||
POSTGRES_PASSWORD: ${SRC_DB_PASS:-magistrala}
|
||||
PGDATA: ${SRC_PGDATA:-/var/lib/postgresql/18/docker}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${SRC_DB_USER:-magistrala}"]
|
||||
interval: 3s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
networks:
|
||||
- migrate-net
|
||||
|
||||
services:
|
||||
domains-db:
|
||||
<<: *src-db
|
||||
environment:
|
||||
POSTGRES_USER: ${SRC_DB_USER:-magistrala}
|
||||
POSTGRES_PASSWORD: ${SRC_DB_PASS:-magistrala}
|
||||
POSTGRES_DB: domains
|
||||
PGDATA: ${SRC_PGDATA:-/var/lib/postgresql/18/docker}
|
||||
volumes:
|
||||
- domains-src:${SRC_MOUNT:-/var/lib/postgresql}
|
||||
|
||||
users-db:
|
||||
<<: *src-db
|
||||
environment:
|
||||
POSTGRES_USER: ${SRC_DB_USER:-magistrala}
|
||||
POSTGRES_PASSWORD: ${SRC_DB_PASS:-magistrala}
|
||||
POSTGRES_DB: users
|
||||
PGDATA: ${SRC_PGDATA:-/var/lib/postgresql/18/docker}
|
||||
volumes:
|
||||
- users-src:${SRC_MOUNT:-/var/lib/postgresql}
|
||||
|
||||
clients-db:
|
||||
<<: *src-db
|
||||
environment:
|
||||
POSTGRES_USER: ${SRC_DB_USER:-magistrala}
|
||||
POSTGRES_PASSWORD: ${SRC_DB_PASS:-magistrala}
|
||||
POSTGRES_DB: clients
|
||||
PGDATA: ${SRC_PGDATA:-/var/lib/postgresql/18/docker}
|
||||
volumes:
|
||||
- clients-src:${SRC_MOUNT:-/var/lib/postgresql}
|
||||
|
||||
channels-db:
|
||||
<<: *src-db
|
||||
environment:
|
||||
POSTGRES_USER: ${SRC_DB_USER:-magistrala}
|
||||
POSTGRES_PASSWORD: ${SRC_DB_PASS:-magistrala}
|
||||
POSTGRES_DB: channels
|
||||
PGDATA: ${SRC_PGDATA:-/var/lib/postgresql/18/docker}
|
||||
volumes:
|
||||
- channels-src:${SRC_MOUNT:-/var/lib/postgresql}
|
||||
|
||||
groups-db:
|
||||
<<: *src-db
|
||||
environment:
|
||||
POSTGRES_USER: ${SRC_DB_USER:-magistrala}
|
||||
POSTGRES_PASSWORD: ${SRC_DB_PASS:-magistrala}
|
||||
POSTGRES_DB: groups
|
||||
PGDATA: ${SRC_PGDATA:-/var/lib/postgresql/18/docker}
|
||||
volumes:
|
||||
- groups-src:${SRC_MOUNT:-/var/lib/postgresql}
|
||||
|
||||
auth-db:
|
||||
<<: *src-db
|
||||
environment:
|
||||
POSTGRES_USER: ${SRC_DB_USER:-magistrala}
|
||||
POSTGRES_PASSWORD: ${SRC_DB_PASS:-magistrala}
|
||||
POSTGRES_DB: auth
|
||||
PGDATA: ${SRC_PGDATA:-/var/lib/postgresql/18/docker}
|
||||
volumes:
|
||||
- auth-src:${SRC_MOUNT:-/var/lib/postgresql}
|
||||
|
||||
re-db:
|
||||
<<: *src-db
|
||||
environment:
|
||||
POSTGRES_USER: ${SRC_DB_USER:-magistrala}
|
||||
POSTGRES_PASSWORD: ${SRC_DB_PASS:-magistrala}
|
||||
POSTGRES_DB: rules_engine
|
||||
PGDATA: ${SRC_PGDATA:-/var/lib/postgresql/18/docker}
|
||||
volumes:
|
||||
- re-src:${SRC_MOUNT:-/var/lib/postgresql}
|
||||
|
||||
reports-db:
|
||||
<<: *src-db
|
||||
environment:
|
||||
POSTGRES_USER: ${SRC_DB_USER:-magistrala}
|
||||
POSTGRES_PASSWORD: ${SRC_DB_PASS:-magistrala}
|
||||
POSTGRES_DB: reports
|
||||
PGDATA: ${SRC_PGDATA:-/var/lib/postgresql/18/docker}
|
||||
volumes:
|
||||
- reports-src:${SRC_MOUNT:-/var/lib/postgresql}
|
||||
|
||||
# Target: the SAME volume `make run_latest` mounts, so migrated data persists.
|
||||
atom-db:
|
||||
image: postgres:16-alpine
|
||||
restart: "no"
|
||||
environment:
|
||||
POSTGRES_USER: ${ATOM_DB_USER:-atom}
|
||||
POSTGRES_PASSWORD: ${ATOM_DB_PASSWORD:-atom}
|
||||
POSTGRES_DB: ${ATOM_DB_NAME:-atom}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${ATOM_DB_USER:-atom} -d ${ATOM_DB_NAME:-atom}"]
|
||||
interval: 3s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
volumes:
|
||||
- atom-target:/var/lib/postgresql/data
|
||||
networks:
|
||||
- migrate-net
|
||||
|
||||
# Seeds / upgrades the Atom schema in the target volume on boot (migrations run
|
||||
# at startup). Certs disabled so it needs no mounted CA. No host ports.
|
||||
atom:
|
||||
image: ghcr.io/absmach/atom:${MG_RELEASE_TAG:-latest}
|
||||
restart: "no"
|
||||
depends_on:
|
||||
atom-db:
|
||||
condition: service_healthy
|
||||
env_file:
|
||||
- ../../docker/.env
|
||||
environment:
|
||||
DATABASE_URL: postgres://${ATOM_DB_USER:-atom}:${ATOM_DB_PASSWORD:-atom}@atom-db:5432/${ATOM_DB_NAME:-atom}
|
||||
LISTEN_ADDR: 0.0.0.0:8080
|
||||
ATOM_CERTS_ENABLED: "false"
|
||||
networks:
|
||||
- migrate-net
|
||||
|
||||
# The migration binary. Run on demand: `compose run --rm migrator <flags>`.
|
||||
migrator:
|
||||
image: magistrala/atom-migration:dev
|
||||
depends_on:
|
||||
atom-db:
|
||||
condition: service_healthy
|
||||
domains-db:
|
||||
condition: service_healthy
|
||||
users-db:
|
||||
condition: service_healthy
|
||||
clients-db:
|
||||
condition: service_healthy
|
||||
channels-db:
|
||||
condition: service_healthy
|
||||
groups-db:
|
||||
condition: service_healthy
|
||||
auth-db:
|
||||
condition: service_healthy
|
||||
re-db:
|
||||
condition: service_healthy
|
||||
reports-db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
ATOM_DATABASE_URL: host=atom-db port=5432 user=${ATOM_DB_USER:-atom} password=${ATOM_DB_PASSWORD:-atom} dbname=${ATOM_DB_NAME:-atom} sslmode=disable
|
||||
user: "${HOST_UID:-0}:${HOST_GID:-0}"
|
||||
working_dir: /work
|
||||
volumes:
|
||||
- ../../:/work
|
||||
networks:
|
||||
- migrate-net
|
||||
# Args (e.g. --apply / --verify) are supplied by migrate.sh via `compose run`.
|
||||
entrypoint: ["atom-migration", "--env", "tools/atom-migration/migrate.db.env"]
|
||||
|
||||
networks:
|
||||
migrate-net:
|
||||
driver: bridge
|
||||
|
||||
# All source volumes and the Atom target are pre-existing (external): Compose
|
||||
# never creates or deletes them, so `down` can never destroy migration data.
|
||||
volumes:
|
||||
domains-src:
|
||||
external: true
|
||||
name: ${SRC_VOL_PREFIX:-magistrala_magistrala-}domains-db-volume
|
||||
users-src:
|
||||
external: true
|
||||
name: ${SRC_VOL_PREFIX:-magistrala_magistrala-}users-db-volume
|
||||
clients-src:
|
||||
external: true
|
||||
name: ${SRC_VOL_PREFIX:-magistrala_magistrala-}clients-db-volume
|
||||
channels-src:
|
||||
external: true
|
||||
name: ${SRC_VOL_PREFIX:-magistrala_magistrala-}channels-db-volume
|
||||
groups-src:
|
||||
external: true
|
||||
name: ${SRC_VOL_PREFIX:-magistrala_magistrala-}groups-db-volume
|
||||
auth-src:
|
||||
external: true
|
||||
name: ${SRC_VOL_PREFIX:-magistrala_magistrala-}auth-db-volume
|
||||
re-src:
|
||||
external: true
|
||||
name: ${SRC_VOL_PREFIX:-magistrala_magistrala-}re-db-volume
|
||||
reports-src:
|
||||
external: true
|
||||
name: ${SRC_VOL_PREFIX:-magistrala_magistrala-}reports-db-volume
|
||||
atom-target:
|
||||
external: true
|
||||
name: ${ATOM_TARGET_VOLUME}
|
||||
@@ -0,0 +1,57 @@
|
||||
# Copyright (c) Abstract Machines
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Source DB connection details for the migrator when it runs inside the isolated
|
||||
# docker-compose.migrate.yaml network. Hosts are the Compose service names, so
|
||||
# this resolves only from a container attached to that network (which is how
|
||||
# migrate.sh runs it). The Atom target DSN is passed separately via
|
||||
# ATOM_DATABASE_URL. Override SRC_DB_USER/SRC_DB_PASS in migrate.sh if the old
|
||||
# deployment used non-default Postgres credentials.
|
||||
|
||||
MG_DOMAINS_DB_HOST=domains-db
|
||||
MG_DOMAINS_DB_PORT=5432
|
||||
MG_DOMAINS_DB_USER=magistrala
|
||||
MG_DOMAINS_DB_PASS=magistrala
|
||||
MG_DOMAINS_DB_NAME=domains
|
||||
|
||||
MG_USERS_DB_HOST=users-db
|
||||
MG_USERS_DB_PORT=5432
|
||||
MG_USERS_DB_USER=magistrala
|
||||
MG_USERS_DB_PASS=magistrala
|
||||
MG_USERS_DB_NAME=users
|
||||
|
||||
MG_CLIENTS_DB_HOST=clients-db
|
||||
MG_CLIENTS_DB_PORT=5432
|
||||
MG_CLIENTS_DB_USER=magistrala
|
||||
MG_CLIENTS_DB_PASS=magistrala
|
||||
MG_CLIENTS_DB_NAME=clients
|
||||
|
||||
MG_CHANNELS_DB_HOST=channels-db
|
||||
MG_CHANNELS_DB_PORT=5432
|
||||
MG_CHANNELS_DB_USER=magistrala
|
||||
MG_CHANNELS_DB_PASS=magistrala
|
||||
MG_CHANNELS_DB_NAME=channels
|
||||
|
||||
MG_GROUPS_DB_HOST=groups-db
|
||||
MG_GROUPS_DB_PORT=5432
|
||||
MG_GROUPS_DB_USER=magistrala
|
||||
MG_GROUPS_DB_PASS=magistrala
|
||||
MG_GROUPS_DB_NAME=groups
|
||||
|
||||
MG_AUTH_DB_HOST=auth-db
|
||||
MG_AUTH_DB_PORT=5432
|
||||
MG_AUTH_DB_USER=magistrala
|
||||
MG_AUTH_DB_PASS=magistrala
|
||||
MG_AUTH_DB_NAME=auth
|
||||
|
||||
MG_RE_DB_HOST=re-db
|
||||
MG_RE_DB_PORT=5432
|
||||
MG_RE_DB_USER=magistrala
|
||||
MG_RE_DB_PASS=magistrala
|
||||
MG_RE_DB_NAME=rules_engine
|
||||
|
||||
MG_REPORTS_DB_HOST=reports-db
|
||||
MG_REPORTS_DB_PORT=5432
|
||||
MG_REPORTS_DB_USER=magistrala
|
||||
MG_REPORTS_DB_PASS=magistrala
|
||||
MG_REPORTS_DB_NAME=reports
|
||||
Executable
+164
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright (c) Abstract Machines
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# One-command Magistrala v0.30.0 -> Atom migration.
|
||||
#
|
||||
# Brings up an isolated, collision-free stack (its own Compose project + private
|
||||
# network, no host ports, no fixed container names), seeds the Atom schema into
|
||||
# the SAME volume `make run_latest` uses, runs the migrator, then tears the stack
|
||||
# down leaving every volume intact. After it finishes you can simply:
|
||||
#
|
||||
# make run_latest
|
||||
#
|
||||
# and the new deployment serves the migrated data.
|
||||
#
|
||||
# Usage:
|
||||
# tools/atom-migration/migrate.sh # dry-run (reads + validates, writes nothing)
|
||||
# tools/atom-migration/migrate.sh --apply # perform the migration
|
||||
# tools/atom-migration/migrate.sh --verify # reconcile source vs Atom after apply
|
||||
# tools/atom-migration/migrate.sh --apply --keep # leave the stack running for debugging
|
||||
#
|
||||
# Env overrides:
|
||||
# DOCKER_PROJECT run_latest Compose project (default: derived like the Makefile)
|
||||
# SRC_VOL_PREFIX old DB volume name prefix (default: magistrala_magistrala-)
|
||||
# SRC_DB_USER/PASS old Postgres credentials (default: magistrala/magistrala)
|
||||
# MIGRATE_PROJECT isolated Compose project name (default: atommig)
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
COMPOSE_FILE="$SCRIPT_DIR/docker-compose.migrate.yaml"
|
||||
ENV_FILE="$REPO_ROOT/docker/.env"
|
||||
MIGRATE_PROJECT="${MIGRATE_PROJECT:-atommig}"
|
||||
SRC_VOL_PREFIX="${SRC_VOL_PREFIX:-magistrala_magistrala-}"
|
||||
|
||||
MIGRATOR_ARGS=()
|
||||
KEEP=false
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--keep) KEEP=true ;;
|
||||
--apply|--verify|--dry-run) MIGRATOR_ARGS+=("$arg") ;;
|
||||
--unmapped-action=*|--report-dir=*) MIGRATOR_ARGS+=("$arg") ;;
|
||||
-h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//' | head -40; exit 0 ;;
|
||||
*) echo "unknown argument: $arg" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
||||
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
command -v docker >/dev/null || die "docker not found"
|
||||
[[ -f "$ENV_FILE" ]] || die "missing $ENV_FILE"
|
||||
|
||||
# DOCKER_PROJECT: match the Makefile derivation so we target the volume that
|
||||
# `make run_latest` will mount.
|
||||
if [[ -z "${DOCKER_PROJECT:-}" ]]; then
|
||||
repo="$(git -C "$REPO_ROOT" remote get-url origin 2>/dev/null \
|
||||
| sed -E 's@.*/([^/]+)/([^/.]+)(\.git)?@\1_\2@')"
|
||||
DOCKER_PROJECT="$(echo "$repo" | sed -E 's/[^a-zA-Z0-9]/_/g' | tr '[:upper:]' '[:lower:]')"
|
||||
fi
|
||||
[[ -n "$DOCKER_PROJECT" ]] || die "could not determine DOCKER_PROJECT (set it explicitly)"
|
||||
ATOM_TARGET_VOLUME="${DOCKER_PROJECT}_magistrala-atom-db-volume"
|
||||
|
||||
log "Isolated project : $MIGRATE_PROJECT"
|
||||
log "Source volumes : ${SRC_VOL_PREFIX}<svc>-db-volume"
|
||||
log "Atom target vol : $ATOM_TARGET_VOLUME"
|
||||
|
||||
# --- preflight: every source volume must exist ---
|
||||
missing=()
|
||||
for svc in domains users clients channels groups auth re reports; do
|
||||
vol="${SRC_VOL_PREFIX}${svc}-db-volume"
|
||||
docker volume inspect "$vol" >/dev/null 2>&1 || missing+=("$vol")
|
||||
done
|
||||
((${#missing[@]} == 0)) || die "missing source volume(s): ${missing[*]}
|
||||
Run this on the machine whose stopped old Magistrala stack still has these
|
||||
volumes, or set SRC_VOL_PREFIX if the old Compose project used another name."
|
||||
|
||||
# Atom target volume: created here if absent so the seed step can write to it;
|
||||
# `make run_latest` reuses the same name.
|
||||
docker volume inspect "$ATOM_TARGET_VOLUME" >/dev/null 2>&1 || {
|
||||
log "Creating Atom target volume $ATOM_TARGET_VOLUME"
|
||||
docker volume create "$ATOM_TARGET_VOLUME" >/dev/null
|
||||
}
|
||||
|
||||
# --- detect source Postgres layout (mount point + PGDATA + image major) ---
|
||||
log "Detecting source Postgres layout"
|
||||
layout="$(docker run --rm -v "${SRC_VOL_PREFIX}users-db-volume":/d alpine:3.22 sh -c '
|
||||
if [ -f /d/PG_VERSION ]; then
|
||||
echo "classic $(cat /d/PG_VERSION) /"
|
||||
else
|
||||
p=$(find /d -maxdepth 4 -name PG_VERSION 2>/dev/null | head -1)
|
||||
[ -n "$p" ] && echo "nested $(cat "$p") ${p#/d/}" || echo "unknown 0 /"
|
||||
fi')"
|
||||
read -r kind major rel <<<"$layout"
|
||||
[[ "$kind" != "unknown" ]] || die "could not find PG_VERSION in source volume"
|
||||
if [[ "$kind" == "nested" ]]; then
|
||||
# rel = <major>/docker/PG_VERSION -> PGDATA dir = /var/lib/postgresql/<major>/docker
|
||||
subdir="$(dirname "/$rel")" # /<major>/docker
|
||||
SRC_MOUNT="/var/lib/postgresql"
|
||||
SRC_PGDATA="/var/lib/postgresql${subdir}"
|
||||
else
|
||||
SRC_MOUNT="/var/lib/postgresql/data"
|
||||
SRC_PGDATA="/var/lib/postgresql/data"
|
||||
fi
|
||||
SRC_PG_IMAGE="postgres:${major}-alpine"
|
||||
log "Source Postgres : $SRC_PG_IMAGE (mount $SRC_MOUNT, PGDATA $SRC_PGDATA)"
|
||||
|
||||
# --- build the migrator image ---
|
||||
log "Building migrator image"
|
||||
docker build -q -f "$SCRIPT_DIR/Dockerfile" -t magistrala/atom-migration:dev "$REPO_ROOT" >/dev/null
|
||||
|
||||
# Variables consumed by the compose file. docker/.env is passed via --env-file so
|
||||
# ATOM_DB_* / MG_RELEASE_TAG interpolate; these exports take precedence.
|
||||
export SRC_VOL_PREFIX SRC_PG_IMAGE SRC_MOUNT SRC_PGDATA ATOM_TARGET_VOLUME
|
||||
export SRC_DB_USER="${SRC_DB_USER:-magistrala}" SRC_DB_PASS="${SRC_DB_PASS:-magistrala}"
|
||||
export HOST_UID="$(id -u)" HOST_GID="$(id -g)"
|
||||
|
||||
dc() { docker compose --env-file "$ENV_FILE" -p "$MIGRATE_PROJECT" -f "$COMPOSE_FILE" "$@"; }
|
||||
|
||||
cleanup() {
|
||||
if [[ "$KEEP" == true ]]; then
|
||||
log "Leaving stack up (--keep). Tear down with:"
|
||||
echo " docker compose -p $MIGRATE_PROJECT -f $COMPOSE_FILE down"
|
||||
return
|
||||
fi
|
||||
log "Tearing down isolated stack (volumes are external and preserved)"
|
||||
dc down --remove-orphans >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# --- bring up source DBs + Atom DB, seed the Atom schema ---
|
||||
log "Starting source DBs, Atom DB and Atom schema seeder"
|
||||
dc up -d --wait domains-db users-db clients-db channels-db groups-db auth-db re-db reports-db atom-db
|
||||
dc up -d atom
|
||||
|
||||
log "Waiting for Atom to apply its schema into the target volume"
|
||||
for i in $(seq 1 60); do
|
||||
if dc exec -T atom-db psql -U "${ATOM_DB_USER:-atom}" -d "${ATOM_DB_NAME:-atom}" -tAc \
|
||||
"SELECT to_regclass('public.tenants') IS NOT NULL AND to_regclass('public.entities') IS NOT NULL;" 2>/dev/null \
|
||||
| grep -qx t; then
|
||||
log "Atom schema ready"
|
||||
break
|
||||
fi
|
||||
[[ $i -eq 60 ]] && die "Atom did not initialise its schema in time (check: dc logs atom)"
|
||||
sleep 2
|
||||
done
|
||||
|
||||
# --- run the migration ---
|
||||
log "Running migrator ${MIGRATOR_ARGS[*]:-(dry-run)}"
|
||||
set +e
|
||||
dc run --rm migrator "${MIGRATOR_ARGS[@]}"
|
||||
rc=$?
|
||||
set -e
|
||||
|
||||
echo
|
||||
if [[ $rc -eq 0 ]]; then
|
||||
log "Migrator finished OK. Report: tools/atom-migration/report/"
|
||||
if printf '%s\n' "${MIGRATOR_ARGS[@]}" | grep -qx -- --apply; then
|
||||
log "Next: stop the old stack, then 'make run_latest' to serve migrated data."
|
||||
fi
|
||||
else
|
||||
die "migrator exited $rc (see report in tools/atom-migration/report/)"
|
||||
fi
|
||||
Reference in New Issue
Block a user