fix(agent): v0.3.10 cleanup orphan instance dirs on startup

- Add cleanupOrphanInstanceDirs() to remove leftover instance directories
  after failed deletes (common on Windows when compose.log is locked)
- Log RemoveAll errors in dockerComposeRm for better visibility
- Bump version to 0.3.10 and rebuild binaries
This commit is contained in:
EduBox Dev
2026-06-27 21:36:02 +00:00
parent e946b22a42
commit 33d89c66c0
5 changed files with 54 additions and 12 deletions
+11 -10
View File
@@ -426,11 +426,11 @@ Lagent est servi par Caddy depuis le dossier `agent/` monté dans le conteneu
### Binaires disponibles
- **Windows (archive complète)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.9-windows.zip`
- **Windows (archive complète)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.10-windows.zip`
- Contient `studioE5-agent.exe` + `tailscale-bin/windows/` (`tailscale.exe`, `tailscaled.exe`, `wintun.dll`) + `README-Windows.txt`.
- **Windows (exécutable seul)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.9.exe`
- **Windows (exécutable seul)** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.10.exe`
- Nécessite davoir installé Tailscale Windows séparément ou davoir les binaires dans `tailscale-bin/windows/`.
- **Linux** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.9`
- **Linux** : `https://studioe5.edudeploy.com/studioE5-agent-v0.3.10`
### Builder / préparer les binaires
@@ -444,13 +444,13 @@ cd /opt/studioe5-client-a/agent
./build.sh
```
Le `build.sh` génère automatiquement `studioE5-agent-v0.3.9-windows.zip` et copie les binaires versionnés dans `server/public/`.
Le `build.sh` génère automatiquement `studioE5-agent-v0.3.10-windows.zip` et copie les binaires versionnés dans `server/public/`.
### Flow dactivation zéro-config (modèle commercialisable)
L’élève/employé na **aucune configuration technique** à saisir :
1. **Télécharger** lagent Windows (`studioE5-agent-v0.3.9-windows.zip`).
1. **Télécharger** lagent Windows (`studioE5-agent-v0.3.10-windows.zip`).
2. **Extraire** et **lancer** `studioE5-agent.exe`.
3. **Entrer le code dactivation** à 6 caractères fourni par l’établissement (affiché dans lUI locale `http://localhost:7070`).
4. Lagent contacte le serveur, le serveur vérifie le code et renvoie **automatiquement** :
@@ -778,7 +778,7 @@ Créer un package dinstallation unique et professionnel par OS, incluant l
### Mise à jour de lagent vs dépendances système
- **Lagent peut se mettre à jour lui-même** (binaire + fichiers) depuis la v0.3.9.
- **Lagent peut se mettre à jour lui-même** (binaire + fichiers) depuis la v0.3.10.
- **Podman / Docker / Tailscale restent gérés par linstallateur** : lagent vérifie leur présence et alertera lutilisateur si une dépendance est manquante ou trop ancienne, mais ne les met pas à jour automatiquement (droits élevés, risque de casser les machines Podman, etc.).
---
@@ -806,10 +806,11 @@ Créer un package dinstallation unique et professionnel par OS, incluant l
- [x] **Nettoyer les instances/agent de test** (2026-06-27) : agent de test arrêté (`vps-8fc665eb`), `tailscaled` associé arrêté, data-dir `/tmp/studioe5-test-clienta` supprimé ; **13 instances de test supprimées de la base PostgreSQL** (`vps-8fc665eb` + `OMEGA-GAMER-60d7f87c`).
- [x] **Nettoyer les anciens nodes/volumes Headscale de test** (2026-06-27) : nœuds `edubox`, `prof`, `invalid-*`, anciens `vps-8fc665eb`, anciens `studioe5-resolver` et `test-node-b` supprimés ; volume Docker anonyme orphelin supprimé.
- [x] **Centralisation de la version agent** : fichier unique `agent/VERSION`, API `GET /api/agent/version`, dashboard et route `/api/download` alignés.
- [x] **Agent v0.3.9 synchronisation agent ↔ serveur au démarrage** : protocole `sync` / `sync_response`, suppression/lancement automatique des instances décalées pendant un offline.
- [x] **Agent v0.3.9 détails techniques dans lUI locale** : version de lagent, nodeId, version attendue par le serveur, notification de mise à jour.
- [x] **Agent v0.3.9 mise à jour automatique de lagent** : détection de nouvelle version, téléchargement, remplacement du binaire via script helper et redémarrage.
- [x] **Agent v0.3.9 handlers asynchrones** : `start`, `stop`, `delete`, `reset` exécutés dans des goroutines pour ne plus bloquer la boucle WebSocket.
- [x] **Agent v0.3.10 synchronisation agent ↔ serveur au démarrage** : protocole `sync` / `sync_response`, suppression/lancement automatique des instances décalées pendant un offline.
- [x] **Agent v0.3.10 détails techniques dans lUI locale** : version de lagent, nodeId, version attendue par le serveur, notification de mise à jour.
- [x] **Agent v0.3.10 mise à jour automatique de lagent** : détection de nouvelle version, téléchargement, remplacement du binaire via script helper et redémarrage.
- [x] **Agent v0.3.10 handlers asynchrones** : `start`, `stop`, `delete`, `reset` exécutés dans des goroutines pour ne plus bloquer la boucle WebSocket.
- [x] **Agent v0.3.10 nettoyage des dossiers instances orphelins au démarrage** : supprime les répertoires résiduels laissés par des `delete` incomplets (souvent `compose.log` verrouillé sous Windows).
### ⏳ Reste à faire
+1 -1
View File
@@ -1 +1 @@
0.3.9
0.3.10
+6 -1
View File
@@ -2,6 +2,7 @@ package main
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
@@ -92,7 +93,11 @@ func dockerComposeRm(dataDir, instanceID string) error {
if err := cmd.Run(); err != nil {
return err
}
return os.RemoveAll(dir)
if err := os.RemoveAll(dir); err != nil {
log.Printf("dockerComposeRm: failed to remove %s: %v (will retry on next startup)", dir, err)
return err
}
return nil
}
// extractPublicURL tries to find the public URL from a WordPress compose config.
+32
View File
@@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
@@ -130,3 +131,34 @@ func getInstanceStatus(dataDir, instanceID string) string {
}
return "stopped"
}
// cleanupOrphanInstanceDirs removes instance directories that have no entry in
// instances.json. This typically happens on Windows when a delete operation
// could not fully remove the directory because compose.log was locked.
func cleanupOrphanInstanceDirs(dataDir string) {
instancesDir := filepath.Join(dataDir, "instances")
inst, err := loadInstances(dataDir)
if err != nil {
log.Printf("cleanupOrphanInstanceDirs: loadInstances error: %v", err)
return
}
entries, err := os.ReadDir(instancesDir)
if err != nil {
if !os.IsNotExist(err) {
log.Printf("cleanupOrphanInstanceDirs: ReadDir error: %v", err)
}
return
}
for _, entry := range entries {
if !entry.IsDir() {
continue
}
if _, ok := inst[entry.Name()]; !ok {
dir := filepath.Join(instancesDir, entry.Name())
log.Printf("cleanupOrphanInstanceDirs: removing orphan directory %s", dir)
if err := os.RemoveAll(dir); err != nil {
log.Printf("cleanupOrphanInstanceDirs: RemoveAll error for %s: %v", dir, err)
}
}
}
}
+4
View File
@@ -62,6 +62,10 @@ func main() {
log.Printf("[%s Agent] version=%s node=%s data-dir=%s server=%s", APP_NAME, version, cfg.NodeID, *dataDir, cfg.Server)
// Clean up instance directories left behind by failed deletes (common on
// Windows when compose.log is locked during removal).
cleanupOrphanInstanceDirs(*dataDir)
// Ensure Podman machine DNS is configured on Windows/macOS so images can be
// pulled and containers can reach the internet.
ensurePodmanMachineDNS()