Publié par

Il y a 5 ans -

Temps de lecture 7 minutes

Puppet recipes 01 : Gérer un fichier avec Puppet

Premier épisode de cette série ayant pour but d’approfondir certains thèmes ou de résoudre des problématiques courantes sur Puppet.

Cet épisode est consacré à une opération extrêmement fréquente avec Puppet, la mise en place d’un fichier. Trivial me direz-vous ? Tout dépend de ce qui est demandé…

Introduction

Petit rappel pour ceux qui dormaient en fond de classe :

D’après Wikipedia :
« Puppet est un logiciel libre permettant la gestion de la configuration de serveurs esclaves (Linux, Mac OS X et Windows).
Puppet est écrit à l’aide du langage de programmation Ruby et est diffusé sous licence Apache 2.0. »

C’est actuellement l’un des outils les plus populaires dans la gestion de configuration mais il n’est clairement pas le seul dans son domaine. On peut également citer Chef, Ansible, Salt, cfengine,…

L’une des actions les plus courantes à réaliser est de gérer un fichier sur un serveur. Et on peut faire ça de beaucoup de façons avec Puppet !

Dans les cas à venir nous allons modifier le fichier /etc/hosts afin d’ajouter une ou plusieurs lignes à ce fichier.
Voici à quoi ressemble notre fichier juste après l’installation du système :

 127.0.0.1       localhost.localdomain localhost

Utiliser une ressource de base

Première possibilité, utiliser une des ressources de base de Puppet.
Ces ressources sont incluses directement dans Puppet et permettent de modifier un fichier bien particulier. Il n’existe pas de ressource de base pour chaque fichier de configuration mais la ressource « host » permet justement de faire cela pour le fichier /etc/hosts alors nous allons en profiter !

Pour ajouter une ligne à notre fichier il suffit de faire :

host{'server':
  ip => '10.0.0.2'
}

Après un run Puppet notre fichier est conforme à l’attendu :

127.0.0.1       localhost.localdomain localhost
10.0.0.2        server

La ressource « host » dispose également d’autres paramètres pour ajouter des alias, des commentaires, écrire les données dans un autre fichier etc. Tout est détaillé dans la référence sur la ressource host.
Pour info il existe également des ressources de base pour les fichiers /etc/fstab (la ressource « mount ») ou yum.repos.d (ressource « yumrepo »).

Dans quel cas utiliser cette ressource ?
Toujours. Il existe peu de ressources Puppet dédiées à gérer un fichier de configuration particulier. Quand elles existent autant les utiliser d’autant plus qu’elles sont paramétrables et très bien testées.

Utiliser la ressource « file »

Comme son nom l’indique si bien, la ressource « file » est destinée à gérer des fichiers de tout type. Nous utilisons en général soit l’attribut « source » soit l’attribut « content ».

L’attribut « source »

L’attribut « source » permet d’indiquer au client Puppet d’aller chercher le fichier sur le PuppetMaster :

file{'/etc/hosts':
  source => 'puppet:///modules/hosts/hosts'
}

Dans quel cas utiliser cet attribut :

  • Si le contenu de notre fichier ne change pas sur les différents serveurs d’un environnement. Pour gérer des systèmes d’exploitation différents on peut utiliser des variables dans le nom du fichier :
file{'/etc/hosts':
  source => "puppet:///modules/hosts/hosts.$operatingsystem"
}
  • Pour copier de manière récursive un dossier depuis le PuppetMaster vers le client via l’attribut « recurse » :
file{'conf':
  path => '/etc',
  source => 'puppet:///modules/hosts/etc',
  recurse => true
}

L’attribut content

L’attribut « content » permet d’indiquer directement à Puppet le contenu devant être placé dans le fichier géré. Par exemple avec le fichier /etc/hosts :

$hosts = "127.0.0.1       localhost.localdomain localhost
10.0.0.2         server
"

file { "/etc/hosts":
  content => $hosts
}

Mais l’utilisation la plus classique de cet attribut est en conjonction avec la fonction « template » qui permet d’évaluer un template et de retourner son résultat :

$ip = '10.0.0.2'
$server_name = 'server'
file { "/etc/hosts":
  content => template('hosts/host.erb')
}

127.0.0.1       localhost.localdomain localhost
<%= @ip %>      <%= @server_name %>

Dans quel cas utiliser cet attribut :

  • Le contenu du fichier est très court ;
  • Le contenu de votre fichier change en fonction de votre machine, vous avez besoin de gérer des cas ou des boucles. Il faut dans ce cas utiliser un template.

Le module concat

Concat est un module très courant de Puppet disponible sur la forge Puppet. Il est inclus par défaut dans Puppet Enterprise et permet de construire un fichier par concaténation de plusieurs morceaux :

On définit d’abord le fichier que l’on va gérer avec le module concat :

concat { '/etc/hosts':
  ensure => present
}

Et on ajoute un fragment :

concat::fragment { 'hosts_file':
  target  => '/etc/hosts',
  content => '10.0.0.2         server',
  order   => '01'
}

Dans quel cas utiliser ce module :

Un fichier dans Puppet ne peut être géré que par un seul module. Il n’est donc pas possible de déclarer la même ressource « file » dans 2 modules différents utilisés sur la même machine. Or il est tout à fait concevable que deux modules souhaitent modifier le même fichier.

La solution ici est de créer un troisième module chargé de gérer ce fichier via le module « concat » :

concat { '/etc/hosts':
  ensure => present,
}
define host::add($content="", $order=10) {

  if $content == "" {
    # $name is the title of your resource
$body = $name
  } else {
    $body = $content
  }

  concat::fragment{ "host_fragment_$name":
    target  => '/etc/hosts',
    content => "$body\n"
  }
}

Et un module peut ensuite ajouter facilement un élément au fichier /etc/hosts :

class log::client {
  host::add{ 'log_server':
    content => '10.0.0.2         server'
  }
}

La ressource Augeas

Augeas est un projet open-source soutenu par Redhat et permettant de modifier des fichiers de configuration via la ligne de commande. Cet outil bien qu’extrêmement puissant n’est pas très facile d’accès.

Fort heureusement il existe une ressource Puppet permettant de l’utiliser directement depuis un manifest.

On va légèrement modifier notre fichier /etc/hosts de base pour que l’on voit bien  dans quel cas utiliser cette ressource :

127.0.0.1       localhost.localdomain localhost
# Bloc 1
[...]
# Bloc 2
[...]
# Bloc 3
[...]

Notre fichier /etc/hosts est organisé en bloc afin qu’il soit plus facile à lire pour les humains. Que faire si l’on a besoin de rajouter un élément dans le bloc 2 sans toucher au reste du fichier qui n’est pas géré par Puppet ?

Dans ce cas pas le choix il faut passer par la ressource Augeas.

augeas { "insert_host_server":
  context => "/files/etc/hosts",
  changes => [
      "ins 01 after #comment[. = 'Bloc 2']",
      "set 01/ipaddr '10.0.0.2'",
      "set 01/canonical server",
        ],
  onlyif => "match *[ipaddr = '10.0.0.2'] size == 0"
}

Et voilà ! Tout simple !

Quelques explications quand même au cas où :

  • Augeas représente les fichiers sous forme arborescente. Notre fichier a donc cette structure :
augtool> print /files/etc/hosts
/files/etc/hosts
/files/etc/hosts/1
/files/etc/hosts/1/ipaddr = "127.0.0.1"
/files/etc/hosts/1/canonical = "localhost.localdomain"
/files/etc/hosts/1/alias = "localhost"
/files/etc/hosts/#comment[1] = "Bloc 1"
/files/etc/hosts/#comment[2] = "Bloc 2"
/files/etc/hosts/#comment[3] = "Bloc 3"

Comment Augeas reconnait et peut nommer l’adresse IP (en ipaddr) et le nom (en canonical) ? En fait Augeas utilise un  système de lens permettant de préciser la structure de chaque fichier de configuration et permettant donc de faire la distinction au sein d’une même ligne entre l’adresse IP et le nom. Il est possible, pour les plus courageux, de créer ses propres « lens ».

  • Une fois que l’on a précisé le contexte de nos changements (« /files/etc/hosts ») on indique un tableau de changement à appliquer au fichier. En l’occurrence ici, on insère tout d’abord un élément (ayant pour nom « 01 ») après la ligne de commentaire « Bloc 2 ». Puis on assigne à ce nouvel élément une adresse IP et un nom ;
  • Dernière partie, on vérifie que notre élément n’est pas déjà dans le fichier host afin de s’assurer de l’idempotence de nos run Puppet.

Dans quel cas utiliser ce module :

  • Quand un fichier est géré à la fois par Puppet et manuellement ;
  • Pour des besoins très spécifiques.

Conclusion

Et voilà cinq façons de gérer des fichiers avec Puppet :

  • Via une ressource spécifique ;
  • Via la ressource générique « file »:
    • avec l’attribut « source » ,
    • avec l’attribut « content » ;
  • Via le module concat ;
  • Via la ressource Augeas.

Il existe enfin une dernière méthode que l’on verra dans un article ultérieur en déclarant notre propre ressource de la même manière que la ressource « host ». Mais ça, ça sera pour plus tard !

Si vous avez lu jusqu’ici et que le sujet vous intéresse, n’hésitez pas, Xebia recrute !

Au prochain épisode nous verrons comment gérer les paramètres de nos classes Puppet.

Publié par

Publié par Matthieu Nantern

Matthieu est un consultant Java senior spécialisé en DevOps, Apache Cassandra et la performance des applications. Depuis plus de 8 ans il remplit des missions agiles de développement, d'architecture et de conseils pour diverses sociétés. Récemment il est devenu formateur sur Apache Cassandra. Il travaille actuellement sur un projet de développement d'une plateforme IoT pour un grand groupe français.

Commentaire

1 réponses pour " Puppet recipes 01 : Gérer un fichier avec Puppet "

  1. Publié par , Il y a 3 ans

    bjr
    J’ai un user undef cree par le systeme puppet. Est-ce normal ?

  2. Publié par , Il y a 1 an

    Tres bon article, Augeas est vraiment tres bien expliqué. Merci

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Xebian, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.