¿Cuál es la forma correcta de repetir una receta de Chef (solo)?

8

¿Puede alguien explicarme cómo funciona el chef? Esa es una pregunta bastante amplia, así que para reducirla tengo esta receta muy simple que recorre una lista de usuarios y crea cada uno si aún no existen. No funciona.

Por lo que puedo decir, el ciclo parece estar sucediendo como era de esperar. Una vez que el ciclo se ha completado, se ejecutan mis comandos bash para crear cada usuario, una vez para cada iteración en el ciclo. Sin embargo, cuando se ejecutan los comandos bash, solo parecen tener el valor de usuario de la primera iteración del bucle.

¿Cuál es la forma correcta de escribir una receta que recorra datos variables similares a este ejemplo?

Aquí está la receta:

node[:users].each do |user|
  puts "in loop for #{user['username']}"
  bash "create_user" do
    user "root"
    code do
      puts "running 'useradd' for #{user['username']}"
      "useradd #{user['username']}"
    end
    not_if do
      puts "checking /etc/passwd for #{user['username']}"
      "cat /etc/passwd | grep #{user['username']}"
    end
  end
end

Estoy probando esto usando Vagrant con la siguiente configuración:

Vagrant::Config.run do |config|
  config.vm.box = "precise32"
  config.vm.box_url = "http://files.vagrantup.com/precise32.box"
  config.vm.provision :chef_solo do |chef|
    chef.add_recipe "sample"
    chef.json = {
      :users => [
        {:username => 'testA'},
        {:username => 'testB'},
        {:username => 'testC'},
        {:username => 'testD'},
        {:username => 'testE'},
      ],
    }
  end
end

Los mensajes que generan las declaraciones de venta en la receta tienen este aspecto:

2013-03-08T01:03:46+00:00] INFO: Start handlers complete.
in loop for testA

in loop for testB

in loop for testC

in loop for testD

in loop for testE

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Processing bash[create_user] action run (sample::default line 5)
checking /etc/passwd for testA

[2013-03-08T01:03:46+00:00] INFO: Chef Run complete in 0.026071 seconds
Matthew J Morrison
fuente

Respuestas:

5

haz que tu nombre de script sea único ...

bash "create_user_#{user}" do

FWIW, he usado https://github.com/fnichol/chef-user varias veces, lo que le permite crear / eliminar usuarios basados ​​en atributos y bolsas de datos.

Darrin Holst
fuente
Eso parece tan simple, gracias! La receta de crear usuarios fue solo un ejemplo que estaba usando para entender por qué mi bucle no funcionaba en otras recetas. ¡Gracias de nuevo!
Matthew J Morrison
10

El comportamiento que está viendo puede explicarse al comprender la diferencia entre dos de las etapas clave en una ejecución de cliente Chef: compilación y convergencia.

Durante la fase de "compilación", el cliente Chef ejecuta el código en sus recetas para construir una colección de recursos . Esta es una lista de los Recursos que le ha dicho a Chef que administre en su sistema, junto con su estado objetivo. Por ejemplo, un recurso de Directorio para decir que /tmp/foodebería existir y ser propiedad de root:

directory "/tmp/foo" do
  owner "root"
end

Durante la fase de "convergencia", el cliente Chef utiliza proveedores para cargar el estado actual de cada recurso, luego lo compara con el estado objetivo. Si son diferentes, Chef actualizará el sistema. Para nuestro recurso Directorio, Chef crearía el directorio si no existiera, y cambiaría su propietario a "raíz" si fuera necesario.

Los recursos se identifican de manera única por su nombre y tipo, sería nuestro Directorio directory[/tmp/foo]. Sucederán cosas extrañas cuando tenga dos recursos con el mismo nombre pero con atributos diferentes, eso explica su problema y se puede solucionar con la respuesta de Darrin Holst:

node[:users].each do |user|
  puts "in loop for #{user['username']}"
  bash "create_user_#{user}" do
    user "root"
    code do
      puts "running 'useradd' for #{user['username']}"
      "useradd #{user['username']}"
    end
    not_if do
      puts "checking /etc/passwd for #{user['username']}"
      "cat /etc/passwd | grep #{user['username']}"
    end
  end
end

Sin embargo, en este caso particular, se beneficiaría de utilizar el recurso de usuario de Chef. Aquí hay un reemplazo para su receta (sin los mensajes de depuración):

node[:users].each do |u|
  user u['username'] do
    action :create
  end
end

¿Por qué es esto mejor que un conjunto de recursos bash?

  1. El recurso de usuario funciona de la misma manera en varias plataformas: la misma receta funcionará en los sistemas operativos que usan algo diferente a "useradd" para crear usuarios.
  2. Los proveedores responsables de esto saben cómo verificar si el usuario ya existe, por lo que no necesita not_if.
  3. Los mismos proveedores también se pueden usar para eliminar usuarios, bloquear o desbloquear sus contraseñas y actualizar otros atributos en las cuentas de usuario existentes.

Pero la mejor razón para usar los recursos correctos es que comunica más claramente su intención. Su objetivo probablemente no sea ejecutar un montón de comandos de shell, es asegurarse de que algunos usuarios estén presentes en su sistema. Los comandos utilizados para hacer eso son solo un detalle de implementación.

Los proveedores encapsulan esos detalles, dejándonos libres para centrarnos en describir lo que queremos.

zts
fuente
1
Gracias, esto aclara bastante las cosas. La receta de creación de usuarios fue solo un ejemplo con el que pude jugar para comprender mejor por qué los bucles que tenía en otros lugares no funcionaron como esperaba.
Matthew J Morrison