Terry : Chef Solo

Chef Solo

Chef Solo - Run chef without a server.

Get started - RailsCasts Chef Solo Basics

First Chef Solo Run Manually

Create cookbook directory structure

cookbooks directory structure

mkdir /var/chef
mkdir -p /var/chef/cookbooks/main/recipes
Create a recipe in main (also a recipe)

File /var/chef/cookbooks/main/recipes/default.rb

default.rb
package "git" # apt-get install git
package "bash-completion"
bash "echo something" do
    code <<-EOF
        echo 'I am a chef!'
    EOF
end
tmp=Chef::Config[:file_cache_path]
bash "cache" do
    code <<-EOF
        echo #{tmp}
    EOF
end

and a more complicated screenfetch.rb

screenfetch.rb
temp=Chef::Config[:file_cache_path]
remote_file "#{Chef::Config[:file_cache_path]}/screenfetch.sh" do
    source "https://raw.github.com/KittyKatt/screenFetch/master/screenfetch-dev"
    mode 00755
end
bash 'screenfetch' do
    code <<-EOF
      #{temp}/./screenfetch.sh > #{temp}/screenfetch.log 2>/dev/null
    EOF
end

About Providers, see more at => Resources and Providers

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

Long nameShort nameNotes
Chef::Provider::PackagepackageWhen this short name is used, Chef will attempt to determine the correct provider during the Chef run.
Chef::Provider::Package::Aptapt_packageThe apt_package resource is used to manage packages for the Debian and Ubuntu platforms.
Chef::Provider::Package::Dpkgdpkg_packageThe dpkg_package resource is used to manage packages for the dpkg platform. When a package is installed from a local file, it must be added to the node using the remote_file or cookbook_file resources. Can be used with the options attribute.
Chef::Provider::Package::Pacmanpacman_packageThe pacman_package resource is used to manage packages for Arch Linux.
Chef::Provider::Package::Rpmrpm_packageThe rpm_package resource is used to manage packages for the RPM Package Manager platform. Can be used with the options attribute.
Chef::Provider::Package::Rubygemsgem_packageThe gem_package resource is used to manage gem packages that are only included in recipes. When a package is installed from a local file, it must be added to the node using the remote_file or cookbook_file resources. Can be used with the options attribute.
Chef::Provider::Package::Rubygemschef_gemThe chef_gem resource is used to install a gem only for Chef resources. When a package is installed from a local file, it must be added to the node using the remote_file or cookbook_file resources. Can be used with the options attribute.
Chef::Provider::Package::Yumyum_packageThe yum_package resource is used install, upgrade, and remove packages with yum for the Red Hat and CentOS platforms. The yum_package resource is able to resolve provides data for packages much like yum can do when it is run from the command line. This allows a variety of options for installing packages, like minimum versions, virtual provides, and library names.

NOTE: Recommended to use knife-solo to create cookbooks directory structure.

Chef Solo Configuration file

Create chef-solo configuration file

solo.rb
cookbook_path File.expand_path("../cookbooks", __FILE__)
json_attribs File.expand_path("../node.json", __FILE__) 

Or

cookbook_path File.expand_path("cookbooks",File.dirname(__FILE__))
json_attribs File.expand_path("node.json", File.dirname(__FILE__))

NOTE: if the nodes are behind firewall (HTTP Proxy required to access the Internet), configure http proxy (can be used in both client.rb and solo.rb), see http://docs.opscode.com/config.html

In solo.rb, add

http_proxy "http://www-proxy.au.oracle.com:80"
http_proxy_pass "password"
http_proxy_user "username"

For example, the solo.rb for Chef run within Oracle network

solo.rb
cookbook_path File.expand_path("../cookbooks", __FILE__)
json_attribs File.expand_path("../node.json", __FILE__)
http_proxy "http://www-proxy.au.oracle.com:80"

Another example from Chef Server's embedded Chef Solo run

/opt/chef-server/embedded/bin/chef-solo -c /opt/chef-server/embedded/cookbooks/solo.rb -j /opt/chef-server/embedded/cookbooks/dna.json

Its configuration file

solo.rb
CURRENT_PATH = File.expand_path(File.dirname(__FILE__))
file_cache_path "#{CURRENT_PATH}/cache"
cookbook_path CURRENT_PATH
verbose_logging false
# http_proxy "http://www-proxy.au.oracle.com:80"

NOTE: Cookbooks are in /opt/chef-server/embedded/cookbooks

A complete solo.rb ready to eat

solo.rb
cookbook_path File.expand_path("cookbooks", File.dirname(__FILE__))
# alternatives
# cookbook_path File.expand_path("../cookbooks", __FILE__)
# cookbook_path File.absolute_path(File.dirname(__FILE__)) + 'cookbooks'
# json_attribs can be overridden by using -j when running chef-solo
json_attribs File.expand_path("node.json", File.dirname(__FILE__))
# alternatives
# json_attribs File.expand_path("../node.json", __FILE__)
# json_attribs File.absolute_path(File.dirname(__FILE__)) + 'node.json'
# More settings
# role_path File.expand_path("roles", File.dirname(__FILE__))
# file_cache_path
# file_backup_path
# http_proxy "http://proxy.terry.im:3128"
# http_proxy_pass "password"
# http_proxy_user "username"
# See http://docs.opscode.com/config.html for all settings
Node Objects - JSON file

Attributes and run-list, including overriding default attributes in the cookbook (attributes/default.rb)

node.json
{
  "user": {
    "name": "vagrant",
    "password": "$1$0wLEd9RQ$BHZn7Kyk4oarp4sVBxRt20",
    "ls_color": true
  },
  "run_list": ["recipe[main]"]
}
Run - let's cook!
cd /var/chef &&
chef-solo -c solo.rb -j node.json
# Debug log level
chef-solo -c solo.rb -j node.json -l debug

Yeah!!!

NOTE: The cookbook_path does NOT have to be /var/chef/cookbooks, as long as the directory structure is maintained and configured correctly in solo.rb.

Berkshelf - Manage Cookbook Dependencies

Install Berkshelf

gem install berkshelf

Add a Berksfile to cookbook’s root

site :opscode

cookbook 'mysql'
cookbook 'nginx', '~> 0.101.5'

Install dependencies

 NOTE: run the command in the directory where Berksfile resides

berks install

Add Berksfile to the project

git add Berksfile
git commit -m "add Berksfile to project"

NOTE: A Berksfile.lock will also be created. Add this to version control if you want to ensure that other developers (or your build server) will use the same versions of all cookbook dependencies.

Use berkshelf to create a skeleton for a new cookbook

$ berks cookbook cookbook-skeleton
      create  cookbook-skeleton/files/default
      create  cookbook-skeleton/templates/default
      create  cookbook-skeleton/attributes
      create  cookbook-skeleton/definitions
      create  cookbook-skeleton/libraries
      create  cookbook-skeleton/providers
      create  cookbook-skeleton/recipes
      create  cookbook-skeleton/resources
      create  cookbook-skeleton/recipes/default.rb
      create  cookbook-skeleton/metadata.rb
      create  cookbook-skeleton/LICENSE
      create  cookbook-skeleton/README.md
      create  cookbook-skeleton/Berksfile
      create  cookbook-skeleton/Thorfile
      create  cookbook-skeleton/chefignore
      create  cookbook-skeleton/.gitignore
         run  git init from "./cookbook-skeleton"
      create  cookbook-skeleton/Gemfile
      create  cookbook-skeleton/Vagrantfile

directory tree structure

$ tree .
.
└── cookbook-skeleton
    ├── attributes
    ├── Berksfile
    ├── chefignore
    ├── definitions
    ├── files
    │   └── default
    ├── Gemfile
    ├── libraries
    ├── LICENSE
    ├── metadata.rb
    ├── providers
    ├── README.md
    ├── recipes
    │   └── default.rb
    ├── resources
    ├── templates
    │   └── default
    ├── Thorfile
    └── Vagrantfile
11 directories, 9 files

More about Berkshelf

http://berkshelf.com/

https://github.com/RiotGames/berkshelf

Food Critic

A lint tool for Opscode Chef Cookbooks.

Foodcritic has two goals

  • To make it easier to flag problems in your Chef cookbooks that will cause Chef to blow up when you attempt to converge. This is about faster feedback. If you automate checks for common problems you can save a lot of time.
  • To encourage discussion within the Chef community on the more subjective stuff - what does a good cookbook look like? Opscode have avoided being overly prescriptive which by and large I think is a good thing. Having a set of rules to base discussion on helps drive out what we as a community think is good style.

Install foodcritic

gem install foodcritic

Usage

$ foodcritic
foodcritic [cookbook_paths]
    -t, --tags TAGS                  Only check against rules with the specified tags.
    -f, --epic-fail TAGS             Fail the build if any of the specified tags are matched ('any' -> fail on any match).
    -c, --chef-version VERSION       Only check against rules valid for this version of Chef.
    -C, --[no-]context               Show lines matched against rather than the default summary.
    -I, --include PATH               Additional rule file path(s) to load.
    -G, --search-gems                Search rubygems for rule files with the path foodcritic/rules/**/*.rb
    -S, --search-grammar PATH        Specify grammar to use when validating search syntax.
    -V, --version                    Display the foodcritic version. 

Food Critic Rules http://acrmp.github.io/foodcritic/

Use knife-solo to create directory structure ("kitchen")

knife-solo adds a handful of commands that aim to make working with chef-solo as powerful as chef-server. It currently adds 5 subcommands to knife:

  • knife solo init
    used to create a new directory structure (i.e. "kitchen") that fits with Chef's standard structure and can be used to build and store recipes.
  • knife solo prepare
    installs Chef on a given host. It's structured to auto-detect the target OS and change the installation process accordingly.
  • knife solo cook
    uploads the current kitchen (Chef repo) to the target host and runs chef-solo on that host.
  • knife solo bootstrap
    combines the two previous ones (prepare and cook).
  • knife solo clean
    removes the uploaded kitchen from the target host.

Install

gem

gem install knife-solo

git checkout

bundle && bundle exec rake install 

Run knife solo init

knife solo init cafe
 
$ tree cafe
cafe
├── cookbooks
├── data_bags
├── nodes
├── roles
├── site-cookbooks
└── solo.rb

NOTE: Directory structure may change as development continues. More can be added by hand: attributes, files, libraries, templates etc.

Links

Vagrant + Chef Solo

The chef solo provisioner allows you to provision the guest using Chef, specifically with Chef Solo.

Chef solo is ideal for people who are already experienced with Chef, already have Chef cookbooks, or are looking to learn Chef. Specifically, this documentation page will not go into how to use Chef or how to write Chef cookbooks, since Chef is a complete system that is beyond the scope of a single page of documentation.

Specifying a run list

Vagrant.configure("2") do |config|
  config.vm.provision "chef_solo" do |chef|
    chef.add_recipe "apache"
  end
end

This causes Vagrant to run Chef Solo with the "apache" cookbook. The cookbooks by default are looked for in the "cookbooks" directory relative to your project root.

$ tree
.
|-- Vagrantfile
|-- cookbooks
|   |-- apache
|       |-- recipes
|           |-- default.rb

NOTE: What is a Vagrantfile?

Custom cookbook path

Instead of using the default "cookbooks" directory, a custom cookbooks path can also be set via the cookbooks_path configuration directive

Vagrant.configure("2") do |config|
  config.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = "my_cookbooks"
  end
end

The path can be relative or absolute. If it is relative, it is relative to the project root.

 The configuration value can also be an array of paths

Vagrant.configure("2") do |config|
  config.vm.provision "chef_solo" do |chef|
    chef.cookbooks_path = ["cookbooks", "my_cookbooks"]
  end
end
Roles

Vagrant also supports provisioning with Chef roles. This is done by specifying a path to a roles folder where roles are defined and by adding roles to your run list

Vagrant.configure("2") do |config|
  config.vm.provision "chef_solo" do |chef|
    chef.roles_path = "roles"
    chef.add_role("web")
  end
end

Just like the cookbooks path, the roles path is relative to the project root if a relative path is given.

Data Bags

Data bags are also supported by the Chef Solo provisioner. This is done by specifying a path to your data bags directory

Vagrant.configure("2") do |config|
  config.vm.provision "chef_solo" do |chef|
    chef.data_bags_path = "data_bags"
  end
end
Custom JSON Data

Additional configuration data for Chef attributes can be passed in to Chef solo. This is done by setting the json property with a Ruby hash (dictionary-like object), which is converted to JSON and passed in to Chef

 Vagrant.configure("2") do |config|
  config.vm.provision "chef_solo" do |chef|
    # ...

    chef.json = {
      "apache" => {
        "listen_address" => "0.0.0.0"
      }
    }
  end
end

Hashes, arrays, etc. can be used with the JSON configuration object. Basically, anything that can be turned cleanly into JSON works.

Custom Node Name

Specify a custom node name by setting the node_name property. This is useful for cookbooks that may depend on this being set to some sort of value.

Example:

Vagrant.configure("2") do |config|
  config.vm.provision "chef_solo" do |chef|
    chef.node_name = "foo"
  end
end 

Reference

Getting Started with Chef

Learn Chef

http://docs.vagrantup.com/v2/provisioning/chef_solo.html