Search code examples
chef-infrachef-recipeaws-opsworks

How to guard whole recipes without repeating guards?


I'm trying to guard against re-executing of my recipe resources, when included in other recipes, using a lock file to prevent re-downloading and extracting files.

For example, consider a recipe like this:

execute "step1-download" do
  command "step1-command"
  not_if do ::File.exists?('/var/recipe-already-executed') end
end

execute "step2-extract" do
  command "step2-command"
  not_if do ::File.exists?('/var/recipe-already-executed') end
end

Is there a way to guard the recipe as a whole, to avoid repeating the guard in every resource of the recipe?


Solution

  • There are a number of things to consider in this example. First, consider the following:

    You are doing something like this:

    execute 'create directory' do
      command 'mkdir -p /path/on/disk && touch created-directory'
      not_if  { File.exist?('created-directory') }
    end
    

    If I delete the directory, but not the "lockfile", then this block will not execute. This could be problematic, and is the reason why we discourage such mechanisms. You should be doing something like this instead:

    execute 'create directory' do
      command 'mkdir /path/on/disk'
      not_if  { File.directory?('/path/on/disk') }
    end
    

    However, this is entirely unnecessary, because Chef has a built-in resource for handling such situations:

    directory '/path/on/disk'
    

    From your example, it seems like you are trying to download, extract, and install a resource. You should really leverage the built-in Chef resources for this, as then you need-not worry about said guards:

    remote_file "#{Chef::Config[:file_cache_path]}/whatever.tar.gz" do
      source   'https://something.com/file.tar.gz'
      action   :create_if_missing
      notifies :run, 'execute[extract]', :immediately
    end
    
    execute 'extract' do
      command  "tar -xzf #{Chef::Config[:file_cache_path]}/whatever.tar.gz"
      action   :nothing
      notifies :run, 'execute[compile]', :immediately
    end
    
    execute 'compile' do
      command "./configure && make && make install"
      action  :nothing
    end
    

    You should also look at the remote_install community cookbook, which provides a handy resource:

    remote_install 'bash' do
      source 'http://ftp.gnu.org/gnu/bash/bash-4.3.tar.gz'
      version '4.3'
      checksum 'afc687a28e0e24dc21b988fa159ff9dbcf6b7caa92ade8645cc6d5605cd024d4'
      build_command './configure'
      compile_command 'make'
      install_command 'make install'
    en