Search code examples
chef-infrachef-recipechef-solo

Is it advisable to use a return statement inside an included recipe?


I have several cookbooks which include several other cookbooks, depending on each cookbook's needs. The included cookbooks declare services which notify other services.

One of the included cookbook common_actions is included in all the other cookbooks as it contains actions common to all.

include_recipe 'cookbook1'
include_recipe 'common_actions'
include_recipe 'cookbook2'
# Several cookbooks have such includes, but 'common_actions'
# is included in almost all the cookbooks.

# cookbook specific conditional logic that should be
# executed only if some condition in 'common_actions' is true

Is it a wise idea to include a conditional return statement in the common_actions cookbook so that it will force the including cookbooks not to be compiled/executed based on that condition? For the purpose of this question, please consider any fake condition like:

if node['IP'] == 'xyz'
    # All including cookbooks should execute only IP is xyz
    return
end

Can a cookbook with such a return statement cause only certain cookbooks to run? Is it advisable?

Note: I am doing this because I do not want to copy-paste the same code in all the other cookbooks.


Solution

  • If I understood you properly, this won't do what you're after because:

    1. A recipe will only be included once, if there's multiples cookbooks in the runlist calling include_recipe A::B then recipe B of cookbook A will only be compiled once, successive calls will be no-op (won't duplicate the recipe resources).
    2. The return statement will end the actual recipe compilation, in your case it will stop the compilation of recipe default in the cookbook common_actions.

    What you can do is using node.run_state, it's a hash available only during the run.
    You can use it to store another hash of conditions from your command_actions cookbookn for example.

    node.run_state['IP_allowed'] = node['IP'] == 'xyz'
    # Probabaly a little silly, but that's the easier I can think of
    if node.chef_environment == 'Test' 
      if node['DoDebugLog'] == true
        node.run_state['LoggerLevel'] = 'debug' 
      else
        node.run_state['LoggerLevel'] = 'info'
    else
      node.run_state['LoggerLevel'] = 'warn'
    end
    

    Now you can use those values in others recipes to control their behavior while still keeping the conditional definition in a central place.

    In a recipe which should not run if node['IP'] is 'xyz' you'll start the recipe with:

    return if node.run_state['IP_allowed']
    

    And on one which should run only if node['IP'] is 'xyz' you'll start the recipe with:

    return unless node.run_state['IP_allowed']
    

    The other value could be used for logging from recipes in different environments like this:

    log "Message to log" do
      level node.run_state['LoggerLevel']
    end