zap v1
I’m happy to announce v1.0.0 of the zap cookbook.
In keeping with semver, v1.0.0 contains breaking changes. These were necessary to support custom resources.
- Instead of classes,
register
now takes resource name(s) and an optional block to translate the resource into an identifier. zap_firewall
has been removed as the firewall cookbook manages the rules as a whole.zap_apt_repos
has been removed as resource. Use thezap::apt_repos
recipe instead.zap_yum_repos
has been removed as resource. Use thezap::yum_repos
recipe instead.
The best way to describe the changes is to walk through a couple of the included recipes.
The recipe zap::iptables_d
is used in conjunction with iptables cookbook. Given iptables_rule 'foo'
, the file /etc/iptables.d/foo
will be generated.
So let’s dig into the recipe:
zap 'iptables_d' do
action :disable
register :iptables_rule
collect do
Dir
.glob("/etc/iptables.d/#{node['zap']['iptables_d']['pattern']}")
.map { |path| File.basename(path) }
end
end
collect
takes a block that will be executed to “collect” all of the existing resources.
Given the following tree:
/
└── etc
└── iptables.d
├── https
├── obsolete
└── ssh
['https', 'obsolete', 'ssh']
will be returned.
register
is passed an array of resource names that zap will look for in the resource collection. When zap prunes resources on disk, it will create an instance of the first resource name registered to :delete
that on-disk resource.
To “delete” an iptables_rule
, one need specifies a :disable
action.
Back to the above example, calling collect
we determined
existing = ['https', 'obsolete', 'ssh']
and walking through the resource collection, we determined
desired = ['https', 'ssh']
so ‘obsolete’ was determined to be extraneous. zap will add a resource as if
iptables_rule 'obsolete' do
action :disable
end
was specified in a recipe.
Many resources do more than just delete the file. In this case, flush the rule set from the kernel so we cannot simply just rm
the file.
Next let’s look at the zap::cron_d
recipe used inconjunction with the cron cookbook.
zap 'cron_d' do
register :cron_d do |r|
# sanitized_name
r.name.tr('.', '-')
end
collect do
Dir
.glob("/etc/cron.d/#{node['zap']['cron_d']['pattern']}")
.map { |path| File.basename(path) }
end
end
When cron_d
creates the on-disk file, dots are translated into dashes. So when collect
discovers ‘foo-bar’, it has no idea if
cron_d 'foo-bar' do
...
end
or
cron_d 'foo.bar' do
...
end
was specified.
We solve this dilemma by passing an optional block to register
. When zap encounters cron_d 'foo.bar'
in the resource collection it will return 'foo-bar'
to match what collect
discovers on disk.
Finally, let’s look at zap::yum_repos
:
zap '/etc/yum.repos.d' do
register :yum_repository
collect do
Dir
.glob("/etc/yum.repos.d/#{node['zap']['yum_repos']['pattern']}.repo")
.map { |path| File.basename(path, '.repo') }
end
end
Since yum_repository 'chef-stable'
will create /etc/yum.repos.d/chef-stable.repo
we do the additional
.map
to extract the corresponding name of the resource.
In all of the above example, we did a glob
One set the appropriate node attribute if you wanted to restrict zap to a particular namespace, i.e. zap only the acme-*
yum repos.
There are a couple other advanced features that one can leverage when building a zap.
For the zap_crontab
you specify which user’s crontab you want to zap.
register :cron do |r|
r.name if r.user == @name
end
When zap walks the resource collection and finds a cron
, it will return r.name
is user matches. If not, nil
is returned and zap will skip that resource.
Finally there is a purge
callout if you need to do anything special to remove the resource.
purge do |id|
r = Chef::Resource::Cron.new(id, @run_context)
r.action(:delete)
r.user(@name)
r
end
Whew, I know this is a lot to digest. While it may seem complicated, zap
can be hugely powerful tool in your toolbox.
In closing: