Skip to content

Commit 0d32592

Browse files
committed
Fixing issues with Traefik config, adding backup to update script
1 parent 3e78900 commit 0d32592

4 files changed

Lines changed: 60 additions & 15 deletions

File tree

app/services/deployment.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import os
22
import re
3+
import tempfile
34
import yaml
45
import aiodocker
56
import logging
67
from datetime import datetime, timezone
7-
from sqlalchemy import select
8+
from sqlalchemy import or_, select
89
from sqlalchemy.ext.asyncio import AsyncSession
910
from redis.asyncio import Redis
1011
from arq.connections import ArqRedis
@@ -247,17 +248,28 @@ async def update_traefik_config(
247248
project: Project,
248249
db: AsyncSession,
249250
settings: Settings,
251+
*,
252+
include_deployment_ids: set[str] | None = None,
250253
) -> None:
251254
"""Update Traefik config for a project including domains."""
252255
path = os.path.join(settings.traefik_dir, f"project_{project.id}.yml")
253256

254257
# Get aliases
258+
include_ids = include_deployment_ids or set()
259+
if include_ids:
260+
where_clause = or_(
261+
Deployment.conclusion == "succeeded",
262+
Deployment.id.in_(list(include_ids)),
263+
)
264+
else:
265+
where_clause = Deployment.conclusion == "succeeded"
266+
255267
result = await db.execute(
256268
select(Alias)
257269
.join(Deployment, Alias.deployment_id == Deployment.id)
258270
.filter(
259271
Deployment.project_id == project.id,
260-
Deployment.conclusion == "succeeded",
272+
where_clause,
261273
)
262274
)
263275
aliases = result.scalars().all()
@@ -339,6 +351,12 @@ async def update_traefik_config(
339351
}
340352
}
341353

354+
# If there is nothing to configure, remove any stale config file.
355+
if not routers and not services and not middlewares:
356+
if os.path.exists(path):
357+
os.remove(path)
358+
return
359+
342360
# Write config
343361
os.makedirs(settings.traefik_dir, exist_ok=True)
344362
config = {"http": {"routers": routers}}
@@ -347,8 +365,20 @@ async def update_traefik_config(
347365
if middlewares:
348366
config["http"]["middlewares"] = middlewares
349367

350-
with open(path, "w") as f:
351-
yaml.dump(config, f, sort_keys=False, indent=2)
368+
# Write atomically so Traefik's file watcher never reads a partially-written YAML.
369+
fd, tmp_path = tempfile.mkstemp(
370+
prefix=f".{os.path.basename(path)}.", dir=settings.traefik_dir
371+
)
372+
try:
373+
with os.fdopen(fd, "w") as f:
374+
yaml.safe_dump(config, f, sort_keys=False, indent=2)
375+
os.replace(tmp_path, path)
376+
finally:
377+
try:
378+
if os.path.exists(tmp_path):
379+
os.remove(tmp_path)
380+
except OSError:
381+
pass
352382

353383
async def create(
354384
self,

app/workers/tasks/deployment.py

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -331,14 +331,6 @@ async def finalize_deployment(ctx, deployment_id: str):
331331
)
332332
return
333333

334-
await service.update_status(
335-
db,
336-
deployment,
337-
status="finalize",
338-
error=None,
339-
redis_client=redis_client,
340-
)
341-
342334
# Log a success message
343335
async with aiodocker.Docker(url=settings.docker_host) as docker_client:
344336
container = await docker_client.containers.get(deployment.container_id)
@@ -353,7 +345,10 @@ async def finalize_deployment(ctx, deployment_id: str):
353345
# Update Traefik dynamic config
354346
try:
355347
await DeploymentService().update_traefik_config(
356-
deployment.project, db, settings
348+
deployment.project,
349+
db,
350+
settings,
351+
include_deployment_ids={deployment.id},
357352
)
358353
except Exception as e:
359354
logger.error(f"{log_prefix} Failed to update Traefik config: {e}")

scripts/update-apply.sh

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Apply a fetched update: validate, pull images, rollout, migrate, and record vers
2020
--components <csv>
2121
Comma-separated list of services to update (${VALID_COMPONENTS//|/, })
2222
--full Full stack update (down whole stack, then up). Causes downtime
23+
--backup Create a backup before applying the update (recommended)
2324
--no-migrate Do not run DB migrations after app update
2425
--no-telemetry Do not send telemetry
2526
--yes, -y Non-interactive yes to prompts
@@ -30,7 +31,7 @@ USG
3031
}
3132

3233
# Parse CLI flags
33-
ref=""; comps=""; do_all=0; do_full=0; migrate=1; yes=0; skip_components=0; telemetry=1
34+
ref=""; comps=""; do_all=0; do_full=0; do_backup=0; migrate=1; yes=0; skip_components=0; telemetry=1
3435
[[ "${NO_TELEMETRY:-0}" == "1" ]] && telemetry=0
3536
while [[ $# -gt 0 ]]; do
3637
case "$1" in
@@ -49,6 +50,7 @@ while [[ $# -gt 0 ]]; do
4950
shift 2
5051
;;
5152
--full) do_full=1; shift ;;
53+
--backup) do_backup=1; shift ;;
5254
--no-migrate) migrate=0; shift ;;
5355
--no-telemetry) telemetry=0; shift ;;
5456
--yes|-y) yes=1; shift ;;
@@ -240,6 +242,19 @@ elif [[ -z "$comps" ]] && ((do_all==0)) && ((do_full==0)) && [[ -n "$meta_compon
240242
comps="$meta_components"
241243
fi
242244

245+
# Backup (recommended)
246+
if ((do_backup==1)); then
247+
printf '\n'
248+
run_cmd "Creating backup" bash "$SCRIPT_DIR/backup.sh"
249+
elif ((yes!=1)) && [[ -t 0 ]]; then
250+
printf '\n'
251+
read -r -p "Create a backup before updating? [Y/n]: " ans
252+
if [[ -z "${ans:-}" || "$ans" =~ ^[Yy]([Ee][Ss])?$ ]]; then
253+
printf '\n'
254+
run_cmd "Creating backup" bash "$SCRIPT_DIR/backup.sh"
255+
fi
256+
fi
257+
243258
# Full stack update helper (with downtime)
244259
full_update() {
245260
printf '\n'

scripts/update.sh

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Update /dev/push by Git tag; performs rollouts (blue-green rollouts or simple re
2525
--components <csv>
2626
Comma-separated list of services (${VALID_COMPONENTS//|/, })
2727
--full Full stack update (down whole stack, then up). Causes downtime
28+
--backup Create a backup before applying the update (recommended)
2829
--no-migrate Do not run DB migrations after app update
2930
--no-telemetry Do not send telemetry
3031
--yes, -y Non-interactive yes to prompts
@@ -35,7 +36,7 @@ USG
3536
}
3637

3738
# Parse CLI flags
38-
ref=""; comps=""; do_all=0; do_full=0; migrate=1; yes=0; telemetry=1
39+
ref=""; comps=""; do_all=0; do_full=0; do_backup=0; migrate=1; yes=0; telemetry=1
3940
[[ "${NO_TELEMETRY:-0}" == "1" ]] && telemetry=0
4041
while [[ $# -gt 0 ]]; do
4142
case "$1" in
@@ -54,6 +55,7 @@ while [[ $# -gt 0 ]]; do
5455
shift 2
5556
;;
5657
--full) do_full=1; shift ;;
58+
--backup) do_backup=1; shift ;;
5759
--no-migrate) migrate=0; shift ;;
5860
--no-telemetry) telemetry=0; shift ;;
5961
--yes|-y) yes=1; shift ;;
@@ -127,6 +129,9 @@ elif [[ -n "$comps" ]]; then
127129
elif ((do_full==1)); then
128130
apply_args+=(--full)
129131
fi
132+
if ((do_backup==1)); then
133+
apply_args+=(--backup)
134+
fi
130135
if ((migrate==0)); then
131136
apply_args+=(--no-migrate)
132137
fi

0 commit comments

Comments
 (0)