Install the Chef Development Kit

I am using CentOS 7. Grab the package appropriate for you from here

[root@testbox ~]# wget https://packages.chef.io/files/stable/chefdk/2.5.3/el/7/chefdk-2.5.3-1.el7.x86_64.rpm
--2018-05-21 1:22:53--  https://packages.chef.io/files/stable/chefdk/2.5.3/el/7/chefdk-2.5.3-1.el7.x86_64.rpm
Resolving packages.chef.io (packages.chef.io)... 151.101.2.110, 151.101.66.110, 151.101.130.110, ...
Connecting to packages.chef.io (packages.chef.io)|151.101.2.110|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 107263528 (102M) [application/x-rpm]
Saving to: ‘chefdk-2.5.3-1.el7.x86_64.rpm’

100%[======================================================================================================================================================================================>] 107,263,528 82.1MB/s   in 1.2s   

2018-05-21 1:22:55 (82.1 MB/s) - ‘chefdk-2.5.3-1.el7.x86_64.rpm’ saved [107263528/107263528]

[root@testbox ~]# rpm -i chefdk-2.5.3-1.el7.x86_64.rpm 
warning: chefdk-2.5.3-1.el7.x86_64.rpm: Header V4 DSA/SHA1 Signature, key ID 83ef826a: NOKEY
Thank you for installing Chef Development Kit!
[root@testbox ~]# 

Now, lets generate a bare cookbook. I plan on doing this in /etc/chef, just because I dont want it running out of a home directory or any weird location. But, the path is totally up to you, just make sure to change the configuration later if you do, since your solo.rb will differ. The /etc/chef directory should already exist since you installed the chefdk.

[root@testbox ~]# ls -l /etc/chef
total 0
drwxr-xr-x 3 root root 19 May 21 1:30 local-mode-cache
[root@testbox ~]# cd /etc/chef/
[root@testbox chef]# chef generate cookbook service-monitor
Hyphens are discouraged in cookbook names as they may cause problems with custom resources. See https://docs.chef.io/ctl_chef.html#chef-generate-cookbook for more information.
Generating cookbook service-monitor
- Ensuring correct cookbook file content
- Committing cookbook files to git
- Ensuring delivery configuration
- Ensuring correct delivery build cookbook content
- Adding delivery configuration to feature branch
- Adding build cookbook to feature branch
- Merging delivery content feature branch to master

Your cookbook is ready. Type `cd service-monitor` to enter it.

There are several commands you can run to get started locally developing and testing your cookbook.
Type `delivery local --help` to see a full list.

Why not start by writing a test? Tests for the default recipe are stored at:

test/integration/default/default_test.rb

If you'd prefer to dive right in, the default recipe can be found at:

recipes/default.rb

[root@testbox chef]# 

This created some basic directory structure and default files for us. Can you do it manually? Sure, but why would you?

[root@testbox service-monitor]# find ./ | grep -v .delivery | grep -v .git
./
./metadata.rb
./README.md
./chefignore
./Berksfile
./LICENSE
./.kitchen.yml
./test
./test/integration
./test/integration/default
./test/integration/default/default_test.rb
./spec
./spec/unit
./spec/unit/recipes
./spec/unit/recipes/default_spec.rb
./spec/spec_helper.rb
./recipes
./recipes/default.rb
[root@testbox service-monitor]# 

The place you really need to focus your attention for the most basic example is recipes/default.rb. Currently, this is just a blank file with some comments up at the top. The test stuff is really neat, but we will save the rspec and kitchen testing for another post.

But before we are ready to get going, we need to give chef-solo some basic configuration. This is stored in two files. One is solo.rb, and the other is a json file. As far as I know there isnt a way to generate this, so you might as well copy/paste.

Lets created /etc/chef/solo.rb

[root@testbox chef]# cat solo.rb 
cookbook_path "/etc/chef"
[root@testbox chef]# 

Now lets give it a run and see what happens:

[root@testbox chef]# chef-solo -c /etc/chef/solo.rb 
Starting Chef Client, version 13.8.5
resolving cookbooks for run list: []
Synchronizing Cookbooks:
Installing Cookbook Gems:
Compiling Cookbooks...
[2018-05-21T1:50:09-04:00] WARN: Node testbox.fazey.org has an empty run list.
Converging 0 resources

Running handlers:
Running handlers complete
Chef Client finished, 0/0 resources updated in 03 seconds
[root@testbox chef]# 

So, it looks like chef ran, and didnt error out. But it warned us that our runlist is empty. This was totally expected since we haven’t created the runlist yet. So lets go ahead and do that next.

Lets create /etc/chef/solo.json

[root@testbox chef]# cat solo.json 
{
  "run_list": [
    "recipe[service-monitor]"
  ]
}
[root@testbox chef]# 

I realize the directive states “recipe”, but what you put in there is your cookbook name. This will tell it to use service-monitor::default. We will use the default recipe to include what we want. If you break it out into separate files, you can put “recipe[service-monitor::some-recipe]”. I prefer to drive everything via default recipe for something this simple. Now lets give it a test run and see what happens. For this, we include the -j parameter to chef solo.

[root@testbox chef]# chef-solo -c /etc/chef/solo.rb -j /etc/chef/solo.json 
Starting Chef Client, version 13.8.5
resolving cookbooks for run list: ["service-monitor"]
Synchronizing Cookbooks:
  - service-monitor (0.1.0)
Installing Cookbook Gems:
Compiling Cookbooks...
Converging 0 resources

Running handlers:
Running handlers complete
Chef Client finished, 0/0 resources updated in 03 seconds
[root@testbox chef]# 

Perfect, it found our cookbook, acknowledged our runlist, and no warnings. At this point, with the exception of the cron job we will setup later, our environment is ready. We can get to work on our recipes.

Writing our recipes

Lets move back into your cookbook/recipes directory. In my case /etc/chef/service-monitor/recipes, and open up default.rb. Lets start with making sure ntp is installed.

With chef, you can use yum_package, or apt_package. But if you intend for it to be multi-platform, just use the package resource and let chef figure it out. The default action is :install if you dont specify anything. But we want it to auto-upgrade too if a new version comes out.

[root@testbox recipes]# cat default.rb 
#
# Cookbook:: service-monitor
# Recipe:: default
#
# Copyright:: 2018, The Authors, All Rights Reserved.
package 'ntp' do 
  action [ :install, :upgrade ]
end

[root@testbox recipes]#

Lets run chef, and make sure it does what we want it to:

[root@testbox recipes]# chef-solo -c /etc/chef/solo.rb -j /etc/chef/solo.json 
Starting Chef Client, version 13.8.5
resolving cookbooks for run list: ["service-monitor"]
Synchronizing Cookbooks:
  - service-monitor (0.1.0)
Installing Cookbook Gems:
Compiling Cookbooks...
Converging 1 resources
Recipe: service-monitor::default
  * yum_package[ntp] action install
    - install version 4.2.6p5-28.el7.centos of package ntp
  * yum_package[ntp] action upgrade (up to date)

Running handlers:
Running handlers complete
Chef Client finished, 1/2 resources updated in 13 seconds
[root@testbox recipes]# 

Score, it looks like it installed just like we wanted. Now, we also want it running after it is installed. We also want it to start if chef ever finds it stopped. So lets reopen our default.rb, and use the service resource.

Lets re-open our default.rb, and get to work on our service resource.

[root@testbox recipes]# cat default.rb 
#
# Cookbook:: service-monitor
# Recipe:: default
#
# Copyright:: 2018, The Authors, All Rights Reserved.
package 'ntp' do 
  action [ :install, :upgrade ]
end

service 'ntpd' do
  action [ :enable, :start ]
end

[root@testbox recipes]# 

Take note that our service is actually named ntpd, but the package is ntp. This is not a typo. We :enable, because we want it setup to run on startup, and we :start, because we want it started now.

Lets test it to make sure it will start ntpd if its stopped:

[root@testbox recipes]# systemctl status ntpd
● ntpd.service - Network Time Service
   Loaded: loaded (/usr/lib/systemd/system/ntpd.service; disabled; vendor preset: disabled)
   Active: inactive (dead)
[root@testbox recipes]# chef-solo -c /etc/chef/solo.rb -j /etc/chef/solo.json 
Starting Chef Client, version 13.8.5
resolving cookbooks for run list: ["service-monitor"]
Synchronizing Cookbooks:
  - service-monitor (0.1.0)
Installing Cookbook Gems:
Compiling Cookbooks...
Converging 2 resources
Recipe: service-monitor::default
  * yum_package[ntp] action install (up to date)
  * yum_package[ntp] action upgrade (up to date)
  * service[ntpd] action enable
    - enable service service[ntpd]
  * service[ntpd] action start
    - start service service[ntpd]

Running handlers:
Running handlers complete
Chef Client finished, 2/4 resources updated in 08 seconds
[root@testbox recipes]# systemctl status ntpd
● ntpd.service - Network Time Service
   Loaded: loaded (/usr/lib/systemd/system/ntpd.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2018-05-21 1:37:30 EDT; 4s ago
  Process: 11905 ExecStart=/usr/sbin/ntpd -u ntp:ntp $OPTIONS (code=exited, status=0/SUCCESS)
 Main PID: 11906 (ntpd)
   CGroup: /system.slice/ntpd.service
           └─11906 /usr/sbin/ntpd -u ntp:ntp -g

May 21 1:37:30 testbox.fazey.org ntpd[11906]: Listen normally on 3 ens160 172.16.0.7 UDP 123
May 21 1:37:30 testbox.fazey.org ntpd[11906]: Listen normally on 4 docker0 172.17.0.1 UDP 123
May 21 1:37:30 testbox.fazey.org ntpd[11906]: Listen normally on 5 lo ::1 UDP 123
May 21 1:37:30 testbox.fazey.org ntpd[11906]: Listen normally on 6 ens160 fe80::8286:f624:d436:940e UDP 123
May 21 1:37:30 testbox.fazey.org ntpd[11906]: Listen normally on 7 docker0 fe80::42:d7ff:fecc:974a UDP 123
May 21 1:37:30 testbox.fazey.org ntpd[11906]: Listening on routing socket on fd #24 for interface updates
May 21 1:37:30 testbox.fazey.org systemd[1]: Started Network Time Service.
May 21 1:37:30 testbox.fazey.org ntpd[11906]: 0.0.0.0 c016 06 restart
May 21 1:37:30 testbox.fazey.org ntpd[11906]: 0.0.0.0 c012 02 freq_set kernel 0.000 PPM
May 21 1:37:30 testbox.fazey.org ntpd[11906]: 0.0.0.0 c011 01 freq_not_set
[root@testbox recipes]# 

Looks like everything worked as we intended. Lets see if we can break it by removing the ntp rpm.

[root@testbox recipes]# rpm -e ntp
[root@testbox recipes]# chef-solo -c /etc/chef/solo.rb -j /etc/chef/solo.json 
Starting Chef Client, version 13.8.5
resolving cookbooks for run list: ["service-monitor"]
Synchronizing Cookbooks:
  - service-monitor (0.1.0)
Installing Cookbook Gems:
Compiling Cookbooks...
Converging 2 resources
Recipe: service-monitor::default
  * yum_package[ntp] action install
    - install version 4.2.6p5-28.el7.centos of package ntp
  * yum_package[ntp] action upgrade (up to date)
  * service[ntpd] action enable
    - enable service service[ntpd]
  * service[ntpd] action start
    - start service service[ntpd]

Running handlers:
Running handlers complete
Chef Client finished, 3/4 resources updated in 12 seconds
[root@testbox recipes]# 

Nope, chef reinstalled it, and started it back up.

Run chef-solo via cron.

If you’ve spent much time using cron, you will know the PATH can come back and bite you in the behind. So make sure to set your PATH. When something running via cron fails, the system mails that to root. In most cases, that isn’t desirable. I also choose to run it every 5 minutes, and use lock files in case the system gets a bit taxed. You dont want chef runs piling up behind each other to make the resource contention worse.

[root@testbox recipes]# cat /etc/cron.d/chef-solo 
PATH="/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
MAILTO=""
*/5 * * * * root /usr/bin/chef-solo -c /etc/chef/solo.rb -j /etc/chef/solo.json --lockfile /var/run/chef-solo.lock --run-lock-timeout 0
[root@testbox recipes]#