Chef

Table Of Contents

bash

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 bash resource to execute scripts using the Bash interpreter. This resource may also use any of the actions and attributes that are available to the execute resource. 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.

Note

The bash script resource (which is based on the script resource) is different from the ruby_block resource because Ruby code that is run with this resource is created as a temporary file and executed like other script resources, rather than run inline.

Syntax

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

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

where

  • bash tells the chef-client to use the Chef::Resource::Script::Bash 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 name of 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

Actions

This resource has the following actions:

Action Description
:run Default. Use to run a script.
: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
code A quoted (” ”) string of code to be executed.
command The name of the command to be executed. Default value: the name of the resource block. (See “Syntax” section above for more information.)
creates Use to prevent a command from creating a file when that file already exists.
cwd The current working directory.
environment A Hash of environment variables in the form of {"ENV_VARIABLE" => "VALUE"}. (These variables must exist for a command to be run successfully.)
flags One (or more) command line flags that are passed to the interpreter when a command is invoked.
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 explicitly specify a provider. (See “Providers” section below for more information.)
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::Script script When this short name is used, the chef-client will determine the correct provider during the chef-client run.
Chef::Provider::Script::Bash bash The provider that is used with the Bash command interpreter.

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.

Use a named provider to run a script

bash "install_something" do
  user "root"
  cwd "/tmp"
  code <<-EOH
  wget http://www.example.com/tarball.tar.gz
  tar -zxf tarball.tar.gz
  cd tarball
  ./configure
  make
  make install
  EOH
end

Install a file from a remote location using bash

The following is an example of how to install the foo123 module for Nginx. This module adds shell-style functionality to an Nginx configuration file and does the following:

  • Declares three variables
  • Gets the Nginx file from a remote location
  • Installs the file using Bash to the path specified by the src_filepath variable
#  the following code sample is similar to the ``upload_progress_module`` recipe in the ``nginx`` cookbook: https://github.com/opscode-cookbooks/nginx

src_filename = "foo123-nginx-module-v#{node['nginx']['foo123']['version']}.tar.gz"
src_filepath = "#{Chef::Config['file_cache_path']}/#{src_filename}"
extract_path = "#{Chef::Config['file_cache_path']}/nginx_foo123_module/#{node['nginx']['foo123']['checksum']}"

remote_file src_filepath do
  source node['nginx']['foo123']['url']
  checksum node['nginx']['foo123']['checksum']
  owner 'root'
  group 'root'
  mode '0644'
end

bash 'extract_module' do
  cwd ::File.dirname(src_filepath)
  code <<-EOH
    mkdir -p #{extract_path}
    tar xzf #{src_filename} -C #{extract_path}
    mv #{extract_path}/*/* #{extract_path}/
    EOH
  not_if { ::File.exists?(extract_path) }
end

Install an application from git using bash

The following example shows how Bash can be used to install a plug-in for rbenv named ruby-build, which is located in git version source control. First, the application is synchronized, and then Bash changes its working directory to the location in which ruby-build is located, and then runs a command.

 git "#{Chef::Config[:file_cache_path]}/ruby-build" do
   repository "git://github.com/sstephenson/ruby-build.git"
   reference "master"
   action :sync
 end

 bash "install_ruby_build" do
   cwd "#{Chef::Config[:file_cache_path]}/ruby-build"
   user "rbenv"
   group "rbenv"
   code <<-EOH
     ./install.sh
     EOH
   environment 'PREFIX' => "/usr/local"
end

To read more about ruby-build, see here: https://github.com/sstephenson/ruby-build.

Store certain settings

The following recipe shows how an attributes file can be used to store certain settings. An attributes file is located in the attributes/ directory in the same cookbook as the recipe which calls the attributes file. In this example, the attributes file specifies certain settings for Python that are then used across all nodes against which this recipe will run.

Python packages have versions, installation directories, URLs, and checksum files. An attributes file that exists to support this type of recipe would include settings like the following:

default['python']['version'] = '2.7.1'

if python['install_method'] == 'package'
  default['python']['prefix_dir'] = '/usr'
else
  default['python']['prefix_dir'] = '/usr/local'
end

default['python']['url'] = 'http://www.python.org/ftp/python'
default['python']['checksum'] = '80e387...85fd61'

and then the methods in the recipe may refer to these values. A recipe that is used to install Python will need to do the following:

  • Identify each package to be installed (implied in this example, not shown)
  • Define variables for the package version and the install_path
  • Get the package from a remote location, but only if the package does not already exist on the target system
  • Use the bash resource to install the package on the node, but only when the package is not already installed
#  the following code sample comes from the ``oc-nginx`` cookbook on |github|: https://github.com/cookbooks/oc-nginx

version = node['python']['version']
install_path = "#{node['python']['prefix_dir']}/lib/python#{version.split(/(^\d+\.\d+)/)[1]}"

remote_file "#{Chef::Config[:file_cache_path]}/Python-#{version}.tar.bz2" do
  source "#{node['python']['url']}/#{version}/Python-#{version}.tar.bz2"
  checksum node['python']['checksum']
  mode '0644'
  not_if { ::File.exists?(install_path) }
end

bash "build-and-install-python" do
  cwd Chef::Config[:file_cache_path]
  code <<-EOF
    tar -jxvf Python-#{version}.tar.bz2
    (cd Python-#{version} && ./configure #{configure_options})
    (cd Python-#{version} && make && make install)
  EOF
  not_if { ::File.exists?(install_path) }
end