Saturn

Saturn est l'API centrale de supervision de flotte. Elle orchestre l'inventaire des nodes, le dispatch d'actions et les integrations DNS/DHCP.
Le service est implemente en Go et expose
principalement une API REST sur le port :8130.
Pour certaines integrations internes, Saturn peut aussi communiquer
en gRPC.
Inventaire : gestion des nodes, adresses IP et metadonnees d'exploitation.
Actions : mise en file, journalisation et dispatch des commandes operateur vers les nodes.
Reseau : relais DNS/DHCP via le service
central-dnsdhcp.Logs : consommation des logs via
central-mercure(sans acces direct a Elasticsearch).
Endpoints principaux
| Méthode | Chemin | Description |
|---|---|---|
GET | /health | Santé API + état PostgreSQL et API logs. |
GET | /api/nodes | Liste des nodes connus. |
POST | /api/nodes/upsert | Ajoute ou met à jour un node. |
POST | /api/nodes/{id}/actions | Enfile une action pour un node. |
GET | /api/actions | Liste des actions récentes. |
GET | /api/logs | Requête des logs via Mercure. |
IAM natif: login email et bootstrap utilisateurs
Le provider natif de Saturn impose une authentification par email uniquement:
- l'identifiant HTTP Basic de
/api/auth/loginet/api/auth/token(grantpassword) doit etre un email; - l'email est aussi persiste comme
usernametechnique (alignement strict); - un changement d'email met a jour la cle de connexion.
Pour initialiser des comptes admin automatiquement au demarrage, Saturn peut charger un fichier JSON (idempotent: creation si absent, mise a jour sinon).
Variables d'environnement:
AUTH_BOOTSTRAP_USERS_ENABLED=true|falseAUTH_BOOTSTRAP_USERS_FILE=/data/config/users.jsonAUTH_ADMIN_EMAIL=admin@leukos.local(fallback si aucun utilisateur existant)
Exemple users.json:
[
{
"email": "admin1@example.com",
"password": "StrongPass@123",
"display_name": "Admin One",
"roles": ["admin"]
}
]
Notes:
- les roles du bootstrap sont forces a
adminpour ces comptes; - au redemarrage, Saturn ne duplique pas les utilisateurs existants.
Comment Saturn se connecte aux autres APIs
Saturn est expose en REST pour les clients amont, puis agit en client sortant vers les autres services de la flotte.
| Source / cible | Protocole | Exemple | Usage |
|---|---|---|---|
| Venus -> Saturn | HTTP REST | GET /api/nodes | Inventaire, actions, DNS/DHCP, logs. |
| Saturn -> Mercure | HTTP REST | GET /logs?node_id=node-01&level=error | Lecture des logs agreges. |
| Saturn -> DNSDHCP | gRPC interne | UpsertDnsRecord, DeleteDhcpRecord | CRUD DNS/DHCP via service dedie. |
| Saturn -> Node | gRPC prioritaire | RelaySet, ValveSet, DisplayOperation | Pilotage temps reel sur daemon node. |
| Saturn -> Node (fallback) | HTTP REST | POST /relay/on, POST /valve/open | Compatibilite nodes sans gRPC actif. |
Connexion aux nodes: gRPC puis fallback HTTP
Le dispatcher Saturn applique une strategie en deux etapes:
- tentative gRPC sur le daemon node pour les domaines supportes;
- si indisponible, repli HTTP sur l'API locale node.
// Exemple d'intention: sequence simplifiee de dispatch.
func dispatchNodeAction(ctx context.Context, nodeIP string, action Action) error {
// 1) Tentative gRPC (ports historiques supportes)
if err := tryGRPC(ctx, nodeIP, 19080, action); err == nil {
return nil
}
if err := tryGRPC(ctx, nodeIP, 9080, action); err == nil {
return nil
}
// 2) Fallback HTTP pour compatibilite
endpoint := mapActionToHTTP(action) // ex: /relay/on, /valve/close
return postJSON(ctx, "http://"+nodeIP+":8080"+endpoint, action.Payload)
}
Exemple d'appel HTTP vers un node (fallback):
curl -X POST http://10.20.0.21:8080/relay/on \
-H 'Content-Type: application/json' \
-d '{"relay_id":"1","value":"on"}'
Donnees metier et reseau
Saturn est le point d'entree metier pour la supervision, mais s'appuie sur des services infra specialises.
- PostgreSQL (leukos_central) : persistance des utilisateurs, inventaire nodes, etat de connectivite et historique d'actions.
- central-dnsdhcp : source de verite DNS/DHCP, consommee par Saturn en gRPC interne.
- CoreDNS : resolution DNS locale de la flotte
(
*.leukos.lan), alimentee via les synchronisations DNS.
Integration DNS/DHCP
Saturn ne porte pas la logique de stockage DNS/DHCP localement : il delegue les mutations et lectures au service dedie, puis orchestre la synchronisation DNS necessaire a l'exploitation.
Exemple d'operation API cote Saturn:
curl -X POST http://central:8130/api/dns/records/upsert \\
-H 'Content-Type: application/json' \\
-d '{"hostname":"node-01.leukos.lan","ip":"10.20.0.21","enabled":true}'
curl -X POST http://central:8130/api/dns/sync
Exemples de code d'integration
Appel Mercure depuis Saturn (REST)
// Exemple d'adaptateur REST pour requeter les logs via Mercure.
func (c *LogsClient) QueryLogs(ctx context.Context, nodeID, level string, limit int) ([]LogItem, error) {
req, _ := http.NewRequestWithContext(
ctx,
http.MethodGet,
c.baseURL+"/logs?node_id="+url.QueryEscape(nodeID)+"&level="+url.QueryEscape(level)+"&limit="+strconv.Itoa(limit),
nil,
)
resp, err := c.http.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return nil, fmt.Errorf("mercure returned status %d", resp.StatusCode)
}
var out struct{ Items []LogItem `json:"items"` }
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
return nil, err
}
return out.Items, nil
}
Appel DNS/DHCP depuis Saturn (gRPC)
// Exemple d'appel gRPC interne vers le service DNS/DHCP.
func (c *DNSDHCPClient) UpsertDNS(ctx context.Context, host, ip string) error {
_, err := c.grpc.UpsertDnsRecord(ctx, &dnspb.UpsertDnsRecordRequest{
Hostname: host,
Ip: ip,
Enabled: true,
})
return err
}
Flux de commande node
- Le frontend envoie une action à Saturn.
- Saturn persiste et marque l'action
queued. - Le dispatcher tente un routage gRPC pour les domaines supportés.
- En fallback, Saturn utilise HTTP vers l'API locale node si nécessaire.
- Le statut final est publié (
sentoufailed).