Chef

Table Of Contents

execute

A resource defines the desired state for a single configuration item present on a node that is under management by Chef. A resource collection—one (or more) individual resources—defines the desired state for the entire node. During every chef-client run, the current state of each resource is tested, after which the chef-client will take any steps that are necessary to repair the node and bring it back into the desired state.

Use the execute resource to execute a command. Commands that are executed with this resource are (by their nature) not idempotent, as they are typically unique to the environment in which they are run. Use not_if and only_if to guard this resource for idempotence.

Use the script resource to execute a script using a specific interpreter (Ruby, Python, Perl, csh, or Bash).

Syntax

The syntax for using the execute resource in a recipe is as follows:

execute "name" do
  attribute "value" # see attributes section below
  ...
  action :action # see actions section below
end

where

  • execute tells the chef-client to use the Chef::Provider::Execute provider during the chef-client run
  • name is the name of the resource block; when the command attribute is not specified as part of a recipe, name is also the command to be executed
  • attribute is zero (or more) of the attributes that are available for this resource
  • :action is the step that the resource will ask the provider to take during the chef-client run

The following is an example of how the execute resource can work when used in a recipe. In this example, a whitespace array is used to identify the names of the pets that will then be fed (by the command that is run):

%w{rover fido bubbers}.each do |pet_name|
  execute "feed_pet_#{pet_name}" do
    command "echo 'Feeding: #{pet_name}'; touch '/tmp/#{pet_name}'"
    not_if { ::File.exists?("/tmp/#{pet_name}")}
  end
end

Note

When using the not_if and only_if guards with the execute resource, the current working directory attribute (cwd) is not inherited from the resource. For example:

execute 'bundle install' do
  cwd '/myapp'
  not_if 'bundle check' # This is not run inside /myapp
end

Actions

This resource has the following actions:

Action Description
:run Default. Use to run a command.
:nothing Use to prevent a command from running. This action is used to specify that a command is run only when another resource notifies it.

Attributes

This resource has the following attributes:

Attribute Description
command The name of the command to be executed. Default value: the name of the resource block (see Syntax section above).
creates Use to prevent a command from creating a file when that file already exists.
cwd The current working directory from which a command is run.
environment A Hash of environment variables in the form of {"ENV_VARIABLE" => "VALUE"}. (These variables must exist for a command to be run successfully.)
group The group name or group ID that must be changed before running a command.
path An array of paths to use when searching for a command. These paths are not added to the command’s environment $PATH. The default value uses the system path.
provider Optional. Use to specify a provider by using its long name. For example: provider Chef::Provider::Long::Name.
returns The return value for a command. This may be an array of accepted values. An exception is raised when the return value(s) do not match. Default value: 0.
timeout The amount of time (in seconds) a command will wait before timing out. Default value: 3600.
user The user name or user ID that should be changed before running a command.
umask The file mode creation mask, or umask.

Guards

A guard attribute can be used to evaluate the state of a node during the execution phase of the chef-client run. Based on the results of this evaluation, a guard attribute is then used to tell the chef-client if it should continue executing a resource. A guard attribute accepts either a string value or a Ruby block value:

  • A string is executed as a shell command. If the command returns 0, the guard is applied. If the command returns any other value, then the guard attribute is not applied.
  • A block is executed as Ruby code that must return either true or false. If the block returns true, the guard attribute is applied. If the block returns false, the guard attribute is not applied.

A guard attribute is useful for ensuring that a resource is idempotent by allowing that resource to test for the desired state as it is being executed, and then if the desired state is present, for the chef-client to do nothing.

Attributes

The following attributes can be used to define a guard that is evaluated during the execution phase of the chef-client run:

Guard Description
not_if Use to prevent a resource from executing when the condition returns true.
only_if Use to allow a resource to execute only if the condition returns true.

Arguments

The following arguments can be used with the not_if or only_if guard attributes:

Argument Description
:user

Use to specify the user that a command will run as. For example:

not_if "grep adam /etc/passwd", :user => 'adam'
:group

Use to specify the group that a command will run as. For example:

not_if "grep adam /etc/passwd", :group => 'adam'
:environment

Use to specify a Hash of environment variables to be set. For example:

not_if "grep adam /etc/passwd", :environment => { 'HOME' => "/home/adam" }
:cwd

Use to set the current working directory before running a command. For example:

not_if "grep adam passwd", :cwd => '/etc'
:timeout

Use to set a timeout for a command. For example:

not_if "sleep 10000", :timeout => 10

Providers

The chef-client will attempt to determine the correct provider during the chef-client run, and then choose the best/correct provider based on configuration data collected at the start of the chef-client run. In general, a specific provider does not need to be specified. In situations where a specific provider must be specified, there are two approaches:

  • Using a provider’s short name as the name of the resource, e.g. short_name "foo" do
  • Using the provider attribute to specify the long name as an attribute of a resource, e.g. provider Chef::Provider::Long::Name

Whenever possible, try using the short name first, and then use the long name when necessary.

This resource has the following providers:

Long name Short name Notes
Chef::Provider::Execute execute The default provider for all platforms.

Examples

The following examples demonstrate various approaches for using resources in recipes. If you want to see examples of how Chef uses resources in recipes, take a closer look at the cookbooks that Chef authors and maintains: https://github.com/opscode-cookbooks.

Run a command upon notification

execute "slapadd" do
  command "slapadd < /tmp/something.ldif"
  creates "/var/lib/slapd/uid.bdb"
  action :nothing
end

template "/tmp/something.ldif" do
  source "something.ldif"
  notifies :run, "execute[slapadd]", :immediately
end

Run a touch file only once while running a command

execute "upgrade script" do
  command "php upgrade-application.php && touch /var/application/.upgraded"
  creates "/var/application/.upgraded"
  action :run
end

Run a command which requires an environment variable

execute "slapadd" do
  command "slapadd < /tmp/something.ldif"
  creates "/var/lib/slapd/uid.bdb"
  action :run
  environment ({'HOME' => '/home/myhome'})
end

Delete a repository using yum to scrub the cache

# the following code sample thanks to gaffneyc @ https://gist.github.com/918711

execute "clean-yum-cache" do
  command "yum clean all"
  action :nothing
end

file "/etc/yum.repos.d/bad.repo" do
  action :delete
  notifies :run, "execute[clean-yum-cache]", :immediately
  notifies :create, "ruby_block[reload-internal-yum-cache]", :immediately
end

Install repositories from a file, trigger a command, and force the internal cache to reload

The following example shows how to install new yum repositories from a file, where the installation of the repository triggers a creation of the yum cache that forces the internal cache for the chef-client to reload:

execute "create-yum-cache" do
 command "yum -q makecache"
 action :nothing
end

ruby_block "reload-internal-yum-cache" do
  block do
    Chef::Provider::Package::Yum::YumCache.instance.reload
  end
  action :nothing
end

cookbook_file "/etc/yum.repos.d/custom.repo" do
  source "custom"
  mode "0644"
  notifies :run, "execute[create-yum-cache]", :immediately
  notifies :create, "ruby_block[reload-internal-yum-cache]", :immediately
end

Prevent restart and reconfigure if configuration is broken

Use the :nothing common action to prevent an application from restarting, and then use the subscribes notification to ask the broken configuration to be reconfigured immediately:

execute "test-nagios-config" do
  command "nagios3 --verify-config"
  action :nothing
  subscribes :run, "template[/etc/nagios3/configures-nagios.conf]", :immediately
end

Notify in a specific order

To notify multiple resources, and then have these resources run in a certain order, do something like the following:

execute 'foo' do
  command '...'
  notifies :run, 'template[baz]', :immediately
  notifies :install, 'package[bar]', :immediately
  notifies :run, 'execute[final]', :immediately
end

template 'baz' do
  ...
  notifies :run, 'execute[restart_baz]', :immediately
end

package 'bar'

execute 'restart_baz'

execute 'final' do
  command '...'
end

where the sequencing will be in the same order as the resources are listed in the recipe: execute 'foo', template 'baz', execute [restart_baz], package 'bar', and execute 'final'.

Execute a command using a template

The following example shows how to set up IPv4 packet forwarding using the execute resource to run a command named forward_ipv4 that uses a template defined by the template resource:

execute "forward_ipv4" do
  command "echo > /proc/.../ipv4/ip_forward"
  action :nothing
end

template "/etc/file_name.conf" do
  source "routing/file_name.conf.erb"
  notifies :run, 'execute[forward_ipv4]', :delayed
end

where the command attribute for the execute resource contains the command that is to be run and the source attribute for the template resource specifies which template to use. The notifies attribute for the template specifies that the execute[forward_ipv4] (which is defined by the execute resource) should be queued up and run at the end of the chef-client run.

Add a rule to an IP table

The following example shows how to add a rule named test_rule to an IP table using the execute resource to run a command using a template that is defined by the template resource:

execute 'test_rule' do
  command "command_to_run
    --option value
    ...
    --option value
    --source #{node[:name_of_node][:ipsec][:local][:subnet]}
    -j test_rule"
  action :nothing
end

template "/etc/file_name.local" do
  source "routing/file_name.local.erb"
  notifies :run, 'execute[test_rule]', :delayed
end

where the command attribute for the execute resource contains the command that is to be run and the source attribute for the template resource specifies which template to use. The notifies attribute for the template specifies that the execute[test_rule] (which is defined by the execute resource) should be queued up and run at the end of the chef-client run.

Stop a service, do stuff, and then restart it

The following example shows how to use the execute, service, and mount resources together to ensure that a node running on Amazon EC2 is running MySQL. This example does the following:

  • Checks to see if the Amazon EC2 node has MySQL
  • If the node has MySQL, stops MySQL
  • Installs MySQL
  • Mounts the node
  • Restarts MySQL
#  the following code sample comes from the ``server_ec2`` recipe in the following cookbook: https://github.com/opscode-cookbooks/mysql

if (node.attribute?('ec2') && ! FileTest.directory?(node['mysql']['ec2_path']))

  service "mysql" do
    action :stop
  end

  execute "install-mysql" do
    command "mv #{node['mysql']['data_dir']} #{node['mysql']['ec2_path']}"
    not_if do FileTest.directory?(node['mysql']['ec2_path']) end
  end

  [node['mysql']['ec2_path'], node['mysql']['data_dir']].each do |dir|
    directory dir do
      owner "mysql"
      group "mysql"
    end
  end

  mount node['mysql']['data_dir'] do
    device node['mysql']['ec2_path']
    fstype "none"
    options "bind,rw"
    action [:mount, :enable]
  end

  service "mysql" do
    action :start
  end

end

where

  • the two service resources are used to stop, and then restart the MySQL service
  • the execute resource is used to install MySQL
  • the mount resource is used to mount the node and enable MySQL

Use the platform_family? method

The following is an example of using the platform_family? method in the Recipe DSL to create a variable that can be used with other resources in the same recipe. In this example, platform_family? is being used to ensure that a specific binary is used for a specific platform before using the remote_file resource to download a file from a remote location, and then using the execute resource to install that file by running a command.

if platform_family?("rhel")
  pip_binary = "/usr/bin/pip"
else
  pip_binary = "/usr/local/bin/pip"
end

remote_file "#{Chef::Config[:file_cache_path]}/distribute_setup.py" do
  source "http://python-distribute.org/distribute_setup.py"
  mode "0644"
  not_if { ::File.exists?(pip_binary) }
end

execute "install-pip" do
  cwd Chef::Config[:file_cache_path]
  command <<-EOF
    # command for installing Python goes here
    EOF
  not_if { ::File.exists?(pip_binary) }
end

where a command for installing Python might look something like:

#{node['python']['binary']} distribute_setup.py
#{::File.dirname(pip_binary)}/easy_install pip

Control a service using the execute resource

Warning

This is an example of something that should NOT be done. Use the service resource to control a service, not the execute resource.

Do something like this:

service "tomcat" do
  action :start
end

and NOT something like this:

execute "start-tomcat" do
  command "/etc/init.d/tomcat6 start"
  action :run
end

There is no reason to use the execute resource to control a service because the service resource exposes the start_command attribute directly, which gives a recipe full control over the command issued in a much cleaner, more direct manner.

Use the search recipe DSL method to find users

The following example shows how to use the search method in the Recipe DSL to search for users:

#  the following code sample comes from the openvpn cookbook: https://github.com/opscode-cookbooks/openvpn

search("users", "*:*") do |u|
  execute "generate-openvpn-#{u['id']}" do
    command "./pkitool #{u['id']}"
    cwd "/etc/openvpn/easy-rsa"
    environment(
      'EASY_RSA' => '/etc/openvpn/easy-rsa',
      'KEY_CONFIG' => '/etc/openvpn/easy-rsa/openssl.cnf',
      'KEY_DIR' => node["openvpn"]["key_dir"],
      'CA_EXPIRE' => node["openvpn"]["key"]["ca_expire"].to_s,
      'KEY_EXPIRE' => node["openvpn"]["key"]["expire"].to_s,
      'KEY_SIZE' => node["openvpn"]["key"]["size"].to_s,
      'KEY_COUNTRY' => node["openvpn"]["key"]["country"],
      'KEY_PROVINCE' => node["openvpn"]["key"]["province"],
      'KEY_CITY' => node["openvpn"]["key"]["city"],
      'KEY_ORG' => node["openvpn"]["key"]["org"],
      'KEY_EMAIL' => node["openvpn"]["key"]["email"]
    )
    not_if { ::File.exists?("#{node["openvpn"]["key_dir"]}/#{u['id']}.crt") }
  end

  %w{ conf ovpn }.each do |ext|
    template "#{node["openvpn"]["key_dir"]}/#{u['id']}.#{ext}" do
      source "client.conf.erb"
      variables :username => u['id']
    end
  end

  execute "create-openvpn-tar-#{u['id']}" do
    cwd node["openvpn"]["key_dir"]
    command <<-EOH
      tar zcf #{u['id']}.tar.gz \
      ca.crt #{u['id']}.crt #{u['id']}.key \
      #{u['id']}.conf #{u['id']}.ovpn \
    EOH
    not_if { ::File.exists?("#{node["openvpn"]["key_dir"]}/#{u['id']}.tar.gz") }
  end
end

where

  • the search will use both of the execute resources, unless the condition specified by the not_if commands are met
  • the environments attribute in the first execute resource is being used to define values that appear as variables in the OpenVPN configuration
  • the template resource tells the chef-client which template to use

Enable remote login for Mac OS X

execute "enable ssh" do
  command "/usr/sbin/systemsetup -setremotelogin on"
  not_if "/usr/sbin/systemsetup -getremotelogin | /usr/bin/grep On"
  action :run
end

Execute code immediately, based on the template resource

By default, notifications are :delayed, that is they are queued up as they are triggered, and then executed at the very end of a chef-client run. To run an action immediately, use :immediately:

template "/etc/nagios3/configures-nagios.conf" do
  # other parameters
  notifies :run, "execute[test-nagios-config]", :immediately
end

and then the chef-client would immediately run the following:

execute "test-nagios-config" do
  command "nagios3 --verify-config"
  action :nothing
end

Sourcing a file

The execute resource cannot be used to source a file (e.g. command "source filename"). The following example will fail because source is not an executable:

execute "foo" do
  command "source /tmp/foo.sh"
end

Instead, use the script resource or one of the script-based resources (bash, csh, perl, python, or ruby). For example:

bash "foo" do
  code "source /tmp/foo.sh"
end

Run a Knife command

execute 'create_user' do
  command <<-EOM.gsub(/\s+/, ' ').strip!
        knife user create #{user}
      --admin
      --password password
      --disable-editing
      --file /home/vagrant/.chef/user.pem
      --config /tmp/knife-admin.rb
    EOM
end