Pourquoi trier un hash dans un template Puppet
Je vous propose ici une petite astuce pour parcourir un hash dans un template Puppet. Le besoin : je génère un fichier via un template dans lequel je parcours un hash pour afficher la liste des clés/valeurs. Problème : Puppet génère un fichier différent à chaque lancement car on ne peut pas prédire l’ordre de ce hash. J’ai rencontré ce problème de façon aléatoire sur certains serveurs avec Ruby 1.8 (Ruby 1.9 conserve l’ordre du hash).
Exemple de template
Je veux écrire un fichier grâce à Puppet en utilisant le hash suivant embarqué dans un de mes manifests :
class binbash::test {
$h={
'cle3' => {'a'=>3, 'b'=>1, 'c'=>2},
'cle1' => {'a'=>1, 'b'=>2, 'c'=>3},
'cle2' => {'a'=>2, 'b'=>3, 'c'=>1}
}
file{'/tmp/test_puppet.txt':
ensure => present,
content => template("binbash/test.erb"),
}
}
Mon template va générer un fichier très simple avec autant de lignes que de clés. Voici le template pour cet exemple :
<% h.each do |key,value| -%>
<%= key %> : a=<%= value['a'] %>, b=<%= value['b'] %>, c=<%= value['c'] %>
<% end -%>
Le problème
Je déclenche l’agent Puppet sur un client pour voir le résultat :
info: Retrieving plugin
info: Loading facts...
info: Caching catalog for mon_serveur
info: Applying configuration version '13'
notice: /Stage[main]/Binbash::Test/File[/tmp/test_puppet.txt]/ensure: created
notice: Finished catalog run in 2.54 seconds
Le fichier /tmp/test_puppet.txt a bien été créé. Il a pour contenu :
cle3 : a=3, b=1, c=2
cle2 : a=2, b=3, c=1
cle1 : a=1, b=2, c=3
Ce n’est pas l’ordre défini dans le manifest. Avec Ruby 1.8, l’ordre n’est pas conservé. Mais bon, jusque ici, ce n’est pas vraiment un problème. Je relance l’agent qui ne devrait faire aucune modification, le fichier étant présent avec le contenu défini par le template :
hinfo: Retrieving plugin
info: Loading facts...
info: Caching catalog for mon_serveur
info: Applying configuration version '13'
notice: /Stage[main]/Binbash::Test/File[/tmp/test_puppet.txt]/content:
--- /tmp/test_puppet.txt 2012-02-20
+++ /tmp/puppet-file20120220 2012-02-20
@@ -1,3 +1,3 @@
-cle3 : a=3, b=1, c=2
cle2 : a=2, b=3, c=1
cle1 : a=1, b=2, c=3
+cle3 : a=3, b=1, c=2
info: /Stage[main]/Binbash::Test/File[/tmp/test_puppet.txt]: Filebucketed /tmp/test_puppet.txt to main with sum 9139c
notice: /Stage[main]/Binbash::Test/File[/tmp/test_puppet.txt]/content: content changed '{md5}9139c' to '{md5}ba9521'
notice: Finished catalog run in 2.79 seconds
L’ordre a changé !
cle2 : a=2, b=3, c=1
cle1 : a=1, b=2, c=3
cle3 : a=3, b=1, c=2
Et c’est sans fin… Résultat, au niveau de votre dashboard, le serveur est toujours remonté comme “Changed”…
Une solution
Une solution simple et rapide est de remplacer le h.each par un h.sort.map dans votre template :
<% h.sort.map do |key,value| -%>
<%= key %> : a=<%= value['a'] %>, b=<%= value['b'] %>, c=<%= value['c'] %>
<% end -%>
Je vérifie le fonctionnement en déclenchant mon agent Puppet :
info: Retrieving plugin
info: Loading facts...
info: Caching catalog for mon_serveur
info: Applying configuration version '13'
notice: /Stage[main]/Binbash::Test/File[/tmp/test_puppet.txt]/content:
--- /tmp/test_puppet.txt 2012-02-20
+++ /tmp/puppet-file20120220 2012-02-20
@@ -1,3 +1,3 @@
-cle3 : a=3, b=1, c=2
-cle2 : a=2, b=3, c=1
cle1 : a=1, b=2, c=3
+cle2 : a=2, b=3, c=1
+cle3 : a=3, b=1, c=2
info: /Stage[main]/Binbash::Test/File[/tmp/test_puppet.txt]: Filebucketed /tmp/test_puppet.txt to main with sum 9139c
notice: /Stage[main]/Binbash::Test/File[/tmp/test_puppet.txt]/content: content changed '{md5}9139c' to '{md5}e7006'
notice: Finished catalog run in 2.61 seconds
Le fichier a de nouveau changé, mais cette fois c’est normal : le hash a été parcouru dans l’ordre. Le résultat est bien celui attendu :
cle1 : a=1, b=2, c=3
cle2 : a=2, b=3, c=1
cle3 : a=3, b=1, c=2
Je relance l’agent pour m’assurer une dernière fois que le résultat est définitif !
info: Retrieving plugin
info: Loading facts...
info: Caching catalog for mon_serveur
info: Applying configuration version '13'
notice: Finished catalog run in 2.48 seconds
Pas de changements \\o/
Conclusion : je n’utilise plus que le sort.map pour parcourir mes hashs.