Ordem de receita do chef de execução redux

11

Dada a seguinte receita:

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

Eu esperaria que o ruby_block fosse executado primeiro (porque vem primeiro) e depois pelo remote_file.

Eu gostaria de usar o ruby_block para determinar o URL do remote_file do qual fazer o download, então o pedido é importante.

Se não fosse pelas minhas instruções puts (), eu diria que elas estão sendo executadas na ordem esperada, porque o log diz:

==> 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

Mas acima disso, minhas instruções puts () são as seguintes:

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

Se você acha que os recursos estão sendo executados na ordem esperada, considere esta receita:

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

Este falha da seguinte forma:

==> 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)

A string "no bloco1" não aparece na saída, então o ruby_block nunca foi executado.

Então a questão é, como eu posso forçar o ruby_block a rodar e rodar primeiro?

    
por Dan Tenenbaum 12.06.2014 / 19:57

2 respostas

15

Boa pergunta - os dois exemplos funcionam da maneira que eu esperaria, mas não é imediatamente óbvio o motivo.

Como StephenKing escreveu em sua resposta, a primeira coisa a entender é que as receitas são compiladas (para produzir um conjunto de recursos) e, em seguida, os recursos são convergidos (para efetuar alterações em seu sistema). Estas duas fases são frequentemente intercaladas - alguns dos seus recursos podem ser convergidos antes de o Chef terminar de compilar todas as suas receitas. Erik Hollensbe cobre isso com algum detalhe em sua postagem "The Chef Resource Run Queue" .

Aqui está seu primeiro exemplo novamente:

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

Estes são os passos que o Chef irá seguir ao processar esse exemplo.

  1. Primeiro, a declaração ruby_block é compilada, o que resulta em um recurso chamado ruby_block[block1] sendo adicionado à coleção de recursos. O conteúdo do bloco (a primeira instrução puts ) ainda não foi executado - ele é salvo para ser executado quando esse recurso for convergido.
  2. Em seguida, a declaração remote_file é compilada. Isso resulta em um recurso chamado remote_file[/tmp/foo/] sendo adicionado à coleção de recursos, com uma fonte de " link ". No processo de compilação dessa declaração, a segunda instrução puts será executada - isso tem efeito colateral de imprimir "em remote_file", mas não afeta o recurso que é colocado na coleção de recursos.
  3. Com mais nada para compilar, o Chef começa a convergir os recursos na coleção de recursos. O primeiro é ruby_block[block1] e Chef executa o código ruby no bloco - imprimindo "no bloco1". Depois de terminar a execução do bloco, ele registra uma mensagem dizendo que o recurso foi chamado.
  4. Finalmente, o Chef converge remote_file[/tmp/foo] . Novamente, ele registra uma mensagem (ou duas) associada a essa atividade.

Isso deve produzir a seguinte sequência de saída:

  1. Nada é impresso quando o ruby_block é compilado.
  2. "em remote_file" será impresso enquanto o remote_file é compilado.
  3. "no bloco1" será impresso enquanto o ruby_block estiver convergido.
  4. Uma mensagem de registro do Chef será impressa depois que o ruby_block for convergido.
  5. Outras mensagens de registros do Chef serão impressas durante / depois que o remote_file for convergido.

No seu segundo exemplo:

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

Como no primeiro exemplo, não esperamos que nada seja impresso enquanto o ruby_block é compilado - todo o "bloco" é salvo e seu conteúdo não será executado até que o recurso seja convergido.

A primeira saída que vemos é "in remote_file", como a instrução puts é executada quando Chef compila o recurso remote_file. Na linha seguinte, definimos o parâmetro source para o valor de node.default['test']['foo'] , que aparentemente é {} . Esse não é um valor válido para source , então a execução do Chef termina nesse ponto - antes que o código em ruby_block seja executado.

Portanto, a saída esperada desta receita é:

  1. Nenhuma saída ao compilar o ruby_block
  2. "no remote_file" impresso durante a compilação do remote_file
  3. Um erro devido ao parâmetro source inválido

Espero que isso ajude você a entender o comportamento que está vendo, mas ainda temos um problema para resolver.

Embora você tenha perguntado "como eu posso forçar o ruby_block a ser executado primeiro?", seu comentário para StephenKing sugere que isso não é realmente o que você quer - se você realmente quer que o bloco seja executado primeiro, você pode colocá-lo diretamente no seu código da receita. Como alternativa, você pode usar o método .run_action () forçar o recurso a ser convergido assim que for compilado - mas você diz que ainda há mais recursos que precisam convergir antes que o ruby_block possa ser útil.

Como vimos acima, os recursos não são "executados", são primeiro "compilados" e "convergidos". Com isso em mente, o que você precisa é que o recurso remote_file use alguns dados que não são conhecidos quando compilados, mas que serão conhecidos quando forem convergidos. Em outras palavras, algo como o parâmetro "block" no ruby_block - um trecho de código que não é executado até mais tarde. Algo parecido com isto:

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

Felizmente, isso existe - é chamado Avaliação de Atributo Preguiçoso . Usando esse recurso, seu segundo exemplo ficaria assim:

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

E a saída esperada desta receita?

  1. Nenhuma saída ao compilar o ruby_block
  2. "no remote_file" impresso durante a compilação do remote_file
  3. "no bloco1" impresso ao convergir o ruby_block
  4. Mensagem de registro do chef mostrando que o ruby_block foi convergido
  5. Mensagens de registro do chef mostrando o remote_file foram convergidas
por 13.06.2014 / 17:50
0

O Chef tem uma compilação e uma fase de execução . Eu suponho que o código dentro do ruby_block não seja executado durante a fase de compilação, porque ele está dentro da instrução block (que seria então executada durante a fase de execução). O puts dentro do bloco remote_file , entretanto, está no "nível de atributo" dentro da definição de recurso, que é realmente executada pelo chef (na verdade, afaik, o source node.default... é uma chamada de função).

Então, se eu acertar, o que você quer fazer pode ser feito com o seguinte código:

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

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

Se a configuração de atributos via node.default não funcionar, use node.set .

Mencione também que não leio o atributo através de node.default[] , mas diretamente através de node[] . Caso contrário, o recurso de precedência de atributo do chef não faria sentido.

    
por 12.06.2014 / 21:06

Tags