11 votes

Chef ordre d'exécution de la recette redux

Étant donnée la recette suivante :

ruby_block "block1" do
    block do
        puts "dans le block1"
    end
    action :create
end

remote_file "/tmp/foo" do
    puts "dans remote_file"
    source "https://yahoo.com"
end

Je m'attends à ce que le ruby_block s'exécute en premier (car il vient en premier) puis le remote_file.

Je voudrais utiliser le ruby_block pour déterminer l'URL du remote_file à télécharger, donc l'ordre est important.

Si ce n'était pas pour mes instructions puts(), je supposerais que ces ressources sont exécutées dans l'ordre attendu, car le journal indique :

==> default: [2014-06-12T17:49:19+00:00] INFO: ruby_block[block1] appelé
==> default: [2014-06-12T17:49:19+00:00] INFO: remote_file[/tmp/foo] fichier créé /tmp/foo
==> default: [2014-06-12T17:49:20+00:00] INFO: remote_file[/tmp/foo] contenu du fichier mis à jour /tmp/foo

Mais au-dessus de cela, mes instructions puts() apparaissent comme suit :

==> default: dans remote_file
==> default: dans le block1

Si vous pensez que les ressources sont exécutées dans l'ordre attendu, considérez cette recette :

ruby_block "block1" do
    block do
        node.default['test'] = {}
        node.default['test']['foo'] ='https://google.com'
        puts "dans le block1"
    end
    action :create
end

remote_file "/tmp/foo" do
    puts "dans remote_file"
    source node.default['test']['foo']

Celui-ci échoue comme suit :

==> default: [2014-06-12T17:55:38+00:00] ERREUR : {} n'est pas un paramètre `source` valide pour remote_file. `source` doit être une URI absolue ou un tableau d'URIs.
==> default: [2014-06-12T17:55:38+00:00] FATALE : Chef::Exceptions::ChildConvergeError : Le processus d'exécution de Chef s'est terminé de manière non satisfaisante (code de sortie 1)

La chaîne "dans le block1" n'apparaît pas dans la sortie, donc le ruby_block n'a jamais été exécuté.

La question est donc : comment puis-je forcer le ruby_block à s'exécuter en premier ?

15voto

zts Points 945

Bonne question - les deux exemples fonctionnent comme je m'y attendrais, mais il n'est pas immédiatement évident pourquoi.

Comme l'a écrit StephenKing dans sa réponse, la première chose à comprendre est que les recettes sont compilées (pour produire un ensemble de ressources), puis les ressources sont convergées (pour effectuer des modifications sur votre système). Ces deux phases sont souvent entremêlées - certaines de vos ressources peuvent être convergées avant que Chef n'ait fini de compiler toutes vos recettes. Erik Hollensbe couvre cela en détail dans son article "The Chef Resource Run Queue".


Voici à nouveau votre premier exemple:

ruby_block "block1" do
    block do
        puts "in block1"
    end
    action :create
end    

remote_file "/tmp/foo" do
    puts "in remote_file"
    source "https://yahoo.com"
end

Voici les étapes que Chef suivra pour traiter cet exemple.

  1. Tout d'abord, la déclaration de ruby_block est compilée, ce qui donne une ressource appelée ruby_block[block1] ajoutée à la collection de ressources. Le contenu du bloc (la première déclaration de puts) ne s'exécute pas encore - il est enregistré pour s'exécuter lorsque cette ressource sera convergée.
  2. Ensuite, la déclaration de remote_file est compilée. Cela donne une ressource appelée remote_file[/tmp/foo/] ajoutée à la collection de ressources, avec une source de "https://yahoo.com". Au cours de la compilation de cette déclaration, la deuxième déclaration de puts sera exécutée - cela a pour effet secondaire d'imprimer "in remote_file", mais cela n'affecte pas la ressource qui est placée dans la collection de ressources.
  3. N'ayant rien d'autre à compiler, Chef commence à converger les ressources dans la collection de ressources. Le premier est ruby_block[block1], et Chef exécute le code ruby dans le bloc - en imprimant "in block1". Après avoir terminé d'exécuter le bloc, il enregistre un message pour dire que la ressource a été appelée.
  4. Enfin, Chef converge remote_file[/tmp/foo]. Encore une fois, il enregistre un message (ou deux) associé à cette activité.

Cela devrait produire la séquence de sortie suivante:

  1. Rien n'est imprimé lorsque le ruby_block est compilé.
  2. "in remote_file" sera imprimé pendant que le remote_file est compilé.
  3. "in block1" sera imprimé pendant que le ruby_block est convergé.
  4. Un message de journal Chef sera imprimé après que le ruby_block ait été convergé.
  5. D'autres messages de journal Chef seront imprimés pendant/après que le remote_file soit convergé.

Pour votre deuxième exemple:

ruby_block "block1" do
    block do
        node.default['test'] = {}
        node.default['test']['foo'] ='https://google.com'
        puts "in block1"
    end
    action :create

Comme pour le premier exemple, nous ne nous attendons pas à ce que quoi que ce soit soit imprimé pendant que le ruby_block est compilé - tout le "bloc" est sauvegardé, et son contenu ne s'exécutera pas tant que cette ressource ne sera pas convergée.

La première sortie que nous voyons est "in remote_file", car l'instruction puts est exécutée lorsque Chef compile la ressource remote_file. À la ligne suivante, nous définissons le paramètre source sur la valeur de node.default['test']['foo'], qui est apparemment {}. Ce n'est pas une valeur valide pour source, donc l'exécution de Chef se termine à ce moment-là - avant que le code dans le ruby_block ne soit jamais exécuté.

Par conséquent, la sortie attendue de cette recette est:

  1. Aucune sortie pendant la compilation du ruby_block
  2. "in remote_file" imprimé lors de la compilation du remote_file
  3. Une erreur due au paramètre source invalide

J'espère que cela vous aide à comprendre le comportement que vous observez, mais nous avons toujours un problème à résoudre.

Vous avez demandé "comment puis-je forcer l'exécution du ruby_block en premier?", mais votre commentaire à StephenKing suggère que ce n'est pas vraiment ce que vous voulez - si vous vouliez vraiment que ce bloc s'exécute en premier, vous pourriez le mettre directement dans votre code de recette. Alternativement, vous pourriez utiliser la méthode .run_action() pour forcer la ressource à être convergée dès qu'elle est compilée - mais vous dites qu'il y a encore d'autres ressources qui doivent converger avant que le ruby_block ne soit utile.

Comme nous l'avons vu ci-dessus, les ressources ne sont pas "exécutées", elles sont d'abord "compilées", puis "convergées". Avec cela à l'esprit, ce dont vous avez besoin est que la ressource remote_file utilise certaines données qui ne sont pas connues lorsqu'elle est compilée, mais qui le seront lorsqu'elle sera convergée. En d'autres termes, quelque chose comme le paramètre "block" dans le ruby_block - un morceau de code qui ne s'exécute pas immédiatement. Quelque chose comme ceci:

remote_file "/tmp/foo" do
    puts "in remote_file"
    # cette syntaxe n'est pas valide...
    source do 
        node.default['test']['foo']
    end

Heureusement, une telle chose existe - c'est ce qu'on appelle l'Évaluation Paresseuse d'Attribut. En utilisant cette fonctionnalité, votre deuxième exemple ressemblerait à ceci:

ruby_block "block1" do
    block do
        node.default['test'] = {}
        node.default['test']['foo'] = 'https://google.com'
        puts "in block1"
    end
    action :create
end

remote_file "/tmp/foo" do
    puts "in remote_file"
    source lazy { node['test']['foo'] }

Et la sortie attendue de cette recette?

  1. Aucune sortie pendant la compilation du ruby_block
  2. "in remote_file" imprimé lors de la compilation du remote_file
  3. "in block1" imprimé lors de la convergence du ruby_block
  4. Message de journal Chef montrant que le ruby_block a été convergé
  5. Messages de journal Chef montrant que le remote_file a été convergé

0voto

StephenKing Points 922

Chef a une phase de compilation et d'exécution. Je suppose que le code à l'intérieur du ruby_block n'est pas exécuté pendant la phase de compilation, car il se trouve à l'intérieur de l'instruction block (qui serait ensuite exécutée pendant la phase d'exécution). Le puts à l'intérieur du bloc remote_file, cependant, se situe au niveau de l'"attribut" à l'intérieur de la définition de la ressource, qui est vraiment exécuté par chef (en fait, à ce que je sache, le source node.default... est un appel de fonction).

Donc si je comprends bien, ce que vous voulez faire peut être réalisé avec le code suivant :

node.default['test']['foo'] ='https://google.com'

remote_file "/tmp/foo" do
  source node['test']['foo']

Si définir des attributs via node.default ne fonctionne pas, utilisez node.set.

Je tiens également à mentionner que je ne lis pas l'attribut via node.default[], mais directement via node[]. Sinon, la fonction de précédence des attributs de chef n'aurait pas de sens.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X