Chef

Table Of Contents

execute

A resource is a key part of a recipe that defines the actions that can be taken against a piece of the system. These actions are identified during each chef-client run as the resource collection is compiled. Once identified, each resource (in turn) is mapped to a provider, which then configures each piece of the system.

The execute resource is used 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 the not_if and only_if meta parameters to guard the use of this resource for idempotence.

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

Note

The Microsoft Windows platform does not support running as an alternate user unless full credentials (a username and password or equivalent) are specified.

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. Indicates that the command should be run.
:nothing Indicates that the command should not be run. 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 Indicates that a command to create a file will not be run 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. See the Providers section below for the list of providers available to this resource.
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.

Providers

The following providers are available. Use the short name to call the provider from a recipe:

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]"
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 00644
  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