Garcon
A collection of methods helpful in writing complex cookbooks that are impossible to comprehend.
Hipster, hoodie ninja cool awesome.
Requirements
Before trying to use the cookbook make sure you have a supported system. If you are attempting to use the cookbook in a standalone manner to do testing and development you will need a functioning Chef/Ruby environment, with the following:
- Chef 11 or higher
- Ruby 1.9 (preferably from the Chef full-stack installer)
Chef
Chef Server version 11+ and Chef Client version 11.16.2+ and Ohai 7+ are required. Clients older that 11.16.2 do not work.
Platforms
This cookbook uses Test Kitchen to do cross-platform convergence and post- convergence tests. The tested platforms are considered supported. This cookbook may work on other platforms or platform versions with or without modification.
- Red Hat Enterprise Linux (RHEL) Server 6 x86_64 (RedHat, CentOS, Oracle etc.)
Cookbooks
The following cookbooks are required as noted (check the metadata.rb file for the specific version numbers):
- chef_handler - Distribute and enable Chef Exception and Report handlers.
- yum - The Yum cookbook exposes the yum_repository resources that allows a user to both control global behavior and make individual Yum repositories available for use.
Development Requirements
In order to develop and test this Cookbook, you will need a handful of gems installed.
It is recommended for you to use the Chef Developer Kit (ChefDK). You can get the latest release of ChefDK from the downloads page.
On Mac OS X, you can also use homebrew-cask to install ChefDK.
Once you install the package, the chef-client
suite, berks
, kitchen
, and
this application (chef
) will be symlinked into your system bin directory,
ready to use.
You should then set your Ruby/Chef development environment to use ChefDK. You can do so by initializing your shell with ChefDK's environment.
eval "$(chef shell-init SHELL_NAME)"
where SHELL_NAME
is the name of your shell, (bash or zsh). This modifies your
PATH
and GEM_*
environment variables to include ChefDK's paths (run without
the eval
to see the generated code). Now your default ruby
and associated
tools will be the ones from ChefDK:
which ruby
# => /opt/chefdk/embedded/bin/ruby
You will also need Vagrant 1.6+ installed and a Virtualization provider such as VirtualBox or VMware.
Usage
Include the garcon recipe in your run list:
include_recipe 'garcon::default'
Attributes
Attributes are under the garcon
namespace, the following attributes affect
the behavior of how the cookbook performs an installation, or are used in the
recipes for various settings that require flexibility.
General attributes:
General attributes can be found in the default.rb
file.
-
node[:garcon][:repo][:gpgcheck]
: [TrueClass, FalseClass] This tells yum whether or not it should perform a GPG signature check on packages. When this is set in the [main] section it sets the default for all repositories. The default istrue
. -
node[:garcon][:repo][:gpgkey]
: [String, URI::HTTP] A URL pointing to the ASCII-armored GPG key file for the repository. This option is used if yum needs a public key to verify a package and the required key hasn't been imported into the RPM database. If this option is set, yum will automatically import the key from the specified URL. -
node[:garcon][:repo][:mirrorlist]
: [String, URI::HTTP] Specifies a URL to a file containing a list of baseurls. This can be used instead of or with the baseurl option. Substitution variables, described below, can be used with this option. As a special hack is the mirrorlist URL contains the word "metalink" then the value of mirrorlist is copied to metalink (if metalink is not set).
Providers
This cookbook includes HWRPs for managing:
-
Chef::Provider::Download
: Thedownload
resource is a lightweight multi- protocol and multi-source download utility. It supports HTTP/HTTPS, FTP, BitTorrent and Metalink. -
Chef::Provider::ZipFile
: Provides a pure-ruby implementation for managing zip files, adapted from thewindows_zipfile
resource. -
Chef::Provider::Thread
:
download
Use the download
resource to transfer files from a remote location, similar
to remote_file
but provides multithreaded torrent and http downloader
utilizing Aria2. This can be up to four times
faster than the standard remote_file
resource, useful for large file
transfers.
Syntax
The syntax for using the download
resource in a recipe is as follows:
download 'name' do
attribute 'value' # see attributes section below
...
action :action # see actions section below
end
Where:
-
download
tells the chef-client to use theChef::Provider::Download
provider during the chef-client run; -
name
is the name of the resource block; when thepath
attribute is not specified as part of a recipe,name
is also the path to the remote file; -
attribute
is zero (or more) of the attributes that are available for this resource; -
:action
identifies which steps the chef-client will take to bring the node into the desired state.
For example:
download ::File.join(Chef::Config[:file_cache_path], 'file.tar.gz') do
source 'http://www.example.org/file.tar.gz'
end
Actions
-
:create
: Default. Use to create a file. If a file already exists (but does not match), use to update that file to match. -
:create_if_missing
: Use to create a file only if the file does not exist. (When the file exists, nothing happens.) -
:delete
: Use to delete a file. -
:touch
: Use to touch a file. This updates the access (atime) and file modification (mtime) times for a file. (This action may be used with this resource, but is typically only used with the file resource.)
Attribute Parameters
-
backup
: The number of backups to be kept. Set to false to prevent backups from being kept. Default value: 5. -
checksum
: Optional, allows for conditional gets. The SHA-256 checksum of the file. Use to prevent thedownload
resource from re-downloading a file. When the local file matches the checksum, it will not be downloaded. -
connections
: Download a file using N connections. If more than N URIs are given, first N URIs are used and remaining URIs are used for backup. If less than N URIs are given, those URIs are used more than once so that N connections total are made simultaneously. Default: 5. -
group
: A string or ID that identifies the group owner by group name. If this value is not specified, existing groups will remain unchanged and new group assignments will use the default POSIX group (if available). -
max_connections
: The maximum number of connections to one server for each download. Default: 5. -
mode
: A quoted string that defines the octal mode for a file. If mode is not specified and if the file already exists, the existing mode on the file is used. If mode is not specified, the file does not exist, and the :create action is specified, the chef-client will assume a mask value of "0777" and then apply the umask for the system on which the file will be created to the mask value. For example, if the umask on a system is "022", the chef-client would use the default value of "0755". -
owner
: A string or ID that identifies the group owner by user name. If this value is not specified, existing owners will remain unchanged and new owner assignments will use the current user (when necessary). -
path
: The full path to the file, including the file name and its extension. Default value: the name of the resource block. -
source
: name attribute. The location (URI) of the source file. This value may also specify HTTP (http://), FTP (ftp://), or local (file://) source file locations.
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.
Transfer a file from a URL
download '/tmp/testfile' do
source 'http://www.example.com/tempfiles/testfile'
mode 00644
checksum '3a7dac00b1' # A SHA256 (or portion thereof) of the file.
end
Transfer a file only when the source has changed
download '/tmp/file.png' do
source 'http://example.com/file.png'
action :nothing
end
http_request 'HEAD //example.com/file.png' do
message ''
url '//example.com/file.png'
action :head
if ::File.exists?('/tmp/file.png')
header "If-Modified-Since" => ::File.mtime("/tmp/file.png").httpdate
end
notifies :create, 'download[http://example.com/file.png]', :immediately
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 to the path specified by the
src_path
variable.
The following code sample is similar to the upload_progress_module
recipe in
the nginx cookbook.
src_file = "foo123-nginx-module-v#{node[:nginx][:foo123][:version]}.tar.gz"
src_path = ::File.join(Chef::Config[:file_cache_path], src_file)
extract_path = ::File.join(Chef::Config[:file_cache_path],
'nginx_foo123_module', node[:nginx][:foo123][:checksum]
)
download src_path do
source node[:nginx][:foo123][:url]
checksum node[:nginx][:foo123][:checksum]
owner 'root'
group 'root'
mode 00644
end
bash 'extract module' do
cwm ::File.dirname(src_path)
code <<-CODE
mkdir -p #{extract_path}
tar xzf #{src_file} -C #{extract_path}
mv #{extract_path}/*/* #{extract_path}/
CODE
not_if { ::File.exist?(extract_path) }
end
zip_file
This resource provides a pure-ruby implementation for managing zip files. Be sure to use the not_if or only_if meta parameters to guard the resource for idempotence or action will be taken every Chef run.
Syntax
The syntax for using the zip_file
resource in a recipe is as follows:
zip_file 'name' do
attribute 'value' # see attributes section below
...
action :action # see actions section below
end
Where:
-
zip_file
tells the chef-client to use theChef::Provider::ZipFile
provider during the chef-client run; -
name
is the name of the resource block; when thepath
attribute is not specified as part of a recipe,name
is also the path where files will be (un)zipped to; -
attribute
is zero (or more) of the attributes that are available for this resource; -
:action
identifies which steps the chef-client will take to bring the node into the desired state.
For example:
zip_file '/tmp/path' do
source 'http://www.example.org/file.tar.gz'
end
Actions
-
:unzip
: unzip a compressed file. -
:zip
: zip a directory (recursively).
Attribute Parameters
-
checksum
: for :unzip, useful if source is remote, if the local file matches the SHA-256 checksum, Chef will not download it. -
group
: A string or ID that identifies the group owner by group name. If this value is not specified, existing groups will remain unchanged and new group assignments will use the default POSIX group (if available). -
mode
: A quoted string that defines the octal mode for a file. If mode is not specified and if the file already exists, the existing mode on the file is used. If mode is not specified, the file does not exist, and the :create action is specified, the chef-client will assume a mask value of "0777" and then apply the umask for the system on which the file will be created to the mask value. For example, if the umask on a system is "022", the chef-client would use the default value of "0755". -
overwrite
: force an overwrite of the files if they already exist. -
owner
: A string or ID that identifies the group owner by user name. If this value is not specified, existing owners will remain unchanged and new owner assignments will use the current user (when necessary). -
path
: name attribute. The path where files will be (un)zipped to. -
remove_after
: If the zip file should be removed once it has been unzipped. Default is false. -
source
: The source of the zip file (either a URI or local path) for :unzip, or directory to be zipped for :zip.
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.
Unzip a remote zip file locally
zip_file '/opt/app' do
source 'http://example.com/app.zip'
owner 'app_user'
group 'app_group'
action :unzip
not_if { ::File.exist?('/opt/app/bin/startup.sh') }
end
Unzip a local zipfile
zip_file '/home/foo' do
source '/tmp/something.zip'
action :unzip
end
Create a local zipfile
zip_file '/tmp/foo.zip' do
source '/home/foo'
action :zip
end
Testing
Ensure you have all the required prerequisite listed in the Development Requirements section. You should have a working Vagrant installation with either VirtualBox or VMware installed. From the parent directory of this cookbook begin by running bundler to ensure you have all the required Gems:
bundle install
A ruby environment with Bundler installed is a prerequisite for using the testing harness shipped with this cookbook. At the time of this writing, it works with Ruby 2.1.2 and Bundler 1.6.2. All programs involved, with the exception of Vagrant and VirtualBox, can be installed by cd'ing into the parent directory of this cookbook and running 'bundle install'.
Vagrant and VirtualBox
The installation of Vagrant and VirtualBox is extremely complex and involved. Please be prepared to spend some time at your computer:
If you have not yet installed Homebrew do so now:
ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
Next install Homebrew Cask:
brew tap phinze/homebrew-cask && brew install brew-cask
Then, to get Vagrant installed run this command:
brew cask install vagrant
Finally install VirtualBox:
brew cask install virtualbox
You will also need to get the Berkshelf and Omnibus plugins for Vagrant:
vagrant plugin install vagrant-berkshelf
vagrant plugin install vagrant-omnibus
Try doing that on Windows.
Rakefile
The Rakefile ships with a number of tasks, each of which can be ran individually, or in groups. Typing rake
by itself will perform style checks with Rubocop and Foodcritic, Chefspec with rspec, and integration with Test Kitchen using the Vagrant driver by default. Alternatively, integration tests can be ran with Test Kitchen cloud drivers for EC2 are provided.
$ rake -T
rake all # Run all tasks
rake chefspec # Run RSpec code examples
rake doc # Build documentation
rake foodcritic # Lint Chef cookbooks
rake kitchen:all # Run all test instances
rake kitchen:apps-dir-centos-65 # Run apps-dir-centos-65 test instance
rake kitchen:default-centos-65 # Run default-centos-65 test instance
rake kitchen:ihs-centos-65 # Run ihs-centos-65 test instance
rake kitchen:was-centos-65 # Run was-centos-65 test instance
rake kitchen:wps-centos-65 # Run wps-centos-65 test instance
rake readme # Generate README.md from _README.md.erb
rake rubocop # Run RuboCop
rake rubocop:auto_correct # Auto-correct RuboCop offenses
rake test # Run all tests except `kitchen` / Run
# kitchen integration tests
rake yard # Generate YARD Documentation
Style Testing
Ruby style tests can be performed by Rubocop by issuing either the bundled binary or with the Rake task:
$ bundle exec rubocop
or
$ rake style:ruby
Chef style tests can be performed with Foodcritic by issuing either:
$ bundle exec foodcritic
or
$ rake style:chef
Testing
This cookbook uses Test Kitchen to verify functionality.
- Install ChefDK
- Activate ChefDK's copy of ruby:
eval "$(chef shell-init bash)"
bundle install
bundle exec kitchen test kitchen:default-centos-65
Spec Testing
Unit testing is done by running Rspec examples. Rspec will test any libraries, then test recipes using ChefSpec. This works by compiling a recipe (but not converging it), and allowing the user to make assertions about the resource_collection.
Integration Testing
Integration testing is performed by Test Kitchen. Test Kitchen will use either the Vagrant driver or EC2 cloud driver to instantiate machines and apply cookbooks. After a successful converge, tests are uploaded and ran out of band of Chef. Tests are be designed to ensure that a recipe has accomplished its goal.
Integration Testing using Vagrant
Integration tests can be performed on a local workstation using Virtualbox or VMWare. Detailed instructions for setting this up can be found at the Bento project web site. Integration tests using Vagrant can be performed with either:
$ bundle exec kitchen test
or
$ rake integration:vagrant
Integration Testing using EC2 Cloud provider
Integration tests can be performed on an EC2 providers using Test Kitchen plugins. This cookbook references environmental variables present in the shell that kitchen test
is ran from. These must contain authentication tokens for driving APIs, as well as the paths to ssh private keys needed for Test Kitchen log into them after they've been created.
Examples of environment variables being set in ~/.bash_profile
:
# aws
export AWS_ACCESS_KEY_ID='your_bits_here'
export AWS_SECRET_ACCESS_KEY='your_bits_here'
export AWS_KEYPAIR_NAME='your_bits_here'
Integration tests using cloud drivers can be performed with either
$ bundle exec kitchen test
or
$ rake integration:cloud
Guard
Guard tasks have been separated into the following groups:
doc
lint
unit
integration
By default, Guard will generate documentation, lint, and run unit tests.
The integration group must be selected manually with guard -g integration
.
Contributing
Please see the CONTRIBUTING.md.
License and Authors
Author:: Stefano Harding riddopic@gmail.com
Copyright:: 2014-2015, Stefano Harding
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.