Deploy GeoIntel as DockerMan native container
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
- Added `deploy/unraid/nginx-all-in-one.conf` with localhost backend proxying inside the same container.
|
- Added `deploy/unraid/nginx-all-in-one.conf` with localhost backend proxying inside the same container.
|
||||||
- Added a PNG DockerMan icon and made the Unraid template name match the running `geointel` container.
|
- Added a PNG DockerMan icon and made the Unraid template name match the running `geointel` container.
|
||||||
- Added DockerMan labels to the all-in-one Compose service so Unraid can associate the running container with web UI and icon metadata.
|
- Added DockerMan labels to the all-in-one Compose service so Unraid can associate the running container with web UI and icon metadata.
|
||||||
|
- Added `deploy/unraid/run-dockerman-container.sh` so repo deploys automatically replace Compose-owned containers with a DockerMan-native `geointel` container while preserving/migrating persisted data.
|
||||||
- Updated Tower deploy scripts to install `/boot/config/plugins/dockerMan/templates-user/my-geointel.xml` and `/boot/config/plugins/dockerMan/images/geointel-icon.png`.
|
- Updated Tower deploy scripts to install `/boot/config/plugins/dockerMan/templates-user/my-geointel.xml` and `/boot/config/plugins/dockerMan/images/geointel-icon.png`.
|
||||||
- Updated Tower deploy scripts to stop the old multi-container stack without removing volumes and start the all-in-one stack.
|
- Updated Tower deploy scripts to stop the old multi-container stack without removing volumes and start the all-in-one stack.
|
||||||
- Updated the Unraid template so the Docker can be edited from Unraid with one web port, storage path, PostGIS data path and app icon.
|
- Updated the Unraid template so the Docker can be edited from Unraid with one web port, storage path, PostGIS data path and app icon.
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ make readiness
|
|||||||
|
|
||||||
## Unraid / Tower deployment
|
## Unraid / Tower deployment
|
||||||
|
|
||||||
GeoIntel runs on Unraid as an all-in-one Docker container. The container embeds PostGIS, runs the FastAPI backend internally, and serves the frontend through nginx on one editable web port.
|
GeoIntel runs on Unraid as an all-in-one DockerMan-native container. The container embeds PostGIS, runs the FastAPI backend internally, and serves the frontend through nginx on one editable web port.
|
||||||
|
|
||||||
Unraid template assets live in:
|
Unraid template assets live in:
|
||||||
|
|
||||||
@@ -85,7 +85,8 @@ Copy the Unraid env template to `.env` in the checkout and edit ports/paths ther
|
|||||||
cd /mnt/user/appdata/geointel
|
cd /mnt/user/appdata/geointel
|
||||||
cp deploy/unraid/geointel.env.example .env
|
cp deploy/unraid/geointel.env.example .env
|
||||||
nano .env
|
nano .env
|
||||||
docker compose -f docker-compose.unraid.yml up -d --build
|
docker compose -f docker-compose.unraid.yml build geointel
|
||||||
|
bash deploy/unraid/run-dockerman-container.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Common editable values:
|
Common editable values:
|
||||||
@@ -98,7 +99,7 @@ GEOINTEL_POSTGIS_DATA_PATH=/mnt/user/appdata/geointel/postgres-data
|
|||||||
|
|
||||||
The backend and PostGIS ports are intentionally not exposed to the LAN in the all-in-one runtime. See `deploy/unraid/README.md` for full setup, port-change and cleanup notes.
|
The backend and PostGIS ports are intentionally not exposed to the LAN in the all-in-one runtime. See `deploy/unraid/README.md` for full setup, port-change and cleanup notes.
|
||||||
|
|
||||||
On Tower/Unraid, `scripts/deploy_tower.ps1` and `scripts/deploy_tower.sh` also install the editable DockerMan template as `/boot/config/plugins/dockerMan/templates-user/my-geointel.xml` and the PNG icon as `/boot/config/plugins/dockerMan/images/geointel-icon.png`.
|
On Tower/Unraid, `scripts/deploy_tower.ps1` and `scripts/deploy_tower.sh` automatically install the editable DockerMan template as `/boot/config/plugins/dockerMan/templates-user/my-geointel.xml`, install the PNG icon as `/boot/config/plugins/dockerMan/images/geointel-icon.png`, remove any old Compose-owned `geointel` container and start the final container with DockerMan labels.
|
||||||
|
|
||||||
## Sprint 2 quick start
|
## Sprint 2 quick start
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,9 @@ def test_unraid_readme_explains_port_changes_and_safe_cleanup() -> None:
|
|||||||
|
|
||||||
assert "cp deploy/unraid/geointel.env.example .env" in readme
|
assert "cp deploy/unraid/geointel.env.example .env" in readme
|
||||||
assert "GEOINTEL_FRONTEND_PORT=1203" in readme
|
assert "GEOINTEL_FRONTEND_PORT=1203" in readme
|
||||||
assert "docker compose -f docker-compose.unraid.yml up -d --build" in readme
|
assert "docker compose -f docker-compose.unraid.yml build geointel" in readme
|
||||||
|
assert "bash deploy/unraid/run-dockerman-container.sh" in readme
|
||||||
|
assert "net.unraid.docker.managed=dockerman" in readme
|
||||||
assert "curl -fsS" in readme
|
assert "curl -fsS" in readme
|
||||||
assert "docker builder prune -af" in readme
|
assert "docker builder prune -af" in readme
|
||||||
assert "Avoid broad volume pruning" in readme
|
assert "Avoid broad volume pruning" in readme
|
||||||
@@ -67,6 +69,7 @@ def test_unraid_readme_explains_port_changes_and_safe_cleanup() -> None:
|
|||||||
def test_unraid_all_in_one_runtime_starts_embedded_postgis_backend_and_nginx() -> None:
|
def test_unraid_all_in_one_runtime_starts_embedded_postgis_backend_and_nginx() -> None:
|
||||||
dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8")
|
dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8")
|
||||||
start_script = (ROOT / "deploy" / "unraid" / "all-in-one-start.sh").read_text(encoding="utf-8")
|
start_script = (ROOT / "deploy" / "unraid" / "all-in-one-start.sh").read_text(encoding="utf-8")
|
||||||
|
dockerman_script = (ROOT / "deploy" / "unraid" / "run-dockerman-container.sh").read_text(encoding="utf-8")
|
||||||
nginx_config = (ROOT / "deploy" / "unraid" / "nginx-all-in-one.conf").read_text(encoding="utf-8")
|
nginx_config = (ROOT / "deploy" / "unraid" / "nginx-all-in-one.conf").read_text(encoding="utf-8")
|
||||||
dockerignore = (ROOT / ".dockerignore").read_text(encoding="utf-8")
|
dockerignore = (ROOT / ".dockerignore").read_text(encoding="utf-8")
|
||||||
|
|
||||||
@@ -85,6 +88,10 @@ def test_unraid_all_in_one_runtime_starts_embedded_postgis_backend_and_nginx() -
|
|||||||
assert "python -m alembic upgrade head" in start_script
|
assert "python -m alembic upgrade head" in start_script
|
||||||
assert "uvicorn app.main:app --host 127.0.0.1 --port 8000 &" in start_script
|
assert "uvicorn app.main:app --host 127.0.0.1 --port 8000 &" in start_script
|
||||||
assert 'exec nginx -g "daemon off;"' in start_script
|
assert 'exec nginx -g "daemon off;"' in start_script
|
||||||
|
assert "docker run -d" in dockerman_script
|
||||||
|
assert "--label net.unraid.docker.managed=dockerman" in dockerman_script
|
||||||
|
assert "migrate_compose_volume_if_needed" in dockerman_script
|
||||||
|
assert "docker rm -f geointel" in dockerman_script
|
||||||
assert "proxy_pass http://127.0.0.1:8000/api/" in nginx_config
|
assert "proxy_pass http://127.0.0.1:8000/api/" in nginx_config
|
||||||
assert "proxy_pass http://127.0.0.1:8000/health" in nginx_config
|
assert "proxy_pass http://127.0.0.1:8000/health" in nginx_config
|
||||||
assert "location = /geointel-icon.png" in nginx_config
|
assert "location = /geointel-icon.png" in nginx_config
|
||||||
@@ -98,13 +105,18 @@ def test_tower_deploy_uses_single_container_unraid_compose() -> None:
|
|||||||
bash = (ROOT / "scripts" / "deploy_tower.sh").read_text(encoding="utf-8")
|
bash = (ROOT / "scripts" / "deploy_tower.sh").read_text(encoding="utf-8")
|
||||||
|
|
||||||
for script in (powershell, bash):
|
for script in (powershell, bash):
|
||||||
assert "/boot/config/plugins/dockerMan/templates-user/my-geointel.xml" in script
|
|
||||||
assert "/boot/config/plugins/dockerMan/images/geointel-icon.png" in script
|
|
||||||
assert "docker compose down --remove-orphans || true" in script
|
|
||||||
assert "docker compose -f docker-compose.unraid.yml config" in script
|
assert "docker compose -f docker-compose.unraid.yml config" in script
|
||||||
assert "docker compose -f docker-compose.unraid.yml build geointel" in script
|
assert "docker compose -f docker-compose.unraid.yml build geointel" in script
|
||||||
assert "docker compose -f docker-compose.unraid.yml up -d" in script
|
assert "bash deploy/unraid/run-dockerman-container.sh" in script
|
||||||
assert "COMPOSE_FILE=docker-compose.unraid.yml bash scripts/live_migration_smoke.sh" in script
|
assert "LIVE_SMOKE_CONTAINER=geointel bash scripts/live_migration_smoke.sh" in script
|
||||||
|
|
||||||
|
|
||||||
|
def test_live_migration_smoke_supports_dockerman_native_container() -> None:
|
||||||
|
script = (ROOT / "scripts" / "live_migration_smoke.sh").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert 'LIVE_SMOKE_CONTAINER="${LIVE_SMOKE_CONTAINER:-}"' in script
|
||||||
|
assert "run_container_smoke()" in script
|
||||||
|
assert 'docker exec -i "$container_name" sh' in script
|
||||||
|
|
||||||
|
|
||||||
def test_frontend_and_unraid_icon_assets_are_present() -> None:
|
def test_frontend_and_unraid_icon_assets_are_present() -> None:
|
||||||
|
|||||||
+12
-6
@@ -1,6 +1,6 @@
|
|||||||
# GeoIntel Unraid all-in-one container
|
# GeoIntel Unraid all-in-one container
|
||||||
|
|
||||||
GeoIntel can run on Unraid as one Docker container.
|
GeoIntel can run on Unraid as one DockerMan-native container.
|
||||||
|
|
||||||
Inside that single container:
|
Inside that single container:
|
||||||
|
|
||||||
@@ -25,9 +25,10 @@ http://<unraid-ip>:${GEOINTEL_FRONTEND_PORT}/geointel-icon.png
|
|||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
- `docker-compose.unraid.yml`: recommended single-container Compose stack.
|
- `docker-compose.unraid.yml`: build definition for the all-in-one image.
|
||||||
- `deploy/unraid/Dockerfile.all-in-one`: builds the single container.
|
- `deploy/unraid/Dockerfile.all-in-one`: builds the single container.
|
||||||
- `deploy/unraid/all-in-one-start.sh`: starts embedded PostGIS, backend and nginx.
|
- `deploy/unraid/all-in-one-start.sh`: starts embedded PostGIS, backend and nginx.
|
||||||
|
- `deploy/unraid/run-dockerman-container.sh`: starts/replaces the running container with DockerMan labels and editable Unraid metadata.
|
||||||
- `deploy/unraid/nginx-all-in-one.conf`: frontend and API proxy config for one container.
|
- `deploy/unraid/nginx-all-in-one.conf`: frontend and API proxy config for one container.
|
||||||
- `deploy/unraid/geointel.env.example`: copy to `.env` and edit ports/paths.
|
- `deploy/unraid/geointel.env.example`: copy to `.env` and edit ports/paths.
|
||||||
- `deploy/unraid/geointel-unraid-template.xml`: Unraid/DockerMan metadata for editable fields.
|
- `deploy/unraid/geointel-unraid-template.xml`: Unraid/DockerMan metadata for editable fields.
|
||||||
@@ -60,7 +61,7 @@ net.unraid.docker.icon=/boot/config/plugins/dockerMan/images/geointel-icon.png
|
|||||||
|
|
||||||
These labels are required because a plain Compose container can run correctly while still missing the normal Unraid edit/icon controls.
|
These labels are required because a plain Compose container can run correctly while still missing the normal Unraid edit/icon controls.
|
||||||
|
|
||||||
## First setup with Compose Manager
|
## First setup from the repo
|
||||||
|
|
||||||
From the Unraid shell:
|
From the Unraid shell:
|
||||||
|
|
||||||
@@ -71,9 +72,12 @@ cd /mnt/user/appdata/geointel
|
|||||||
cp deploy/unraid/geointel.env.example .env
|
cp deploy/unraid/geointel.env.example .env
|
||||||
nano .env
|
nano .env
|
||||||
docker compose -f docker-compose.unraid.yml config
|
docker compose -f docker-compose.unraid.yml config
|
||||||
docker compose -f docker-compose.unraid.yml up -d --build
|
docker compose -f docker-compose.unraid.yml build geointel
|
||||||
|
bash deploy/unraid/run-dockerman-container.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The repository deploy scripts run the same flow automatically. They build the image, install the DockerMan template/icon, remove any old Compose-owned `geointel` container, preserve/migrate the PostGIS data path and start the final container with DockerMan labels.
|
||||||
|
|
||||||
Validate:
|
Validate:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -95,7 +99,8 @@ GEOINTEL_CORS_ORIGINS=http://localhost:1203,http://127.0.0.1:1203,http://192.168
|
|||||||
Apply:
|
Apply:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose -f docker-compose.unraid.yml up -d --build
|
docker compose -f docker-compose.unraid.yml build geointel
|
||||||
|
bash deploy/unraid/run-dockerman-container.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
## Persistent paths
|
## Persistent paths
|
||||||
@@ -117,7 +122,8 @@ GEOINTEL_POSTGIS_DATA_PATH=/mnt/user/appdata/geointel/postgres-data
|
|||||||
cd /mnt/user/appdata/geointel
|
cd /mnt/user/appdata/geointel
|
||||||
git fetch origin main
|
git fetch origin main
|
||||||
git reset --hard origin/main
|
git reset --hard origin/main
|
||||||
docker compose -f docker-compose.unraid.yml up -d --build
|
docker compose -f docker-compose.unraid.yml build geointel
|
||||||
|
bash deploy/unraid/run-dockerman-container.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
## Safe cleanup
|
## Safe cleanup
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
if [ -f .env ]; then
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. ./.env
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
GEOINTEL_FRONTEND_PORT="${GEOINTEL_FRONTEND_PORT:-1202}"
|
||||||
|
GEOINTEL_STORAGE_PATH="${GEOINTEL_STORAGE_PATH:-/mnt/user/appdata/geointel/storage}"
|
||||||
|
GEOINTEL_POSTGIS_DATA_PATH="${GEOINTEL_POSTGIS_DATA_PATH:-/mnt/user/appdata/geointel/postgres-data}"
|
||||||
|
GEOINTEL_POSTGRES_DB="${GEOINTEL_POSTGRES_DB:-geointel}"
|
||||||
|
GEOINTEL_POSTGRES_USER="${GEOINTEL_POSTGRES_USER:-geointel}"
|
||||||
|
GEOINTEL_POSTGRES_PASSWORD="${GEOINTEL_POSTGRES_PASSWORD:-geointel}"
|
||||||
|
GEOINTEL_CORS_ORIGINS="${GEOINTEL_CORS_ORIGINS:-http://localhost:${GEOINTEL_FRONTEND_PORT},http://127.0.0.1:${GEOINTEL_FRONTEND_PORT},http://192.168.10.150:${GEOINTEL_FRONTEND_PORT}}"
|
||||||
|
GEOINTEL_MAX_UPLOAD_MB="${GEOINTEL_MAX_UPLOAD_MB:-500}"
|
||||||
|
|
||||||
|
install_dockerman_metadata() {
|
||||||
|
if [ -d /boot/config/plugins/dockerMan ]; then
|
||||||
|
mkdir -p /boot/config/plugins/dockerMan/templates-user /boot/config/plugins/dockerMan/images
|
||||||
|
cp deploy/unraid/geointel-unraid-template.xml /boot/config/plugins/dockerMan/templates-user/my-geointel.xml
|
||||||
|
cp deploy/unraid/geointel-icon.png /boot/config/plugins/dockerMan/images/geointel-icon.png
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
migrate_compose_volume_if_needed() {
|
||||||
|
if [ -f "${GEOINTEL_POSTGIS_DATA_PATH}/PG_VERSION" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local compose_volume_path
|
||||||
|
compose_volume_path="$(docker volume inspect geointel_geointel_postgis --format '{{ .Mountpoint }}' 2>/dev/null || true)"
|
||||||
|
if [ -z "$compose_volume_path" ] || [ ! -f "${compose_volume_path}/PG_VERSION" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Migrating existing Compose PostGIS volume to ${GEOINTEL_POSTGIS_DATA_PATH}..."
|
||||||
|
mkdir -p "$GEOINTEL_POSTGIS_DATA_PATH"
|
||||||
|
cp -a "${compose_volume_path}/." "$GEOINTEL_POSTGIS_DATA_PATH/"
|
||||||
|
}
|
||||||
|
|
||||||
|
install_dockerman_metadata
|
||||||
|
|
||||||
|
docker compose down --remove-orphans || true
|
||||||
|
|
||||||
|
if docker ps -a --format '{{.Names}}' | grep -qx geointel; then
|
||||||
|
docker rm -f geointel
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$GEOINTEL_STORAGE_PATH" "$GEOINTEL_POSTGIS_DATA_PATH"
|
||||||
|
migrate_compose_volume_if_needed
|
||||||
|
|
||||||
|
docker run -d \
|
||||||
|
--name geointel \
|
||||||
|
--restart unless-stopped \
|
||||||
|
--label net.unraid.docker.managed=dockerman \
|
||||||
|
--label 'net.unraid.docker.webui=http://[IP]:[PORT:80]/' \
|
||||||
|
--label net.unraid.docker.icon=/boot/config/plugins/dockerMan/images/geointel-icon.png \
|
||||||
|
-p "${GEOINTEL_FRONTEND_PORT}:80" \
|
||||||
|
-e GEOINTEL_POSTGRES_DB="$GEOINTEL_POSTGRES_DB" \
|
||||||
|
-e GEOINTEL_POSTGRES_USER="$GEOINTEL_POSTGRES_USER" \
|
||||||
|
-e GEOINTEL_POSTGRES_PASSWORD="$GEOINTEL_POSTGRES_PASSWORD" \
|
||||||
|
-e GEOINTEL_STORAGE_ROOT=/app/storage \
|
||||||
|
-e GEOINTEL_CORS_ORIGINS="$GEOINTEL_CORS_ORIGINS" \
|
||||||
|
-e GEOINTEL_MAX_UPLOAD_MB="$GEOINTEL_MAX_UPLOAD_MB" \
|
||||||
|
-v "${GEOINTEL_POSTGIS_DATA_PATH}:/var/lib/postgresql/data" \
|
||||||
|
-v "${GEOINTEL_STORAGE_PATH}:/app/storage" \
|
||||||
|
geointel-all-in-one:latest
|
||||||
|
|
||||||
|
docker ps --filter name=geointel
|
||||||
@@ -1518,3 +1518,15 @@ Changed:
|
|||||||
Expected Unraid behavior:
|
Expected Unraid behavior:
|
||||||
- Refresh the Unraid Docker page after deploy so DockerMan reloads the user template metadata.
|
- Refresh the Unraid Docker page after deploy so DockerMan reloads the user template metadata.
|
||||||
- The running `geointel` container should have template-backed editable fields and a PNG icon.
|
- The running `geointel` container should have template-backed editable fields and a PNG icon.
|
||||||
|
|
||||||
|
## Sprint 32 automatic DockerMan-native deploy follow-up (2026-06-17)
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- Added `deploy/unraid/run-dockerman-container.sh` as the single source of truth for the final Unraid container launch.
|
||||||
|
- Updated Tower deploy scripts so repository deploys build the image with Compose but start the final container with `docker run` and DockerMan labels.
|
||||||
|
- The launch script installs the DockerMan template/icon, removes old `geointel` containers, preserves/migrates the old Compose PostGIS volume when needed and starts the final `geointel` container as DockerMan-managed.
|
||||||
|
- Updated live migration smoke to support direct container execution through `LIVE_SMOKE_CONTAINER=geointel`.
|
||||||
|
|
||||||
|
Expected Unraid behavior:
|
||||||
|
- A deploy from the repo should no longer leave the final app as a plain Compose-owned container.
|
||||||
|
- The running `geointel` container should expose `net.unraid.docker.managed=dockerman`, web UI metadata and icon metadata immediately after deploy.
|
||||||
|
|||||||
@@ -33,20 +33,12 @@ git reset --hard 'origin/$RemoteBranch'
|
|||||||
git branch -M '$RemoteBranch'
|
git branch -M '$RemoteBranch'
|
||||||
chmod +x scripts/*.sh backend/docker_start.sh deploy/unraid/*.sh || true
|
chmod +x scripts/*.sh backend/docker_start.sh deploy/unraid/*.sh || true
|
||||||
|
|
||||||
if [ -d /boot/config/plugins/dockerMan ]; then
|
|
||||||
mkdir -p /boot/config/plugins/dockerMan/templates-user /boot/config/plugins/dockerMan/images
|
|
||||||
cp deploy/unraid/geointel-unraid-template.xml /boot/config/plugins/dockerMan/templates-user/my-geointel.xml
|
|
||||||
cp deploy/unraid/geointel-icon.png /boot/config/plugins/dockerMan/images/geointel-icon.png
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker compose down --remove-orphans || true
|
|
||||||
docker compose -f docker-compose.unraid.yml config >/dev/null
|
docker compose -f docker-compose.unraid.yml config >/dev/null
|
||||||
docker compose -f docker-compose.unraid.yml build geointel
|
docker compose -f docker-compose.unraid.yml build geointel
|
||||||
docker compose -f docker-compose.unraid.yml up -d
|
bash deploy/unraid/run-dockerman-container.sh
|
||||||
docker compose -f docker-compose.unraid.yml ps
|
|
||||||
|
|
||||||
if [ -x scripts/live_migration_smoke.sh ]; then
|
if [ -x scripts/live_migration_smoke.sh ]; then
|
||||||
COMPOSE_FILE=docker-compose.unraid.yml bash scripts/live_migration_smoke.sh
|
LIVE_SMOKE_CONTAINER=geointel bash scripts/live_migration_smoke.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -x scripts/verify_browser_runtime.sh ]; then
|
if [ -x scripts/verify_browser_runtime.sh ]; then
|
||||||
|
|||||||
+2
-10
@@ -34,20 +34,12 @@ git fetch origin "$REMOTE_BRANCH"
|
|||||||
git checkout -B "$REMOTE_BRANCH" "origin/$REMOTE_BRANCH"
|
git checkout -B "$REMOTE_BRANCH" "origin/$REMOTE_BRANCH"
|
||||||
chmod +x scripts/*.sh backend/docker_start.sh deploy/unraid/*.sh || true
|
chmod +x scripts/*.sh backend/docker_start.sh deploy/unraid/*.sh || true
|
||||||
|
|
||||||
if [[ -d /boot/config/plugins/dockerMan ]]; then
|
|
||||||
mkdir -p /boot/config/plugins/dockerMan/templates-user /boot/config/plugins/dockerMan/images
|
|
||||||
cp deploy/unraid/geointel-unraid-template.xml /boot/config/plugins/dockerMan/templates-user/my-geointel.xml
|
|
||||||
cp deploy/unraid/geointel-icon.png /boot/config/plugins/dockerMan/images/geointel-icon.png
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker compose down --remove-orphans || true
|
|
||||||
docker compose -f docker-compose.unraid.yml config >/dev/null
|
docker compose -f docker-compose.unraid.yml config >/dev/null
|
||||||
docker compose -f docker-compose.unraid.yml build geointel
|
docker compose -f docker-compose.unraid.yml build geointel
|
||||||
docker compose -f docker-compose.unraid.yml up -d
|
bash deploy/unraid/run-dockerman-container.sh
|
||||||
docker compose -f docker-compose.unraid.yml ps
|
|
||||||
|
|
||||||
if [[ -x scripts/live_migration_smoke.sh ]]; then
|
if [[ -x scripts/live_migration_smoke.sh ]]; then
|
||||||
COMPOSE_FILE=docker-compose.unraid.yml bash scripts/live_migration_smoke.sh
|
LIVE_SMOKE_CONTAINER=geointel bash scripts/live_migration_smoke.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -x scripts/verify_browser_runtime.sh ]]; then
|
if [[ -x scripts/verify_browser_runtime.sh ]]; then
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|||||||
PYTHON_BIN="${PYTHON_BIN:-}"
|
PYTHON_BIN="${PYTHON_BIN:-}"
|
||||||
LIVE_SMOKE_USE_DOCKER="${LIVE_SMOKE_USE_DOCKER:-auto}"
|
LIVE_SMOKE_USE_DOCKER="${LIVE_SMOKE_USE_DOCKER:-auto}"
|
||||||
COMPOSE_FILE_PATH="${COMPOSE_FILE:-docker-compose.yml}"
|
COMPOSE_FILE_PATH="${COMPOSE_FILE:-docker-compose.yml}"
|
||||||
|
LIVE_SMOKE_CONTAINER="${LIVE_SMOKE_CONTAINER:-}"
|
||||||
|
|
||||||
run_docker_smoke() {
|
run_docker_smoke() {
|
||||||
service_name="$1"
|
service_name="$1"
|
||||||
@@ -91,6 +92,98 @@ echo "== Live migration smoke passed =="
|
|||||||
CONTAINER_SCRIPT
|
CONTAINER_SCRIPT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_container_smoke() {
|
||||||
|
container_name="$1"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
echo "== GeoIntel live migration smoke =="
|
||||||
|
echo "Using Docker container: ${container_name}."
|
||||||
|
|
||||||
|
docker exec -i "$container_name" sh <<'CONTAINER_SCRIPT'
|
||||||
|
set -eu
|
||||||
|
cd /app
|
||||||
|
|
||||||
|
echo "Database URL is read from backend container settings / DATABASE_URL."
|
||||||
|
|
||||||
|
python - <<'PY'
|
||||||
|
import time
|
||||||
|
|
||||||
|
from sqlalchemy import text
|
||||||
|
from sqlalchemy.exc import OperationalError
|
||||||
|
|
||||||
|
from app.db.session import get_engine
|
||||||
|
|
||||||
|
last_error = None
|
||||||
|
for attempt in range(1, 61):
|
||||||
|
try:
|
||||||
|
with get_engine().connect() as connection:
|
||||||
|
connection.execute(text("SELECT 1"))
|
||||||
|
print(f"Database connection: ok after attempt {attempt}")
|
||||||
|
break
|
||||||
|
except OperationalError as exc:
|
||||||
|
last_error = exc
|
||||||
|
print(f"Database not ready yet ({attempt}/60): {exc}")
|
||||||
|
time.sleep(2)
|
||||||
|
else:
|
||||||
|
raise SystemExit(f"Database did not become ready: {last_error}")
|
||||||
|
PY
|
||||||
|
|
||||||
|
python -m alembic upgrade head
|
||||||
|
|
||||||
|
python - <<'PY'
|
||||||
|
from sqlalchemy import text
|
||||||
|
|
||||||
|
from app.db.session import get_engine
|
||||||
|
|
||||||
|
required_objects = [
|
||||||
|
"public.projects",
|
||||||
|
"public.areas",
|
||||||
|
"public.datasets",
|
||||||
|
"public.jobs",
|
||||||
|
"public.vector_features",
|
||||||
|
"public.quality_checks",
|
||||||
|
"public.metrics",
|
||||||
|
"public.analysis_runs",
|
||||||
|
"public.detections",
|
||||||
|
"public.segmentations",
|
||||||
|
"public.ix_vector_features_geometry",
|
||||||
|
"public.ix_detections_geometry",
|
||||||
|
"public.ix_segmentations_geometry",
|
||||||
|
]
|
||||||
|
|
||||||
|
with get_engine().connect() as connection:
|
||||||
|
postgis_version = connection.execute(text("SELECT PostGIS_Version()")).scalar()
|
||||||
|
print(f"PostGIS version: {postgis_version}")
|
||||||
|
|
||||||
|
missing = [
|
||||||
|
object_name
|
||||||
|
for object_name in required_objects
|
||||||
|
if connection.execute(text("SELECT to_regclass(:object_name)"), {"object_name": object_name}).scalar() is None
|
||||||
|
]
|
||||||
|
if missing:
|
||||||
|
raise SystemExit(f"Missing expected migrated schema objects: {', '.join(missing)}")
|
||||||
|
print("Required runtime schema objects: ok")
|
||||||
|
PY
|
||||||
|
|
||||||
|
HEAD_COUNT="$(python -m alembic heads | grep -c 'head')"
|
||||||
|
if [ "$HEAD_COUNT" -ne 1 ]; then
|
||||||
|
echo "Expected exactly one Alembic head, found $HEAD_COUNT" >&2
|
||||||
|
python -m alembic heads >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
python -m alembic heads
|
||||||
|
echo "== Live migration smoke passed =="
|
||||||
|
CONTAINER_SCRIPT
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -n "$LIVE_SMOKE_CONTAINER" ] && command -v docker >/dev/null 2>&1; then
|
||||||
|
if docker ps --format '{{.Names}}' | grep -qx "$LIVE_SMOKE_CONTAINER"; then
|
||||||
|
run_container_smoke "$LIVE_SMOKE_CONTAINER"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$LIVE_SMOKE_USE_DOCKER" != "0" ] && command -v docker >/dev/null 2>&1 && [ -f "$ROOT/$COMPOSE_FILE_PATH" ]; then
|
if [ "$LIVE_SMOKE_USE_DOCKER" != "0" ] && command -v docker >/dev/null 2>&1 && [ -f "$ROOT/$COMPOSE_FILE_PATH" ]; then
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
for service_name in geointel backend; do
|
for service_name in geointel backend; do
|
||||||
|
|||||||
Reference in New Issue
Block a user