Beryl::CLI::Apply
Sous-commande beryl sync <host> : applique sur le serveur FreeBSD
(déjà bootstrappé) les recettes déclarées dans le dossier
d'orchestration du host.
Architecture à 2 niveaux (cf. apply-recipes-architecture.adoc) :
- recettes YAML reliées par
requires:(résolution récursive, tri topologique, détection de cycle) ; - primitives Crystal natives idempotentes (
pkg-install, …).
Dossiers lus :
- recettes PRIVÉES de la société :
<config>/<société>/recipes/; - dépôt central :
<recipes.local_path>/recipes/(défaut<config>/recipes/recipes/).
Priorité de résolution : recettes de la société > dépôt central. Idempotence :
chaque primitive lit l'état réel via SSH avant d'agir. Dry-run par
défaut (calcule le plan sans rien modifier) ; --apply pour appliquer.
Constants
Serveur nommé explicitement mais installed: false : à bootstrapper d'abord.
Variables fournies par l'hôte → ce qu'il faut renseigner pour les avoir.
Class methods
Recettes d'entrée du mode ponctuel (beryl sync <portée> <recette>) :
- recette DÉCLARÉE dans
apply_recipes:→ ses jeux d'arguments du host.yml sont repris (sinon une recette paramétrée par le host, ex.zfs-scrub: { scrub_schedule: … }, repartirait de ses défauts) ; - tirée par une recette déclarée (
requires:) → sans argument ; - non déclarée : appliquée telle quelle sur UN serveur nommé (action
ponctuelle, ex. rotation de clé), mais
nil(serveur sauté) quandrestrict— portée domaine ou société :beryl sync <société> nginx-litene doit pas installer nginx là où rien ne le demande.
Plusieurs recettes ponctuelles : {requêtes à jouer, recettes sautées} (cf. adhoc_requests pour chacune).
Le nom répond-il déjà par cette adresse (A, AAAA) ? Lecture par le
résolveur du poste : un enregistrement juste n'est ni reposé ni compté
comme un changement (sinon les if_changed de la recette se
déclencheraient à chaque apply). CNAME : toujours posé (pas de lecture).
Pose DNS de la primitive dns-record : zone de la société de l'hôte
(la plus longue qui contient le nom), son dns_provider, les
identifiants du coffre déjà appliqués pour l'hôte. Mêmes validations que
beryl dns-record.
Toutes les fiches résolues de la config (une résolution par processus,
même en apply de flotte), comme beryl info : le DNS interne du vRack se
calcule sur le parc. Réutilise la config déjà chargée par run. Une fiche
qui ne se résout pas est ignorée (elle échouera dans son propre apply).
Recettes nommées : les arguments qu'imposent aux dépendances (requires:
en forme map) les recettes DÉCLARÉES du serveur, même non nommées.
beryl sync aloli static-site-https tirait nginx-lite sans le
package: nginx-lite-aloli de nginx-lite-aloli : paquet maison
remplacé par l'officiel, sans HTTP/2 (loulou, 9 nov. 2026). Limité aux
recettes jouées.
« recette netif-ipv6 : variable public_ip6 non définie — renseignez
ovh.ipv6 dans <host.yml> » ; variable de recette inconnue : le message
nomme la recette et les variables connues.
Jeux d'arguments par recette à jouer. Une dépendance tirée par la recette
ponctuelle (requires:) mais DÉCLARÉE dans apply_recipes: reprend ses
arguments du host.yml : beryl sync <portée> periodic-quiet tire monit,
qui sans eux repartait de ses défauts (relais de mail d'un client écrasé
par celui de la société, quimeo-twin, 29 oct. 2026).
Chemin de recherche des recettes, par ORDRE DE PRIORITÉ :
- PRIVÉ société :
<config_root>/<société>/recipes/(si présent) — recettes spécifiques à la société, peut SURCHARGER une générique du même nom. Vit dans le dépôt de config (déjà privé par société). - GÉNÉRIQUE public :
<recipes.local_path>/recipes/(défaut<config_root>/recipes/recipes/) — le set diffusable, sans société. Public : réutilisé parberyl info --versions/--updatespour savoir quelles recettes (donc quels paquets) s'appliquent à un host.
Portée LISTE a,b,c : union des portées citées (serveur, groupe, domaine,
société), dans l'ordre de la liste, sans doublon. Pur côté config.
{{ root_ssh_login }} (PermitRootLogin, recette sshd-auth-keys-only) : la
MÊME règle que le bootstrap — root COUPÉ (no), sauf /home chiffré
(profil C+), où root par clé (prohibit-password) est la porte de secours
tant que les datasets ne sont pas déverrouillés.
{non installés, installés} (installed: false dans le host.yml).