You can create Chef machine policies (essentially pinned resources) using the chef_policy resource. When it is done, the policy, and any referenced cookbooks will be on the Chef server.
You can define the policy inline in the file. The most basic version:
chef_policy 'name' do
recipe 'base'
recipe 'blah::x'
cookbook 'base', '>= 1.1.0'
end
chef_policy 'name' do
run_list 'recipe[base]', 'recipe[blah::x]'
cookbook 'base', '>= 1.1.0'
end
You can pull the policy from a Policyfile, as well:
chef_policy 'web' do
based_on_policyfile 'web'
end
# Shorthand
chef_policy 'web.rb'
The default source for cookbooks is the https://supermarket.getchef.com community site. You can override the default source for cookbooks for a policy in config:
# .chef/knife.rb
default_cookbook_source :community
You can put it in the recipe:
with_cookbook_source :community
chef_policy 'name' do
recipe 'base'
end
chef_policy 'another' do
recipe 'base'
end
You can even put it on each resource:
chef_policy 'a' do
cookbook_source :community
recipe 'base'
end
chef_policy 'a' do
cookbook_source :chef_server
recipe 'base'
end
You can inherit one policy from another using based_on. When this happens, the new policy will have the exact versions of any cookbooks specified in the base policy, and will include any run_list as well.
chef_policy 'base' do
recipe 'base'
cookbook 'base', '> 1.0.0'
end
chef_policy 'web' do
based_on 'base'
recipe 'apache2::install'
end
# base version is same as whatever is in the base policy
# Runlist: recipe[base], recipe[apache2::install]
You can reorder parts of the original run_list by writing down the order of the things you care about:
chef_policy 'base' do
recipe 'a'
recipe 'b'
end
chef_policy 'web' do
based_on 'base'
recipe 'c'
recipe 'b'
recipe 'a'
end
# Results in c, a, b
chef_policy 'web' do
run_list ['recipe[c]', 'recipe[a]']
end
# Results in c, a, b
You can manage lockfiles out of band, or explicitly, and create a policy from a lockfile:
chef_policy 'web.lock.json'
chef_policy 'web' do
based_on_lockfile 'otherbase.lock'
end
This has the same idempotency rules as chef_policy, and all the same attributes are valid.
chef_policy_lockfile 'web.lock.json' do
default_source
recipe
cookbook
based_on
based_on_lockfile
based_on_policyfile
end
To use a policy in a machine or a node, you just put the attribute there:
machine 'blah' do
policy 'web'
end
This attribute is mutually exclusive with node run_lists and attributes--it is an error to specify both on the node or machine.
The resource has to make decisions on when and what to update. Depending on how often you want your machines to change, there are several strategies for doing so.
With aggressive updates, we recalculate the policy lock every time and update the server anytime it has changed. This way, it picks up the latest version of everything, every time (which may or may not have changed). This is usually what you want; if you need to make sure things are pinned the same way from group to group, you can use based_on or based_on_lockfile to ensure version pins don't move.
The conservative update strategy tries not to update things unless it has to. Thus, if a cookbook already has a version in the server policy, we will update it only if it cannot satisfy local constraints.
If there are cookbooks in your local chef repository, it treats those as = constraints.
For example, let's say both your security and apt_get cookbooks are behind. Both are at version 1.5 on your server, and 2.2 has been released. This will trigger an update:
chef_policy 'base' do
recipe 'security::hardening'
recipe 'apt_get'
cookbook 'security', '> 1.0'
updates :conservative
end
# security and apt_get are now both 2.2
And this will not:
chef_policy 'base' do
recipe 'security::hardening'
recipe 'apt_get'
cookbook 'security', '> 2.0'
updates :conservative
end
# security and apt_get are both still at 2.2
Ultra-conservative updates are like the conservative update strategy, except even after the decision has been made to update the policy, it still tries not to update versions unless they cannot satisfy constraints. based_on_* is probably enough to handle this sort of scenario, and this strategy is probably very hard to implement, so we won't have it anytime soon.
For example, let's say both your security and apt_get cookbooks are behind. Both are at version 1.5 on your server, and 2.2 has been released. This will trigger an update:
chef_policy 'base' do
recipe 'security::hardening'
recipe 'apt_get'
cookbook 'security', '> 1.0'
updates :ultra_conservative
end
# security is 2.2, but apt_get is still 1.5
And this will not:
chef_policy 'base' do
recipe 'security::hardening'
recipe 'apt_get'
cookbook 'security', '> 2.0'
updates :ultra_conservative
end
# security and apt_get are both still at 2.2
By default, the chef_policy will not "garbage collect" any old cookbooks. To force a garbage collection, you can use this resource:
chef_cookbooks_garbage_collector 'blah'
It will look through all policies on the server, and delete any cookbook versions that are no longer used.