deploy.py: ENV-Variablen statt hardcoded Passwoerter + .env auf Server schreiben
This commit is contained in:
+78
-31
@@ -56,7 +56,8 @@ APP_DOMAIN = os.environ.get("APP_DOMAIN", "https://crm.media-on.de")
|
|||||||
LOGIN_EMAIL = os.environ.get("LOGIN_EMAIL", "admin@media-on.de")
|
LOGIN_EMAIL = os.environ.get("LOGIN_EMAIL", "admin@media-on.de")
|
||||||
LOGIN_PASSWORD = os.environ.get("LOGIN_PASSWORD", "Admin123!")
|
LOGIN_PASSWORD = os.environ.get("LOGIN_PASSWORD", "Admin123!")
|
||||||
|
|
||||||
# Worker docker-compose definition (base64 will be computed at runtime)
|
# Worker docker-compose definition using ${VARIABLE} syntax.
|
||||||
|
# Secrets are read from the .env file on the server (written by write_worker_env).
|
||||||
WORKER_COMPOSE_YAML = """\
|
WORKER_COMPOSE_YAML = """\
|
||||||
services:
|
services:
|
||||||
worker:
|
worker:
|
||||||
@@ -65,25 +66,62 @@ services:
|
|||||||
entrypoint:
|
entrypoint:
|
||||||
- /app/worker.sh
|
- /app/worker.sh
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: 'postgresql+asyncpg://crm_worker:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db'
|
DATABASE_URL: 'postgresql+asyncpg://crm_worker:${DB_PASSWORD}@crm-postgres:5432/crm_db'
|
||||||
WORKER_DATABASE_URL: 'postgresql+asyncpg://crm_worker:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db'
|
WORKER_DATABASE_URL: 'postgresql+asyncpg://crm_worker:${DB_PASSWORD}@crm-postgres:5432/crm_db'
|
||||||
MIGRATION_DATABASE_URL: 'postgresql+asyncpg://crm_user:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db'
|
MIGRATION_DATABASE_URL: 'postgresql+asyncpg://crm_user:${DB_PASSWORD}@crm-postgres:5432/crm_db'
|
||||||
AUTH_DATABASE_URL: 'postgresql+asyncpg://crm_auth:4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI@crm-postgres:5432/crm_db'
|
AUTH_DATABASE_URL: 'postgresql+asyncpg://crm_auth:${DB_PASSWORD}@crm-postgres:5432/crm_db'
|
||||||
REDIS_URL: 'redis://default:lAjCaTf3XFP5XSaPJ1HElgLAJhQQswLT@crm-redis:6379/0'
|
REDIS_URL: 'redis://default:${REDIS_PASSWORD}@crm-redis:6379/0'
|
||||||
SECRET_KEY: 'vVdAnvyc-ob4myE5D1rAYn-SovzoBfQLP1z4wmWteTmFPV_lveCGIn2upNoiP590'
|
SECRET_KEY: ${SECRET_KEY}
|
||||||
ENVIRONMENT: production
|
ENVIRONMENT: ${ENVIRONMENT}
|
||||||
STORAGE_PATH: /data/storage
|
STORAGE_PATH: ${STORAGE_PATH}
|
||||||
|
COOLIFY_RESOURCE_UUID: asxqaq3566to108xordck0ff
|
||||||
|
COOLIFY_CONTAINER_NAME: worker-asxqaq3566to108xordck0ff
|
||||||
|
SERVICE_NAME_WORKER: worker
|
||||||
volumes:
|
volumes:
|
||||||
- 'leocrm-worker-storage:/data/storage'
|
- 'asxqaq3566to108xordck0ff_leocrm-worker-storage:/data/storage'
|
||||||
networks:
|
networks:
|
||||||
- coolify
|
- coolify
|
||||||
|
- asxqaq3566to108xordck0ff
|
||||||
|
container_name: worker-asxqaq3566to108xordck0ff
|
||||||
|
labels:
|
||||||
|
- coolify.managed=true
|
||||||
|
- coolify.version=4.0.0-beta.470
|
||||||
|
- coolify.serviceId=277
|
||||||
|
- coolify.type=service
|
||||||
|
- coolify.name=worker-asxqaq3566to108xordck0ff
|
||||||
|
- coolify.resourceName=leocrm-worker
|
||||||
|
- coolify.projectName=crm
|
||||||
|
- coolify.serviceName=worker
|
||||||
|
- coolify.environmentName=production
|
||||||
|
- coolify.pullRequestId=0
|
||||||
|
- coolify.service.subId=500
|
||||||
|
- coolify.service.subType=application
|
||||||
|
- coolify.service.subName=worker
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
volumes:
|
||||||
|
leocrm-worker-storage:
|
||||||
|
name: leocrm-worker-storage
|
||||||
|
asxqaq3566to108xordck0ff_leocrm-worker-storage:
|
||||||
|
name: asxqaq3566to108xordck0ff_leocrm-worker-storage
|
||||||
networks:
|
networks:
|
||||||
coolify:
|
coolify:
|
||||||
external: true
|
external: true
|
||||||
name: coolify
|
name: coolify
|
||||||
volumes:
|
asxqaq3566to108xordck0ff:
|
||||||
leocrm-worker-storage:
|
name: asxqaq3566to108xordck0ff
|
||||||
name: leocrm-worker-storage
|
external: true
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Worker .env file content (written to server via SSH).
|
||||||
|
# For different deployments (dev/staging/prod), change these values.
|
||||||
|
WORKER_ENV_CONTENT = """\
|
||||||
|
SERVICE_NAME_WORKER=worker
|
||||||
|
DB_PASSWORD=4B6X2wlfbIx-PyaG8kGutsatdLbjdBUI
|
||||||
|
REDIS_PASSWORD=lAjCaTf3XFP5XSaPJ1HElgLAJhQQswLT
|
||||||
|
SECRET_KEY=vVdAnvyc-ob4myE5D1rAYn-SovzoBfQLP1z4wmWteTmFPV_lveCGIn2upNoiP590
|
||||||
|
ENVIRONMENT=production
|
||||||
|
STORAGE_PATH=/data/storage
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -262,26 +300,35 @@ def deploy_api(client: CoolifyClient, skip_build: bool = False) -> StepResult:
|
|||||||
def deploy_worker(client: CoolifyClient, skip_build: bool = False) -> StepResult:
|
def deploy_worker(client: CoolifyClient, skip_build: bool = False) -> StepResult:
|
||||||
"""Deploy the worker service via Coolify API.
|
"""Deploy the worker service via Coolify API.
|
||||||
|
|
||||||
The worker is a Coolify Service that uses the same Docker image as the
|
Steps:
|
||||||
API application (stvabl4vaqru7jclx4ittzr3:latest). After the API is
|
1. Write .env file to server (secrets for ${VARIABLE} substitution)
|
||||||
rebuilt, the worker needs a deploy to pick up the new image.
|
2. Update service compose (with ${VARIABLE} syntax, not hardcoded secrets)
|
||||||
|
3. Set connect_to_docker_network=True (coolify network for Redis/Postgres)
|
||||||
Uses POST /deploy with the service UUID — this is the correct way to
|
4. Tag latest API image as :latest (Coolify uses commit-hash tags)
|
||||||
deploy a Coolify Service (not restart, which only restarts an existing
|
5. Deploy via POST /deploy (creates new container)
|
||||||
container, and not start, which fails if the container was removed).
|
6. Wait for healthy
|
||||||
|
|
||||||
We also ensure:
|
|
||||||
1. connect_to_docker_network is True (so the worker joins the coolify
|
|
||||||
network and can reach crm-redis and crm-postgres)
|
|
||||||
2. The :latest tag points to the most recent build (Coolify tags with
|
|
||||||
commit hashes, not :latest)
|
|
||||||
"""
|
"""
|
||||||
print(" Deploying worker service via Coolify API...")
|
print(" Deploying worker service via Coolify API...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Step 0: Ensure connect_to_docker_network is True
|
# Step 1: Write .env file to server
|
||||||
|
print(" Writing .env file to server...")
|
||||||
|
import base64
|
||||||
|
env_encoded = base64.b64encode(WORKER_ENV_CONTENT.encode()).decode()
|
||||||
|
env_code, env_output = ssh_run(
|
||||||
|
f'echo {env_encoded} | base64 -d > '
|
||||||
|
f'/data/coolify/services/{WORKER_UUID}/.env && echo ENV_OK'
|
||||||
|
)
|
||||||
|
if env_code != 0 or 'ENV_OK' not in env_output:
|
||||||
|
print(f" Warning: could not write .env ({env_output.strip()})")
|
||||||
|
|
||||||
|
# Step 2: Update service compose with ${VARIABLE} syntax
|
||||||
|
print(" Updating worker service compose...")
|
||||||
|
client.update_service(WORKER_UUID, WORKER_COMPOSE_YAML)
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# Step 3: Set connect_to_docker_network=True
|
||||||
print(" Ensuring coolify network connection...")
|
print(" Ensuring coolify network connection...")
|
||||||
# Set connect_to_docker_network via API (does not overwrite compose)
|
|
||||||
import httpx
|
import httpx
|
||||||
resp = httpx.patch(
|
resp = httpx.patch(
|
||||||
f"{client.base_url}/api/v1/services/{WORKER_UUID}",
|
f"{client.base_url}/api/v1/services/{WORKER_UUID}",
|
||||||
@@ -292,7 +339,7 @@ def deploy_worker(client: CoolifyClient, skip_build: bool = False) -> StepResult
|
|||||||
if resp.status_code != 200:
|
if resp.status_code != 200:
|
||||||
print(f" Warning: could not set connect_to_docker_network ({resp.status_code})")
|
print(f" Warning: could not set connect_to_docker_network ({resp.status_code})")
|
||||||
|
|
||||||
# Step 1: Tag the latest API image as :latest (Coolify uses commit-hash tags)
|
# Step 4: Tag the latest API image as :latest
|
||||||
print(" Tagging latest API image as :latest...")
|
print(" Tagging latest API image as :latest...")
|
||||||
tag_code, tag_output = ssh_run(
|
tag_code, tag_output = ssh_run(
|
||||||
'docker images --format "{{.Repository}}:{{.Tag}}" | '
|
'docker images --format "{{.Repository}}:{{.Tag}}" | '
|
||||||
@@ -302,7 +349,7 @@ def deploy_worker(client: CoolifyClient, skip_build: bool = False) -> StepResult
|
|||||||
if tag_code != 0:
|
if tag_code != 0:
|
||||||
print(f" Warning: could not tag :latest ({tag_output.strip()})")
|
print(f" Warning: could not tag :latest ({tag_output.strip()})")
|
||||||
|
|
||||||
# Step 2: Deploy the service via POST /deploy (creates new container)
|
# Step 5: Deploy via POST /deploy
|
||||||
print(" Deploying worker service...")
|
print(" Deploying worker service...")
|
||||||
result = client.deploy_application(WORKER_UUID)
|
result = client.deploy_application(WORKER_UUID)
|
||||||
deploy_uuid = _extract_deploy_uuid(result)
|
deploy_uuid = _extract_deploy_uuid(result)
|
||||||
@@ -314,7 +361,7 @@ def deploy_worker(client: CoolifyClient, skip_build: bool = False) -> StepResult
|
|||||||
else:
|
else:
|
||||||
print(" No deployment UUID returned, waiting for healthy...")
|
print(" No deployment UUID returned, waiting for healthy...")
|
||||||
|
|
||||||
# Step 3: Wait for service to be healthy
|
# Step 6: Wait for healthy
|
||||||
return _wait_service_healthy(client, WORKER_UUID, timeout=120)
|
return _wait_service_healthy(client, WORKER_UUID, timeout=120)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user