summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Higgins <pete@peterhiggins.org>2020-05-26 13:59:29 -0700
committerPete Higgins <pete@peterhiggins.org>2020-05-26 13:59:29 -0700
commit7491610f0dea9ce2695bee936312111880571162 (patch)
treedebef1fdf20ecfb76139727bda8e0d0888083144
parent6b5baedecd8d6b89e9f6e96a797c3c3bf3341118 (diff)
downloadchef-7491610f0dea9ce2695bee936312111880571162.tar.gz
Add examples for execute resource.
Signed-off-by: Pete Higgins <pete@peterhiggins.org>
-rw-r--r--lib/chef/resource/execute.rb521
1 files changed, 521 insertions, 0 deletions
diff --git a/lib/chef/resource/execute.rb b/lib/chef/resource/execute.rb
index aecf272444..18d652a1f2 100644
--- a/lib/chef/resource/execute.rb
+++ b/lib/chef/resource/execute.rb
@@ -37,6 +37,527 @@ class Chef
interpreter (Ruby, Python, Perl, csh, or Bash).'
DESC
+ examples <<~EXAMPLES
+ **Run a command upon notification**:
+
+ ```ruby
+ 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**:
+
+ ```ruby
+ 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**:
+
+ ```ruby
+ 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**:
+
+ ```ruby
+ # 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 Chef Infra Client to reload.
+
+ ```ruby
+ 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 '0755'
+ 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` action (common to all resources) to prevent the test from
+ starting automatically, and then use the `subscribes` notification to run a
+ configuration test when a change to the template is detected.
+
+ ```ruby
+ 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.
+
+ ```ruby
+ execute 'foo' do
+ command '...'
+ notifies :create, '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.
+
+ ```ruby
+ 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` property for the **execute** resource contains the command
+ that is to be run and the `source` property for the **template** resource
+ specifies which template to use. The `notifies` property 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 a Chef Infra 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:
+
+ ```ruby
+ 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` property for the **execute** resource contains the command
+ that is to be run and the `source` property for the **template** resource
+ specifies which template to use. The `notifies` property 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 a Chef Infra 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
+
+ ```ruby
+ # the following code sample comes from the ``server_ec2``
+ # recipe in the following cookbook:
+ # https://github.com/chef-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.
+
+ ```ruby
+ 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 '0755'
+
+ not_if { File.exist?(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.exist?(pip_binary) }
+ end
+ ```
+
+ where a command for installing Python might look something like:
+
+ ```ruby
+ #{node['python']['binary']} distribute_setup.py #{::File.dirname(pip_binary)}/easy_install pip
+ ```
+
+ **Control a service using the execute resource**:
+
+ <div class="admonition-warning">
+ <p class="admonition-warning-title">Warning</p>
+ <div class="admonition-warning-text">
+ This is an example of something that should NOT be done. Use the **service**
+ resource to control a service, not the **execute** resource.
+ </div>
+ </div>
+
+ Do something like this:
+
+ ```ruby
+ service 'tomcat' do
+ action :start
+ end
+ ```
+
+ and NOT something like this:
+
+ ```ruby
+ 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` property 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:
+
+ ```ruby
+ # the following code sample comes from the openvpn cookbook: https://github.com/chef-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.exist?("#{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.exist?("#{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` property in the first **execute** resource is being used to
+ define values that appear as variables in the OpenVPN configuration
+ - the **template** resource tells Chef Infra Client which template to use
+
+ **Enable remote login for macOS**:
+
+ ```ruby
+ 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 Infra Client run. To run
+ kan action immediately, use `:immediately`:
+
+ ```ruby
+ template '/etc/nagios3/configures-nagios.conf' do
+ # other parameters
+ notifies :run, 'execute[test-nagios-config]', :immediately
+ end
+ ```
+
+ and then Chef Infra Client would immediately run the following:
+
+ ```ruby
+ 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:
+
+ ```ruby
+ 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:
+
+ ```ruby
+ bash 'foo' do
+ code 'source /tmp/foo.sh'
+ end
+ ```
+
+ **Run a Knife command**:
+
+ ```ruby
+ execute 'create_user' do
+ command <<~EOM
+ knife user create #{user}
+ --admin
+ --password password
+ --disable-editing
+ --file /home/vagrant/.chef/user.pem
+ --config /tmp/knife-admin.rb
+ EOM
+ end
+ ```
+
+ **Run install command into virtual environment**:
+
+ The following example shows how to install a lightweight JavaScript framework
+ into Vagrant:
+
+ ```ruby
+ execute "install q and zombiejs" do
+ cwd "/home/vagrant"
+ user "vagrant"
+ environment ({'HOME' => '/home/vagrant', 'USER' => 'vagrant'})
+ command "npm install -g q zombie should mocha coffee-script"
+ action :run
+ end
+ ```
+
+ **Run a command as a named user**:
+
+ The following example shows how to run `bundle install` from a Chef Infra Client
+ run as a specific user. This will put the gem into the path of the user
+ (`vagrant`) instead of the root user (under which the Chef Infra Client runs):
+
+ ```ruby
+ execute '/opt/chefdk/embedded/bin/bundle install' do
+ cwd node['chef_workstation']['bundler_path']
+ user node['chef_workstation']['user']
+
+ environment ({
+ 'HOME' => "/home/#{node['chef_workstation']['user']}",
+ 'USER' => node['chef_workstation']['user']
+ })
+ not_if 'bundle check'
+ end
+ ```
+
+ **Run a command as an alternate user**:
+
+ *Note*: When Chef is running as a service, this feature requires that the user
+ that Chef runs as has 'SeAssignPrimaryTokenPrivilege' (aka
+ 'SE_ASSIGNPRIMARYTOKEN_NAME') user right. By default only LocalSystem and
+ NetworkService have this right when running as a service. This is necessary
+ even if the user is an Administrator.
+
+ This right can be added and checked in a recipe using this example:
+
+ ```ruby
+ # Add 'SeAssignPrimaryTokenPrivilege' for the user
+ Chef::ReservedNames::Win32::Security.add_account_right('<user>', 'SeAssignPrimaryTokenPrivilege')
+
+ # Check if the user has 'SeAssignPrimaryTokenPrivilege' rights
+ Chef::ReservedNames::Win32::Security.get_account_right('<user>').include?('SeAssignPrimaryTokenPrivilege')
+ ```
+
+ The following example shows how to run `mkdir test_dir` from a Chef Infra Client
+ run as an alternate user.
+
+ ```ruby
+ # Passing only username and password
+ execute 'mkdir test_dir' do
+ cwd Chef::Config[:file_cache_path]
+
+ user "username"
+ password "password"
+ end
+
+ # Passing username and domain
+ execute 'mkdir test_dir' do
+ cwd Chef::Config[:file_cache_path]
+
+ domain "domain-name"
+ user "user"
+ password "password"
+ end
+
+ # Passing username = 'domain-name\\username'. No domain is passed
+ execute 'mkdir test_dir' do
+ cwd Chef::Config[:file_cache_path]
+
+ user "domain-name\\username"
+ password "password"
+ end
+
+ # Passing username = 'username@domain-name'. No domain is passed
+ execute 'mkdir test_dir' do
+ cwd Chef::Config[:file_cache_path]
+
+ user "username@domain-name"
+ password "password"
+ end
+ ```
+ EXAMPLES
+
# The ResourceGuardInterpreter wraps a resource's guards in another resource. That inner resource
# needs to behave differently during (for example) why_run mode, so we flag it here. For why_run mode
# we still want to execute the guard resource even if we are not executing the wrapping resource.