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.