feat(docker): pull-check + comparaison déterministe par stack (tâche 2 SJ-5)
- template docker/pull-check.sh.tpl (pull sans up, inspect before/after) - dockerPull: parseDockerPullCheck + buildDockerPullResult (TDD) — compare image id/digest/label OCI → services up_to_date|updates_available|error, changes operation=pulled ; erreurs registry nettoyées (URL/token/password) - dockerDedupKey (digests prioritaires, fallback image ids) + DockerImageChange.dedupKey - pullCheckStack: SSH + upsert docker_stack_services, refuse stack non enabled, refresh Docker séparé (hors refreshMachine, pas de pull auto) - execute: runAction(opts.stackId), branche docker_pull_check, injection stackDir (corrige docker_inspect_current) ; route: allowlist Docker passifs + pull_check, destructives toujours hors API jusqu'à action_requests (SJ-6) Pas de migration (schéma SJ-4 suffisant). tsc 0 erreur · 85 tests · build OK. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
# Tâche 2 — SJ-5 : Docker pull-check + comparaison déterministe
|
||||
|
||||
> Statut : **implémenté** (2026-06-05). tsc 0 erreur · 85 tests · build OK.
|
||||
> Réf. design : `docs/design/tache2/20-docker.md §4.3`, `40-contrats-json.md §3/§6`, `80-sous-jalons.md` SJ-5.
|
||||
|
||||
## Périmètre livré
|
||||
|
||||
Télécharger les images candidates d'un stack Compose **sans démarrer de conteneur**
|
||||
(`docker compose pull`), comparer avant/après par image ID + repo digest + label OCI,
|
||||
et persister l'état des services — **sans toucher au flux jalon 1** et sans déclencher
|
||||
de pull automatique (action manuelle par stack, non incluse dans `refreshMachine`).
|
||||
|
||||
## Composants
|
||||
|
||||
- **Template** `templates/docker/pull-check.sh.tpl` — délimiteurs Mustache `<% %>`
|
||||
(`<%stackDir%>`), Go-templates `{{.Id}}` / `{{join .RepoDigests ","}}` préservés.
|
||||
Sections `===SU:DOCKER_INSPECT_BEFORE/PULL/INSPECT_AFTER===` + `===SU:EXIT=N===`.
|
||||
- **`server/services/dockerPull.ts`** :
|
||||
- `parseDockerPullCheck(raw)` — lit BEFORE/AFTER (id, digest, version), code de sortie,
|
||||
et extrait les erreurs de pull **nettoyées de tout secret** (URLs, token/bearer/password).
|
||||
- `buildDockerPullResult(stackName, raw)` — comparaison déterministe → `services`
|
||||
(`up_to_date | updates_available | error` par image) + `changes` (`operation:"pulled"`
|
||||
uniquement pour les images modifiées) + `status` global + `errors`.
|
||||
- `dockerDedupKey(image, fromDigest, toDigest, fromId?, toId?)` — empreinte fonctionnelle
|
||||
(digests prioritaires, fallback image IDs), conforme `40 §6`.
|
||||
- `pullCheckStack(machineId, stackId, onData?)` — orchestration SSH + upsert des services
|
||||
dans `docker_stack_services` (par `stackId + serviceName`), maj `lastUpdateAt` du stack
|
||||
et `lastPullCheckAt` des settings. **Refuse un stack non `enabled`.**
|
||||
- **`server/services/dockerPull.test.ts`** — 7 cas (parse, nettoyage secret registry,
|
||||
classement up_to_date/updates_available, change unique, status global, dédup).
|
||||
- **Wiring** :
|
||||
- `runAction(machineId, action, opts?: { stackId })` — branche dédiée `docker_pull_check`
|
||||
(archivage report/log, `ExecutionResult.docker.pull.changes` + `dedupKey`, event).
|
||||
- Chemin générique : injection `stackDir` quand `stackId` fourni → **corrige aussi
|
||||
`docker_inspect_current`** (SJ-4 le déclarait sans orchestration par stack).
|
||||
- `POST /:id/actions` — allowlist élargie aux actions Docker passives/non-applicatives
|
||||
(`docker_scan`, `docker_inspect_current`, `docker_pull_check`) ; `stackId` requis pour
|
||||
les actions par-stack. **Destructives (apply/down/prune agressif) toujours hors API**
|
||||
jusqu'au socle `action_requests` (SJ-6).
|
||||
- **`shared/types.ts`** : `DockerImageChange.dedupKey?` (additif, pour mutualisation Hermes).
|
||||
|
||||
## Pas de migration
|
||||
|
||||
Le schéma SJ-4 (`docker_stack_services` avec `current/candidate_image_id|digest`,
|
||||
`version_label`, `status` ; `docker_settings.last_pull_check_at`) couvrait déjà SJ-5.
|
||||
|
||||
## Sécurité
|
||||
|
||||
- `docker compose pull` sans `up` → aucun conteneur recréé (pré-check pur applicatif).
|
||||
- Erreurs registry (`registry_auth_failed` / `pull_failed`) **nettoyées** : ni URL, ni token,
|
||||
ni mot de passe ne remontent vers UI/MCP (test dédié).
|
||||
- Credentials registry (`~/.docker/config.json`) jamais lus ni renvoyés.
|
||||
|
||||
## Reste pour SJ-6
|
||||
|
||||
`docker_compose_apply` (up -d --remove-orphans), `docker_prune_images`, `docker_compose_down`,
|
||||
table `docker_image_events`, et validation UI explicite via `action_requests`.
|
||||
Reference in New Issue
Block a user