Question Ordre de recette du chef redux


Compte tenu de la recette suivante:

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

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

J'aimerais utiliser ruby_block pour déterminer l'URL du fichier remote_file à télécharger, l'ordre est donc important.

S'il n'y avait pas mes instructions put (), je supposerais qu'elles sont exécutées dans l'ordre attendu, car le journal indique:

==> default: [2014-06-12T17:49:19+00:00] INFO: ruby_block[block1] called
==> default: [2014-06-12T17:49:19+00:00] INFO: remote_file[/tmp/foo] created file /tmp/foo
==> default: [2014-06-12T17:49:20+00:00] INFO: remote_file[/tmp/foo] updated file contents /tmp/foo

Mais au-dessus de cela, mes déclarations de put () apparaissent comme suit:

==> default: in remote_file
==> default: in block1

Si vous pensez que les ressources sont exécutées dans l'ordre prévu, envisagez la recette suivante:

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 node.default['test']['foo']
end

Celui-ci échoue comme suit:

==> default: [2014-06-12T17:55:38+00:00] ERROR: {} is not a valid `source` parameter for remote_file. `source` must be an absolute URI or an array of URIs.
==> default: [2014-06-12T17:55:38+00:00] FATAL: Chef::Exceptions::ChildConvergeError: Chef run process exited unsuccessfully (exit code 1)

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

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


11
2018-06-12 17:57


origine




Réponses:


Bonne question - vos deux exemples fonctionnent de la manière à laquelle je m'attendais, mais il est difficile de comprendre pourquoi.

Comme StephenKing l'a écrit dans sa réponse, la première chose à comprendre est que les recettes sont compilées (pour produire un ensemble de ressources), puis que les ressources sont convergées (pour apporter des modifications à votre système). Ces deux phases sont souvent imbriquées: certaines de vos ressources peuvent être convergées avant que Chef n'ait terminé la compilation de toutes vos recettes. Erik Hollensbe en parle dans son article "La file d'attente d'exécution des ressources du chef".


Voici votre premier exemple à nouveau:

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 à suivre par Chef pour traiter cet exemple.

  1. Tout d’abord, la déclaration ruby_block est compilée, ce qui donne une ressource appelée ruby_block[block1] être ajouté à la collection de ressources. Le contenu du bloc (le premier puts instruction) ne fonctionne pas encore - il est enregistré pour être exécuté lorsque cette ressource est convergée.
  2. Ensuite, la déclaration remote_file est compilée. Cela se traduit par une ressource appelée remote_file[/tmp/foo/] être ajouté à la collection de ressources, avec une source de "https://yahoo.com". Lors de la compilation de cette déclaration, le deuxième puts instruction sera exécutée - cela aura pour effet d’imprimer "in remote_file", mais cela n’affectera pas la ressource qui est placée dans la collection de ressources.
  3. N'ayant rien d'autre à compiler, Chef commence à faire converger les ressources de la collection de ressources. Le premier est ruby_block[block1], et Chef exécute le code ruby ​​dans le bloc - en imprimant "in block1". Une fois le bloc exécuté, il enregistre un message indiquant que la ressource a été appelée.
  4. Enfin, Chef converge remote_file[/tmp/foo]. De nouveau, il enregistre un message (ou deux) associé à cette activité.

Cela devrait produire la séquence de sortie suivante:

  1. Rien d’imprimé quand le ruby_block est compilé.
  2. "in remote_file" sera imprimé pendant la compilation du fichier remote_file.
  3. "in block1" sera imprimé pendant la convergence du ruby_block.
  4. Un message du journal Chef sera imprimé après la convergence du ruby_block.
  5. Les autres messages du journal Chef seront imprimés pendant / après la convergence du fichier remote_file.

Sur 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
end

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

Comme dans le premier exemple, nous ne prévoyons rien d'imprimer pendant la compilation de ruby_block - le "bloc" entier est enregistré et son contenu ne s'exécutera pas tant que cette ressource n'aura pas été convergée.

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

Par conséquent, le résultat attendu de cette recette est:

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

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

Bien que vous ayez demandé "Comment puis-je forcer ruby_block à s'exécuter en premier?", Votre commentaire à StephenKing suggère que ce n'est pas vraiment ce que vous voulez - si vous voulez vraiment que ce bloc soit exécuté en premier, vous pouvez l'insérer directement dans le code de votre recette. Alternativement, vous pouvez utiliser le Méthode .run_action () forcer la convergence de la ressource dès qu'elle est compilée - mais vous dites qu'il reste encore beaucoup de ressources à converger avant que ruby_block puisse être utile.

Comme nous l'avons vu plus haut, les ressources ne sont pas "exécutées", elles sont d'abord "compilées" puis "convergées". Dans cet esprit, vous avez besoin de la remote_file ressource pour utiliser des données qui ne sont pas connues lors de la compilation, mais qui le seront par la suite. En d'autres termes, quelque chose comme le paramètre "block" dans le ruby_block - un morceau de code qui ne fonctionne que plus tard. Quelque chose comme ça:

remote_file "/tmp/foo" do
    puts "in remote_file"
    # this syntax isn't valid...
    source do 
        node.default['test']['foo']
    end
end

Heureusement, une telle chose existe - elle s'appelle Évaluation d'attribut paresseux. 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'] }
end

Et le résultat attendu de cette recette?

  1. Aucune sortie lors de la compilation du ruby_block
  2. "in remote_file" imprimé lors de la compilation du remote_file
  3. "in block1" imprimé en faisant converger le ruby_block
  4. Message du journal du chef indiquant que le ruby_block a été convergé
  5. Messages du journal du chef indiquant que le fichier distant a été convergé

15
2018-06-13 15:50



Réponse fantastique! FYI - remote_file a un bogue avec l'évaluation d'attribut lazy qui sera corrigé dans chef-client v11.14 +. Voir tickets.opscode.com/browse/CHEF-5162 - Steve Jansen
Hah! Nous apprenons ici que je n’ai jamais utilisé la valorisation des attributs paresseux avec remote_file :) Merci d’avoir signalé ce billet: lancez 11.14! - zts


Chef a un compiler et une phase 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 du block instruction (qui sera ensuite exécutée pendant la phase d’exécution). le puts à l'intérieur de remote_file Cependant, le bloc se situe au "niveau de l'attribut" dans la définition de la ressource, qui est réellement exécutée par le chef (en réalité, source node.default... est un appel de fonction).

Donc si je comprends bien, la chose que vous voulez faire peut être faite avec le code suivant:

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

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

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

Mentionnez également que je ne lis pas l'attribut à travers node.default[], mais directement par node[]. Sinon le priorité d'attribut fonction de chef n'aurait pas de sens.


0
2018-06-12 19:06



OK, mon exemple était trop minime pour illustrer le problème. Imaginez qu'il existe quelques ressources en haut de mon exemple et que ce ne soit que lorsque ces ressources seront exécutées que je connaisse l'URL à partir de laquelle je souhaite télécharger. Je ne peux donc pas définir l'URL dans un code ruby ​​"nu" (en dehors d'une ressource) car je ne saurai pas ce qu'il devrait être. J'ai essayé d'utiliser des ressources générées dynamiquement, comme illustré dans stackoverflow.com/questions/13019925/… mais je n'aime pas que cela nécessite une nouvelle syntaxe pour quelque chose qui doit être assez commun. - Dan Tenenbaum