Files
Loki/internal/loki/sys_service_rust_linux.go
T
MichaelandClaude Opus 5.5 a672760f3c Façade Rust, lot 3 : la supervision du moteur passe à loki-rs
En conteneur, start/stop/restart/status du moteur passent par
`loki-rs engine <action>`, port à l'identique de la supervision Go :
`loki serve` lancé chef de session, fichier PID et journal aux mêmes
chemins (serviceIsActive, l'UI et `loki logs` lisent les mêmes fichiers),
contrôle à 2 s avec la fin du journal en cas d'échec, arrêt de tout
l'arbre — session, groupe et filiation, pour le moteur de Strata qui prend
sa propre session — avec les mêmes délais (SIGTERM, 4 s, SIGKILL, 20 s).

Le Go garde ses garde-fous (optimisation en cours, preset externe) puis
délègue au loki-rs voisin de son binaire ; la dernière ligne d'erreur du
superviseur devient l'erreur montrée par l'UI. LOKI_ENGINE_SUPERVISOR=go
revient à l'ancienne supervision. `loki serve`, qui construit la ligne de
commande du moteur, ne change pas : il aura son propre lot.

Le contrôle « démarré ? » regarde si l'enfant est sorti plutôt que le
signal 0 : un enfant mort reste un zombie tant que son parent vit, et un
zombie répond encore au signal 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 07:38:37 +02:00

99 lines
2.9 KiB
Go

//go:build linux
package loki
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
)
// Supervision du moteur déléguée à la façade Rust (lot 3 de la migration,
// rust/loki-rs/src/engine). En conteneur, start/stop/restart/status passent
// par `loki-rs engine <action>` : mêmes fichiers PID et journal, même arrêt de
// tout l'arbre, mêmes délais. Les garde-fous restent ici, en amont
// (serviceAction : optimisation en cours ; userSvcAction : preset externe) ;
// `loki serve` construit toujours la ligne de commande du moteur.
//
// Repli sur la supervision Go si loki-rs est absent à côté de ce binaire, ou
// si LOKI_ENGINE_SUPERVISOR=go (porte de sortie).
// rustEngineSupervisorFor : le loki-rs voisin de exe, s'il doit superviser.
func rustEngineSupervisorFor(exe string) string {
if strings.EqualFold(strings.TrimSpace(os.Getenv("LOKI_ENGINE_SUPERVISOR")), "go") || exe == "" {
return ""
}
p := filepath.Join(filepath.Dir(exe), "loki-rs")
st, err := os.Stat(p)
if err != nil || st.IsDir() || st.Mode()&0o111 == 0 {
return ""
}
return p
}
func rustEngineSupervisor() string {
exe, err := os.Executable()
if err != nil {
return ""
}
return rustEngineSupervisorFor(exe)
}
// rustSvcAction lance `loki-rs engine <action>`. Sa sortie s'affiche telle
// quelle ; en cas d'échec, sa dernière ligne d'erreur devient l'erreur rendue
// (c'est elle que l'UI montre), sans le préfixe du binaire.
func rustSvcAction(rs, action string) error {
self, err := os.Executable()
if err != nil {
return err
}
var stderr bytes.Buffer
cmd := exec.Command(rs, "engine", action)
cmd.Stdout = os.Stdout
cmd.Stderr = io.MultiWriter(os.Stderr, &stderr)
cmd.Env = append(os.Environ(),
"LOKI_GO_BIN="+self,
"LOKI_HOME="+LokiHome(),
"LOKI_SERVICE="+serviceName(),
)
if err := cmd.Run(); err != nil {
if msg := lastErrorLine(stderr.String()); msg != "" {
return errors.New(msg)
}
return fmt.Errorf("loki-rs engine %s : %w", action, err)
}
return nil
}
func lastErrorLine(s string) string {
lines := strings.Split(strings.TrimSpace(s), "\n")
last := strings.TrimSpace(lines[len(lines)-1])
return strings.TrimSpace(strings.TrimPrefix(last, "[loki-rs engine]"))
}
// rustUserSvcAction : version déléguée de userSvcAction. handled=false laisse
// l'action au Go (logs, enable/disable).
func rustUserSvcAction(rs, action string) (handled bool, err error) {
switch action {
case "start", "restart":
// Preset externe : `loki serve` sortirait aussitôt (rien à lancer),
// et le superviseur le prendrait pour un plantage.
if isExternalConfig(ReadConfig()) {
if action == "restart" {
_ = rustSvcAction(rs, "stop")
}
fmt.Printf("%s preset externe actif : pas de moteur local à démarrer\n", dim("[info]"))
return true, nil
}
return true, rustSvcAction(rs, action)
case "stop", "status":
return true, rustSvcAction(rs, action)
}
return false, nil
}