Aller au contenu
Infrastructure as Code medium

Idempotence et mode unifié de Chef

9 min de lecture

logo chef

Chef exécute un cookbook en deux temps : il le compile entièrement, puis il fait converger les ressources. Ignorer cette mécanique conduit à des surprises : du code Ruby qui s'exécute « trop tôt », une ressource qui refait son travail à chaque passage. Ce guide ouvre le capot de la convergence : les deux phases, le piège du Ruby en compilation, les gardes d'idempotence, et le mode unifié des versions récentes. Public visé : lecteur ayant pratiqué ressources, recettes et attributs. Testé avec CINC Client 19.3.14 et kitchen-dokken sur Ubuntu 24.04.

  • Distinguer la phase de compilation de la phase de convergence.
  • Repérer une ressource non idempotente et la corriger.
  • Utiliser les gardes not_if et only_if.
  • Comprendre ce qu'apporte le mode unifié des Chef récents.

Une exécution de cinc-client se déroule en deux temps :

  1. La compilation : Chef lit toute la recette de haut en bas, exécute le Ruby rencontré et construit la liste des ressources. Rien n'est encore appliqué à la machine.
  2. La convergence : Chef parcourt cette liste et applique chaque ressource dans l'ordre, en ne changeant que ce qui doit l'être.

La conséquence est capitale : le Ruby « nu » (hors d'un bloc de ressource) s'exécute pendant la compilation, donc avant que la moindre ressource ait convergé.

Regardons ce piège en action. Cette recette crée un fichier, puis teste sa présence avec du Ruby :

file "/tmp/marqueur.txt" do
content "present\n"
end
if ::File.exist?("/tmp/marqueur.txt")
log "compilation : le fichier existe deja"
else
log "compilation : le fichier n'existe pas encore"
end

À la première convergence, le test tombe sur la branche « n'existe pas encore », alors que la ressource file juste au-dessus va bel et bien créer le fichier :

* log[compilation : le fichier n'existe pas encore] action write

Relancez : le message change :

* log[compilation : le fichier existe deja] action write

Le if n'a pas été évalué au moment où on le lit dans le code, mais en compilation, avant la convergence : au premier passage le fichier n'existait pas encore, au second il avait été créé par le passage précédent. Ne fondez jamais une décision sur l'état que vos propres ressources produisent : au moment du test Ruby, elles n'ont pas encore convergé.

L'idempotence n'est pas automatique. La ressource execute lance une commande, et sans garde, elle la relance à chaque convergence :

execute "horodatage" do
command "date >> /tmp/journal.log"
end

Convergez deux fois : la commande tourne les deux fois.

# 1er passage
* execute[horodatage] action run
Infra Phase complete, 1/1 resources updated
# 2e passage
* execute[horodatage] action run
Infra Phase complete, 1/1 resources updated

Chaque exécution ajoute une ligne au journal : ce cookbook n'est pas idempotent. Contrairement à package, file ou service qui savent comparer l'état voulu à l'état courant, execute ne sait pas, par défaut, si son travail est déjà fait.

On ajoute une garde qui dit à Chef quand l'action est nécessaire. not_if empêche l'exécution si sa condition est vraie ; only_if ne l'autorise que si sa condition est vraie.

execute "horodatage" do
command "date >> /tmp/journal.log"
not_if { ::File.exist?("/tmp/journal.log") }
end

Cette fois, la commande ne tourne qu'une fois :

# 1er passage : la garde est fausse, la commande tourne
* execute[horodatage] action run
Infra Phase complete, 1/1 resources updated
# 2e passage : la garde est vraie, la commande est ignorée
* execute[horodatage] action run (skipped due to not_if)
Infra Phase complete, 0/1 resources updated

skipped due to not_if : la garde a fait son travail. Une garde peut tester un fichier (not_if { ::File.exist?(...) }), une commande shell (not_if "test -f /tmp/journal.log"), ou pour les commandes qui créent un fichier, la propriété creates suffit.

Le modèle en deux phases vaut pour les recettes. À l'intérieur d'une ressource personnalisée, les versions récentes de Chef (et de CINC) activent par défaut le mode unifié (unified_mode) : compilation et convergence y sont fusionnées en une seule passe, dans l'ordre d'écriture.

L'intérêt est concret : à l'intérieur d'une ressource personnalisée, le Ruby et les sous-ressources s'exécutent dans l'ordre où vous les lisez, ce qui supprime toute une classe de bugs d'ordre et de notification. Vous profiterez du mode unifié quand vous écrirez vos propres ressources, sujet traité dans un guide dédié. Retenez pour l'instant que les recettes gardent les deux phases, et que le mode unifié concerne les ressources personnalisées.

La preuve reste la même depuis votre premier cookbook : un second kitchen converge à 0 ressource mise à jour. C'est le test à faire systématiquement avant de considérer un cookbook comme fini.

Pour anticiper sans rien modifier, Chef propose le mode why-run (cinc-client --why-run) : il annonce ce qu'il changerait sans l'appliquer. Utile pour inspecter un cookbook hérité avant de le lancer pour de vrai.

Un collègue vous laisse cette ressource. Corrigez-la. Cherchez la solution avant d'ouvrir la réponse.

Cette ressource recrée un fichier de configuration à chaque convergence :

execute "generer-config" do
command "echo 'genere le' $(date) > /etc/app.conf"
end

Ajoutez une garde pour qu'elle ne s'exécute qu'une fois : convergez, la commande tourne ; reconvergez, elle est ignorée.

Indice : le fichier /etc/app.conf prouve que le travail est fait.

  • Chef s'exécute en deux phases : compilation (le Ruby s'exécute, la liste des ressources se construit) puis convergence (les ressources s'appliquent).
  • Le Ruby nu s'exécute en compilation, avant toute convergence : ne décidez rien sur l'état que vos ressources vont produire.
  • Une ressource execute sans garde n'est pas idempotente : elle retourne à chaque passage.
  • Les gardes not_if et only_if (ou creates) rendent une commande idempotente.
  • Le mode unifié fusionne les deux phases dans les ressources personnalisées ; les recettes gardent le modèle en deux phases.
  • La preuve d'idempotence reste un second passage à 0 ressource mise à jour.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn