Class: Chef::PolicyBuilder::Policyfile
- Defined in:
- lib/chef/policy_builder/policyfile.rb
Overview
Policyfile is an experimental policy builder implementation that gets run list and cookbook version information from a single document.
WARNING
This implementation is experimental. It may be changed in incompatible ways in minor or even patch releases, or even abandoned altogether. If using this with other tools, you may be forced to upgrade those tools in lockstep with chef-client because of incompatible behavior changes.
Unsupported Options:
- override_runlist
-
This could potentially be integrated into the
policyfile, or replaced with a similar feature that has different semantics.
- specific_recipes
-
put more design thought into this use case.
- run_list in json_attribs
-
would be ignored anyway, so it raises an error.
- chef-solo
-
not currently supported. Need more design thought around
how this should work.
Defined Under Namespace
Classes: ConfigurationError, PolicyfileError, RunListExpansionIsh, UnsupportedFeature
Instance Attribute Summary collapse
-
#events ⇒ Object
readonly
Returns the value of attribute events.
-
#json_attribs ⇒ Object
readonly
Returns the value of attribute json_attribs.
-
#node ⇒ Object
readonly
Returns the value of attribute node.
-
#node_name ⇒ Object
readonly
Returns the value of attribute node_name.
-
#ohai_data ⇒ Object
readonly
Returns the value of attribute ohai_data.
-
#run_context ⇒ Object
readonly
Returns the value of attribute run_context.
Instance Method Summary collapse
- #apply_policyfile_attributes ⇒ Object
-
#build_node ⇒ Object
Applies environment, external JSON attributes, and override run list to the node, Then expands the run_list.
- #config ⇒ Object
- #cookbook_lock_for(cookbook_name) ⇒ Object
- #cookbook_locks ⇒ Object
-
#cookbooks_to_sync ⇒ Object
Builds a ‘cookbook_hash’ map of the form “COOKBOOK_NAME” => “IDENTIFIER”.
- #deployment_group ⇒ Object
- #expand_run_list ⇒ Object
- #http_api ⇒ Object
-
#initialize(node_name, ohai_data, json_attribs, override_runlist, events) ⇒ Policyfile
constructor
A new instance of Policyfile.
-
#load_node ⇒ Object
Loads the node state from the server.
-
#manifest_for(cookbook_name, lock_data) ⇒ Object
Fetches the CookbookVersion object for the given name and identifer specified in the lock_data.
-
#original_runlist ⇒ Object
Override run_list is not supported.
-
#override_runlist ⇒ Object
Override run_list is not supported.
- #parse_recipe_spec(recipe_spec) ⇒ Object
- #policy ⇒ Object
- #policyfile_location ⇒ Object
- #run_list ⇒ Object
-
#run_list_expansion ⇒ Object
Policyfile gives you the run_list already expanded, but users of this class may expect to get a run_list expansion compatible object by calling this method.
- #run_list_expansion_ish ⇒ Object
-
#run_list_with_versions_for_display ⇒ Object
Internal Public API ##.
- #setup_run_context(specific_recipes = nil) ⇒ Object
- #sync_cookbooks ⇒ Object
-
#temporary_policy? ⇒ Boolean
Whether or not this is a temporary policy.
-
#validate_policyfile ⇒ Object
Do some mimimal validation of the policyfile we fetched from the server.
- #validate_recipe_spec(recipe_spec) ⇒ Object
Constructor Details
#initialize(node_name, ohai_data, json_attribs, override_runlist, events) ⇒ Policyfile
Returns a new instance of Policyfile.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/chef/policy_builder/policyfile.rb', line 63 def initialize(node_name, ohai_data, json_attribs, override_runlist, events) @node_name = node_name @ohai_data = ohai_data @json_attribs = json_attribs @events = events @node = nil Chef::Log.warn("Using experimental Policyfile feature") if Chef::Config[:solo] raise UnsupportedFeature, "Policyfile does not support chef-solo at this time." end if override_runlist raise UnsupportedFeature, "Policyfile does not support override run lists at this time" end if json_attribs && json_attribs.key?("run_list") raise UnsupportedFeature, "Policyfile does not support setting the run_list in json data at this time" end if Chef::Config[:environment] && !Chef::Config[:environment].chomp.empty? raise UnsupportedFeature, "Policyfile does not work with Chef Environments" end end |
Instance Attribute Details
#events ⇒ Object (readonly)
Returns the value of attribute events.
56 57 58 |
# File 'lib/chef/policy_builder/policyfile.rb', line 56 def events @events end |
#json_attribs ⇒ Object (readonly)
Returns the value of attribute json_attribs.
60 61 62 |
# File 'lib/chef/policy_builder/policyfile.rb', line 60 def json_attribs @json_attribs end |
#node ⇒ Object (readonly)
Returns the value of attribute node.
57 58 59 |
# File 'lib/chef/policy_builder/policyfile.rb', line 57 def node @node end |
#node_name ⇒ Object (readonly)
Returns the value of attribute node_name.
58 59 60 |
# File 'lib/chef/policy_builder/policyfile.rb', line 58 def node_name @node_name end |
#ohai_data ⇒ Object (readonly)
Returns the value of attribute ohai_data.
59 60 61 |
# File 'lib/chef/policy_builder/policyfile.rb', line 59 def ohai_data @ohai_data end |
#run_context ⇒ Object (readonly)
Returns the value of attribute run_context.
61 62 63 |
# File 'lib/chef/policy_builder/policyfile.rb', line 61 def run_context @run_context end |
Instance Method Details
#apply_policyfile_attributes ⇒ Object
211 212 213 214 |
# File 'lib/chef/policy_builder/policyfile.rb', line 211 def apply_policyfile_attributes node.attributes.role_default = policy["default_attributes"] node.attributes.role_override = policy["override_attributes"] end |
#build_node ⇒ Object
Applies environment, external JSON attributes, and override run list to the node, Then expands the run_list.
Returns
- node<Chef::Node>
-
The modified node object. node is modified in place.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/chef/policy_builder/policyfile.rb', line 133 def build_node # consume_external_attrs may add items to the run_list. Save the # expanded run_list, which we will pass to the server later to # determine which versions of cookbooks to use. node.reset_defaults_and_overrides node.consume_external_attrs(ohai_data, json_attribs) apply_policyfile_attributes Chef::Log.info("Run List is [#{run_list}]") Chef::Log.info("Run List expands to [#{run_list_with_versions_for_display.join(', ')}]") events.node_load_completed(node, run_list_with_versions_for_display, Chef::Config) node rescue Exception => e events.node_load_failed(node_name, e, Chef::Config) raise end |
#config ⇒ Object
334 335 336 |
# File 'lib/chef/policy_builder/policyfile.rb', line 334 def config Chef::Config end |
#cookbook_lock_for(cookbook_name) ⇒ Object
225 226 227 |
# File 'lib/chef/policy_builder/policyfile.rb', line 225 def cookbook_lock_for(cookbook_name) cookbook_locks[cookbook_name] end |
#cookbook_locks ⇒ Object
326 327 328 |
# File 'lib/chef/policy_builder/policyfile.rb', line 326 def cookbook_locks policy["cookbook_locks"] end |
#cookbooks_to_sync ⇒ Object
Builds a ‘cookbook_hash’ map of the form
"COOKBOOK_NAME" => "IDENTIFIER"
This can be passed to a Chef::CookbookSynchronizer object to synchronize the cookbooks.
TODO: Currently this makes N API calls to the server to get the cookbook objects. With server support (bulk API or the like), this should be reduced to a single call.
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/chef/policy_builder/policyfile.rb', line 293 def cookbooks_to_sync @cookbook_to_sync ||= begin events.cookbook_resolution_start(run_list_with_versions_for_display) cookbook_versions_by_name = cookbook_locks.inject({}) do |cb_map, (name, lock_data)| cb_map[name] = manifest_for(name, lock_data) cb_map end events.cookbook_resolution_complete(cookbook_versions_by_name) cookbook_versions_by_name end rescue Exception => e # TODO: wrap/munge exception to provide helpful error output events.cookbook_resolution_failed(run_list_with_versions_for_display, e) raise end |
#deployment_group ⇒ Object
279 280 281 282 |
# File 'lib/chef/policy_builder/policyfile.rb', line 279 def deployment_group Chef::Config[:deployment_group] or raise ConfigurationError, "Setting `deployment_group` is not configured." end |
#expand_run_list ⇒ Object
167 168 169 170 171 172 |
# File 'lib/chef/policy_builder/policyfile.rb', line 167 def node.run_list(run_list) node.automatic_attrs[:roles] = [] node.automatic_attrs[:recipes] = run_list_expansion_ish.recipes run_list_expansion_ish end |
#http_api ⇒ Object
330 331 332 |
# File 'lib/chef/policy_builder/policyfile.rb', line 330 def http_api @api_service ||= Chef::REST.new(config[:chef_server_url]) end |
#load_node ⇒ Object
Loads the node state from the server.
116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/chef/policy_builder/policyfile.rb', line 116 def load_node events.node_load_start(node_name, Chef::Config) Chef::Log.debug("Building node object for #{node_name}") @node = Chef::Node.find_or_create(node_name) validate_policyfile node rescue Exception => e events.node_load_failed(node_name, e, Chef::Config) raise end |
#manifest_for(cookbook_name, lock_data) ⇒ Object
Fetches the CookbookVersion object for the given name and identifer specified in the lock_data. TODO: This only implements Chef 11 compatibility mode, which means that cookbooks are fetched by the “dotted_decimal_identifier”: a representation of a SHA1 in the traditional x.y.z version format.
316 317 318 319 320 321 322 323 324 |
# File 'lib/chef/policy_builder/policyfile.rb', line 316 def manifest_for(cookbook_name, lock_data) xyz_version = lock_data["dotted_decimal_identifier"] http_api.get("cookbooks/#{cookbook_name}/#{xyz_version}") rescue Exception => e = "Error loading cookbook #{cookbook_name} at version #{xyz_version}: #{e.class} - #{e.}" err = Chef::Exceptions::CookbookNotFound.new() err.set_backtrace(e.backtrace) raise err end |
#original_runlist ⇒ Object
Override run_list is not supported.
94 95 96 |
# File 'lib/chef/policy_builder/policyfile.rb', line 94 def original_runlist nil end |
#override_runlist ⇒ Object
Override run_list is not supported.
99 100 101 |
# File 'lib/chef/policy_builder/policyfile.rb', line 99 def override_runlist nil end |
#parse_recipe_spec(recipe_spec) ⇒ Object
216 217 218 219 220 221 222 223 |
# File 'lib/chef/policy_builder/policyfile.rb', line 216 def parse_recipe_spec(recipe_spec) rmatch = recipe_spec.match(/recipe\[([^:]+)::([^:]+)\]/) if rmatch.nil? raise PolicyfileError, "invalid recipe specification #{recipe_spec} in Policyfile from #{policyfile_location}" else [rmatch[1], rmatch[2]] end end |
#policy ⇒ Object
233 234 235 236 237 |
# File 'lib/chef/policy_builder/policyfile.rb', line 233 def policy @policy ||= http_api.get(policyfile_location) rescue Net::HTTPServerException => e raise ConfigurationError, "Error loading policyfile from `#{policyfile_location}': #{e.class} - #{e.}" end |
#policyfile_location ⇒ Object
239 240 241 |
# File 'lib/chef/policy_builder/policyfile.rb', line 239 def policyfile_location "data/policyfiles/#{deployment_group}" end |
#run_list ⇒ Object
229 230 231 |
# File 'lib/chef/policy_builder/policyfile.rb', line 229 def run_list policy["run_list"] end |
#run_list_expansion ⇒ Object
Policyfile gives you the run_list already expanded, but users of this class may expect to get a run_list expansion compatible object by calling this method.
Returns
- RunListExpansionIsh
-
A RunListExpansion duck type
109 110 111 |
# File 'lib/chef/policy_builder/policyfile.rb', line 109 def run_list_expansion run_list_expansion_ish end |
#run_list_expansion_ish ⇒ Object
203 204 205 206 207 208 209 |
# File 'lib/chef/policy_builder/policyfile.rb', line 203 def run_list_expansion_ish recipes = run_list.map do |recipe_spec| cookbook, recipe = parse_recipe_spec(recipe_spec) "#{cookbook}::#{recipe}" end RunListExpansionIsh.new(recipes, []) end |
#run_list_with_versions_for_display ⇒ Object
Internal Public API ##
194 195 196 197 198 199 200 201 |
# File 'lib/chef/policy_builder/policyfile.rb', line 194 def run_list_with_versions_for_display run_list.map do |recipe_spec| cookbook, recipe = parse_recipe_spec(recipe_spec) lock_data = cookbook_lock_for(cookbook) display = "#{cookbook}::#{recipe}@#{lock_data["version"]} (#{lock_data["identifier"][0...7]})" display end end |
#setup_run_context(specific_recipes = nil) ⇒ Object
156 157 158 159 160 161 162 163 164 165 |
# File 'lib/chef/policy_builder/policyfile.rb', line 156 def setup_run_context(specific_recipes=nil) Chef::Cookbook::FileVendor.fetch_from_remote(http_api) sync_cookbooks cookbook_collection = Chef::CookbookCollection.new(cookbooks_to_sync) run_context = Chef::RunContext.new(node, cookbook_collection, events) run_context.load(run_list_expansion_ish) run_context end |
#sync_cookbooks ⇒ Object
175 176 177 178 179 180 181 182 183 184 |
# File 'lib/chef/policy_builder/policyfile.rb', line 175 def sync_cookbooks Chef::Log.debug("Synchronizing cookbooks") synchronizer = Chef::CookbookSynchronizer.new(cookbooks_to_sync, events) synchronizer.sync_cookbooks # register the file cache path in the cookbook path so that CookbookLoader actually picks up the synced cookbooks Chef::Config[:cookbook_path] = File.join(Chef::Config[:file_cache_path], "cookbooks") cookbooks_to_sync end |
#temporary_policy? ⇒ Boolean
Whether or not this is a temporary policy. Since PolicyBuilder doesn’t support override_runlist, this is always false.
188 189 190 |
# File 'lib/chef/policy_builder/policyfile.rb', line 188 def temporary_policy? false end |
#validate_policyfile ⇒ Object
Do some mimimal validation of the policyfile we fetched from the server. Compatibility mode relies on using data bags to store policy files; therefore no real validation will be performed server-side and we need to make additional checks to ensure the data will be formatted correctly.
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/chef/policy_builder/policyfile.rb', line 248 def validate_policyfile errors = [] unless run_list errors << "Policyfile is missing run_list element" end unless policy.key?("cookbook_locks") errors << "Policyfile is missing cookbook_locks element" end if run_list.kind_of?(Array) run_list_errors = run_list.select do |maybe_recipe_spec| validate_recipe_spec(maybe_recipe_spec) end errors += run_list_errors else errors << "Policyfile run_list is malformed, must be an array of `recipe[cb_name::recipe_name]` items: #{policy["run_list"]}" end unless errors.empty? raise PolicyfileError, "Policyfile fetched from #{policyfile_location} was invalid:\n#{errors.join("\n")}" end end |
#validate_recipe_spec(recipe_spec) ⇒ Object
270 271 272 273 274 275 |
# File 'lib/chef/policy_builder/policyfile.rb', line 270 def validate_recipe_spec(recipe_spec) parse_recipe_spec(recipe_spec) nil rescue PolicyfileError => e e. end |