module

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

EXIT_NO_FREEBSD = 10
EXIT_NOT_INSTALLED = 12

Serveur nommé explicitement mais installed: false : à bootstrapper d'abord.

EXIT_OK = 0
EXIT_RECIPE = 11
EXIT_SSH_FAILED = 4
EXIT_UNEXPECTED = 3
EXIT_USAGE = 1
VARIABLE_SOURCES = {"public_ip" => "`ovh.ipv4` (ou `beryl info --refresh`)", "public_ip6" => "`ovh.ipv6`", "public_ip6_gateway" => "`ovh.ipv6_gateway` (`beryl info --refresh` la lit dans l'API OVH)", "vrack_ip" => "`vrack-interface: { ip: … }` dans `apply_recipes`"}

Variables fournies par l'hôte → ce qu'il faut renseigner pour les avoir.

Class methods

adhoc_requests(name : String, declared : Array(RecipeRequest), closure : Array(String), restrict : Bool) : Array(RecipeRequest) | Nil

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é) quand restrict — portée domaine ou société : beryl sync <société> nginx-lite ne doit pas installer nginx là où rien ne le demande.
Source
adhoc_selection(names : Array(String), declared : Array(RecipeRequest), closure : Array(String), restrict : Bool) : Tuple(Array(RecipeRequest), Array(String))

Plusieurs recettes ponctuelles : {requêtes à jouer, recettes sautées} (cf. adhoc_requests pour chacune).

Source
dns_current?(fqdn : String, type : String, value : String) : Bool

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).

Source
dns_hook(config_root : String, host : Beryl::Config::ResolvedHost) : Beryl::Apply::DnsHook

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.

Source
fleet_hosts(config_root : String) : Array(Beryl::Config::ResolvedHost)

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).

Source
imposed_by_declared(full : Array(Beryl::Apply::Recipe), recipe_args : Hash(String, Array(Hash(String, String))), resolved : Array(String)) : Hash(String, Array(Hash(String, String)))

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.

Source
missing_variable_message(ex : Beryl::Apply::MissingVariable, host : Beryl::Config::ResolvedHost) : String

« 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.

Source
recipe_arguments(requests : Array(RecipeRequest), declared : Array(RecipeRequest), resolved : Array(String)) : Hash(String, Array(Hash(String, String)))

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).

Source
recipes_search_path(config_root : String, host : Beryl::Config::ResolvedHost) : Array(String)

Chemin de recherche des recettes, par ORDRE DE PRIORITÉ :

  1. 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é).
  2. GÉNÉRIQUE public : <recipes.local_path>/recipes/ (défaut <config_root>/recipes/recipes/) — le set diffusable, sans société. Public : réutilisé par beryl info --versions/--updates pour savoir quelles recettes (donc quels paquets) s'appliquent à un host.
Source
resolve_list(root : Beryl::Config::Root, scope : String, account_hint : String | Nil, domain_hint : String | Nil) : Array(Beryl::Config::ResolvedHost)

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.

Source
root_ssh_login(host : Beryl::Config::ResolvedHost) : String

{{ 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.

Source
root_ssh_login(boot_pool : Beryl::Config::Pool) : String
Source
run(config_root : String, args : Array(String)) : Int32
Source

{non installés, installés} (installed: false dans le host.yml).

Source
unknown_parts(root : Beryl::Config::Root, scope : String, account_hint : String | Nil, domain_hint : String | Nil) : Array(String)

Éléments d'une liste qui ne désignent aucun serveur (faute de frappe…) : refusés plutôt qu'ignorés en silence.

Source

Nested types