docs: premiere creation de documentation
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
parent
9a46b5cde7
commit
cb904c4744
34 changed files with 1896 additions and 30 deletions
86
docs/architecture/contraintes.rst
Normal file
86
docs/architecture/contraintes.rst
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
Invariants et pièges
|
||||
====================
|
||||
|
||||
Contraintes découvertes en production ou en corrigeant des bugs. Les enfreindre casse quelque
|
||||
chose qui fonctionne, souvent en silence.
|
||||
|
||||
.. note::
|
||||
|
||||
Cette page reprend la section « Invariants et pièges » de ``CLAUDE.md``, qui reste la
|
||||
référence de développement et fait foi en cas d'écart.
|
||||
|
||||
Réseau
|
||||
------
|
||||
|
||||
* **Ne pas retirer la route ``/32`` vers ``169.254.169.254``** de l'option 121, même quand elle
|
||||
paraît redondante avec la route par défaut. La DNAT vers le serveur de metadata est posée dans
|
||||
le netns du VPC en ``PREROUTING`` : le paquet n'y est traité en L3 que si son next-hop est
|
||||
``interface_ip``. Avec un autre next-hop, la trame est commutée en L2 sans traverser
|
||||
``PREROUTING``, et le provisionnement cloud-init échoue.
|
||||
* **RFC 3442** : un client qui lit l'option 121 **ignore l'option 3**. Toute route par défaut
|
||||
doit donc figurer dans la 121 ; l'option 3 ne sert que les clients qui n'implémentent pas la
|
||||
121.
|
||||
* **La route vers le CIDR du VPC garde ``interface_ip`` comme next-hop** dans tous les modes sauf
|
||||
``bridge`` : sur un subnet à IP publique, le trafic interne ne doit pas sortir par la gateway
|
||||
publique.
|
||||
* ``169.254.169.254`` est centralisé dans ``metadata.ServiceIP`` — ne pas le réécrire en dur.
|
||||
|
||||
QEMU et VM
|
||||
----------
|
||||
|
||||
* **Un seul disque ``vdX`` par VM** ; les disques additionnels passent par le SCSI (``sdX``). La
|
||||
carte PCI en dépend : NIC en ``0x03``, contrôleur SCSI en ``0x1e``, virtio-blk en ``0x1f``.
|
||||
* ``bus=pci.0`` est explicite sur les trois ``-device`` : un passage de la machine en **q35**
|
||||
casserait le démarrage (``Bus 'pci.0' not found``).
|
||||
* QEMU est lancé par ``systemd-run --scope``, **jamais** en unit transitoire : le scope est
|
||||
exécuté par le processus appelant et hérite du netns posé par ``netns.Call``. Une unit
|
||||
transitoire, forkée par PID 1, démarrerait dans le netns racine et ne verrait pas le tap.
|
||||
* L'arrêt d'une VM ne touche **jamais** aux fichiers disque. En revanche, un ``quit`` brutal est
|
||||
envoyé à l'expiration de ``dispatcher.timeout_seconds``.
|
||||
|
||||
Arrêt de l'agent
|
||||
----------------
|
||||
|
||||
* Ordre imposé : serveurs HTTP → drainage des workers → fermeture de la base. L'inverser crée une
|
||||
course.
|
||||
* Budget d'arrêt dépassé ⇒ **la base n'est pas fermée** : fermer Badger sous un écrivain
|
||||
concurrent est pire qu'un rejeu du journal au démarrage suivant.
|
||||
* Jamais de ``log.Fatal`` dans une goroutine : ``os.Exit`` n'exécute aucun ``defer``.
|
||||
|
||||
cloud-init
|
||||
----------
|
||||
|
||||
* ``network-config.tmpl`` cible ``eth0`` alors que les guests sont en ``ens3`` : il ne s'applique
|
||||
donc à rien, et le réseau vient du DHCP. **Ne pas le « corriger » ni le supprimer** — le rendre
|
||||
opérant ferait remplacer par cloud-init la configuration réseau de l'image sur toutes les VM.
|
||||
* Un document fourni par l'appelant est servi **verbatim** ; un document absent retombe sur le
|
||||
template ; un document explicitement vide est servi vide. Les trois cas sont distincts.
|
||||
* ``metadata.password`` est un **hash**, pas un mot de passe en clair.
|
||||
* ``instance-id`` vaut le nom de la VM : recréer une VM du même nom sur le même disque fait que
|
||||
cloud-init la reconnaît et **n'applique pas** le user-data.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
* Le chargement se fait par **viper** : tags ``mapstructure``, jamais ``yaml``.
|
||||
* Un chemin configurable se propage par les signatures de fonction, jamais par une variable ou un
|
||||
setter de paquet.
|
||||
|
||||
Sécurité connue et acceptée
|
||||
---------------------------
|
||||
|
||||
Ces points sont documentés parce qu'ils sont **assumés en l'état**, pas parce qu'ils sont sans
|
||||
conséquence. Ils doivent être réévalués avant toute exposition élargie de l'API.
|
||||
|
||||
* **L'API n'a aucune authentification** et l'exemple de configuration l'expose sur
|
||||
``0.0.0.0:8080``. Quiconque atteint ce port pilote le host KVM.
|
||||
* ``vm/<name>/password`` est stocké tel quel (c'est un hash) et restitué par ``/db?prefix=vm/``
|
||||
du serveur d'administration — contenu par ``admin.enabled: false`` et l'écoute en boucle
|
||||
locale.
|
||||
* ``/run/two/metadata/<vm>/vendor-data`` est en ``0644`` et contient ce hash : tout compte local
|
||||
du host peut le lire.
|
||||
* ``pkg/systemd.New()`` n'a **pas de timeout** : si le socket D-Bus accepte sans répondre,
|
||||
l'appelant se fige. Concerne le watchdog, la création et la suppression de subnets, et le
|
||||
serveur de metadata.
|
||||
* Il n'y a **pas de rollback** : un échec partiel de création laisse des objets réseau orphelins
|
||||
jusqu'à un ``DELETE`` explicite.
|
||||
13
docs/architecture/index.rst
Normal file
13
docs/architecture/index.rst
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
Architecture
|
||||
============
|
||||
|
||||
L'organisation interne de l'agent : découpage en paquets, stockage, et contraintes à ne pas
|
||||
enfreindre. Ces pages s'adressent à qui modifie le code ; ``CLAUDE.md``, à la racine du dépôt,
|
||||
reste la référence de développement et fait foi en cas d'écart.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
vue-densemble
|
||||
stockage
|
||||
contraintes
|
||||
51
docs/architecture/stockage.rst
Normal file
51
docs/architecture/stockage.rst
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
Schéma des clés
|
||||
===============
|
||||
|
||||
Toutes les valeurs stockées dans Badger sont des **chaînes plates** : une clé, une valeur, pas
|
||||
de sérialisation structurée.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
vpc/<name>/state → creating | running | error | deleting | deleted
|
||||
vpc/<name>/cidr → <cidr>
|
||||
|
||||
subnet/<name>/state → creating | running | error | deleting | deleted
|
||||
subnet/<name>/vpc → <vpc-name>
|
||||
subnet/<name>/mode → vxlan | bridge | public_ip
|
||||
subnet/<name>/vxlan_id → <id> (mode vxlan uniquement)
|
||||
subnet/<name>/cidr → <cidr>
|
||||
subnet/<name>/interface_ip → <ip> (gateway, portée par br-<subnetID>)
|
||||
subnet/<name>/local_iface → <bridge-name>
|
||||
subnet/<name>/default_route → "true" | "false"
|
||||
subnet/<name>/gateway → <ip> (optionnel)
|
||||
subnet/<name>/dhcp/<ip> → <mac>
|
||||
|
||||
vm/<name>/state → creating | running | error | deleting | deleted
|
||||
vm/<name>/subnet → <subnet-name>
|
||||
vm/<name>/tap_id → <int>
|
||||
vm/<name>/ip → <ip>
|
||||
vm/<name>/metadata_port → <port>
|
||||
vm/<name>/disk/<dev> → <path> (une clé par disque : sda, vda, …)
|
||||
vm/<name>/memory → <int> (Mo)
|
||||
vm/<name>/cpus → <int>
|
||||
vm/<name>/uefi → "true" (absent si SeaBIOS)
|
||||
vm/<name>/password → <hash> (optionnel — un hash, pas un mot de passe)
|
||||
vm/<name>/sshkey → <pubkey> (optionnel)
|
||||
vm/<name>/metadata/<document> → <contenu brut> (optionnel : user-data, vendor-data, …)
|
||||
|
||||
Règles
|
||||
------
|
||||
|
||||
**Pas de duplication.** Une ressource ne stocke que ce qui lui est propre. Une VM garde le lien
|
||||
``vm/<name>/subnet`` ; le VPC, le bridge et l'``interface_ip`` sont lus depuis le subnet, leur
|
||||
source canonique.
|
||||
|
||||
**Les états passent par ``state``.** Toujours ``state.Set`` / ``state.Get`` : ``Set`` refuse une
|
||||
valeur hors énumération, ``Get`` refuse de retourner une valeur non reconnue. ``error`` n'est
|
||||
écrit que par ``Dispatcher.Dispatch``, via ``cmd.Key()``.
|
||||
|
||||
**Tout entier lu depuis la base peut être corrompu.** Les erreurs de conversion sont retournées,
|
||||
jamais ignorées : une valeur absente ou illisible est un état d'erreur réel.
|
||||
|
||||
**Un seul ouvreur.** L'agent est le seul processus à ouvrir la base. Le serveur de metadata lit
|
||||
des fichiers écrits par l'agent sous ``metadata.run_dir``, jamais Badger.
|
||||
90
docs/architecture/vue-densemble.rst
Normal file
90
docs/architecture/vue-densemble.rst
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
Vue d'ensemble
|
||||
==============
|
||||
|
||||
Cycle d'une requête
|
||||
-------------------
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
HTTP → internal/api/agent → Dispatcher.Prepare() → Dispatcher.Dispatch() → worker.Queue → Command.Execute()
|
||||
|
||||
**Prepare** (synchrone, dans le handler HTTP)
|
||||
valide l'état, écrit l'état initial (``creating`` / ``deleting``) en base, et retourne 202 ou
|
||||
une erreur.
|
||||
|
||||
**Dispatch** (asynchrone)
|
||||
place la commande sur un canal bufferisé ; une goroutine worker appelle ``Execute``. C'est
|
||||
``Dispatch``, et lui seul, qui marque la ressource en ``error`` si ``Execute`` échoue.
|
||||
|
||||
**Execute**
|
||||
effectue le travail réseau (netns, netif, VXLAN, veth, bridge, DHCP), puis met l'état à
|
||||
``running`` / ``deleted``.
|
||||
|
||||
Paquets
|
||||
-------
|
||||
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
:widths: 32 68
|
||||
|
||||
* - Chemin
|
||||
- Rôle
|
||||
* - ``internal/api/agent``
|
||||
- handlers HTTP de ``/vpcs``, ``/subnets`` et ``/vms``
|
||||
* - ``internal/dispatcher/agent``
|
||||
- interface ``Command`` (``Prepare``/``Execute``/``Key``) et commandes concrètes
|
||||
* - ``internal/state``
|
||||
- énumération des états, ``CanDelete``/``IsTransient``, seul point d'écriture des états
|
||||
* - ``internal/migration``
|
||||
- migrations idempotentes jouées au démarrage de l'agent
|
||||
* - ``internal/vpc``, ``internal/subnet``
|
||||
- création et suppression bas niveau (netns + netif)
|
||||
* - ``internal/netns``
|
||||
- network namespaces : create/enter/delete/call
|
||||
* - ``internal/netif``
|
||||
- netlink : bridge, veth, vxlan, tap, routes, adresses
|
||||
* - ``internal/ebtables``, ``internal/iptables``
|
||||
- wrappers dédiés ; ne pas appeler ces binaires ailleurs
|
||||
* - ``internal/qemu``, ``internal/qmp``
|
||||
- lancement de QEMU et client QMP sur socket Unix
|
||||
* - ``internal/vm``
|
||||
- cycle de vie d'une VM : tap, iptables, metadata, qemu
|
||||
* - ``internal/dhcp``
|
||||
- génération des configurations dnsmasq et entrées ip → mac
|
||||
* - ``internal/metadata``
|
||||
- serveur de metadata cloud-init et ses templates
|
||||
* - ``internal/watchdog``
|
||||
- vérification périodique en lecture seule
|
||||
* - ``internal/config/agent``
|
||||
- chargement par viper — tags ``mapstructure``, jamais ``yaml``
|
||||
* - ``internal/prometheus/agent``
|
||||
- collector des métriques ``syonad_*``
|
||||
* - ``pkg/db/kv``
|
||||
- wrapper Badger ; toutes les valeurs sont des chaînes plates
|
||||
* - ``pkg/worker``
|
||||
- pool de goroutines sur canal
|
||||
* - ``pkg/systemd``
|
||||
- client D-Bus systemd
|
||||
* - ``pkg/logger``, ``pkg/prometheus``
|
||||
- journalisation ``slog`` et serveur de métriques
|
||||
|
||||
Ajouter un type de ressource
|
||||
----------------------------
|
||||
|
||||
#. ajouter les helpers KV dans ``pkg/db/kv`` si nécessaire ;
|
||||
#. définir ``Create<X>`` / ``Delete<X>`` dans un nouveau paquet ``internal/<x>/`` ;
|
||||
#. ajouter ``Create<X>Command`` / ``Delete<X>Command`` dans ``internal/dispatcher/agent/``, dont
|
||||
``Key()`` qui retourne ``<x>/<name>`` et le contrôle ``state.CanDelete`` dans
|
||||
``Delete<X>Command.Prepare`` ;
|
||||
#. ajouter les handlers HTTP dans ``internal/api/agent/`` et les routes dans ``server.go``.
|
||||
|
||||
Stubs de plateforme
|
||||
-------------------
|
||||
|
||||
Les fichiers ``_linux.go`` portent l'implémentation netlink/netns réelle ; les ``_other.go``
|
||||
correspondants retournent une erreur « not supported on this platform ». Tous les paquets
|
||||
**compilent** sur macOS, ce qui permet d'y tester la logique qui ne touche ni netlink ni netns.
|
||||
|
||||
Deux exceptions à connaître : les stubs de ``netns`` exécutent ``fn`` **sans changer de
|
||||
namespace** — ``netns.Call`` réussit donc hors Linux — et ``netif`` compile partout parce que
|
||||
netlink fournit une implémentation « unspecified ».
|
||||
Loading…
Add table
Add a link
Reference in a new issue