Class: Chef::PolicyBuilder::Policyfile
- Inherits:
-
Object
- Object
- 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
private
Sets attributes from the policyfile on the node, using the role priority.
-
#build_node ⇒ Object
Applies environment, external JSON attributes, and override run list to the node, Then expands the run_list.
- #config ⇒ Object private
- #cookbook_lock_for(cookbook_name) ⇒ Object private
- #cookbook_locks ⇒ Object private
-
#cookbooks_to_sync ⇒ Object
private
Builds a ‘cookbook_hash’ map of the form “COOKBOOK_NAME” => “IDENTIFIER”.
- #deployment_group ⇒ Object private
-
#expand_run_list ⇒ RunListExpansionIsh
Sets ‘run_list` on the node from the policy, sets `roles` and `recipes` attributes on the node accordingly.
-
#finish_load_node(node) ⇒ Object
PolicyBuilder API ##.
- #http_api ⇒ Object private
-
#initialize(node_name, ohai_data, json_attribs, override_runlist, events) ⇒ Policyfile
constructor
A new instance of Policyfile.
-
#manifest_for(cookbook_name, lock_data) ⇒ Object
private
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 private
- #policy ⇒ Object private
- #policy_group ⇒ Object private
- #policy_group_from_config ⇒ Object private
- #policy_group_from_json_attribs ⇒ Object private
- #policy_group_from_node ⇒ Object private
- #policy_name ⇒ Object private
- #policy_name_from_config ⇒ Object private
- #policy_name_from_json_attribs ⇒ Object private
- #policy_name_from_node ⇒ Object private
- #policyfile_location ⇒ Object private
- #revision_id ⇒ Object private
- #run_list ⇒ Object private
-
#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
private
Sets up a RunListExpansionIsh object so that it can be used in place of a RunListExpansion object, to satisfy the API contract of #expand_run_list.
-
#run_list_with_versions_for_display ⇒ Object
private
Generates an array of strings with recipe names including version and identifier info.
-
#select_policy_name_and_group ⇒ Object
private
Selects the ‘policy_name` and `policy_group` from the following sources in priority order:.
-
#setup_run_context(specific_recipes = nil) ⇒ Chef::RunContext
Synchronizes cookbooks and initializes the run context object for the run.
-
#sync_cookbooks ⇒ Hash{String => Chef::CookbookManifest}
Synchronizes cookbooks.
-
#temporary_policy? ⇒ false
Whether or not this is a temporary policy.
- #validate_policy_config! ⇒ Object private
-
#validate_policyfile ⇒ Object
Do some mimimal validation of the policyfile we fetched from the server.
- #validate_recipe_spec(recipe_spec) ⇒ Object private
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 |
# 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 if Chef::Config[:solo_legacy_mode] raise UnsupportedFeature, "Policyfile does not support chef-solo. Use chef-client local mode instead." end if override_runlist raise UnsupportedFeature, "Policyfile does not support override run lists. Use named run_lists instead." end if json_attribs && json_attribs.key?("run_list") raise UnsupportedFeature, "Policyfile does not support setting the run_list in json data." 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
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Sets attributes from the policyfile on the node, using the role priority.
236 237 238 239 |
# File 'lib/chef/policy_builder/policyfile.rb', line 236 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.
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/chef/policy_builder/policyfile.rb', line 125 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
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
459 460 461 |
# File 'lib/chef/policy_builder/policyfile.rb', line 459 def config Chef::Config end |
#cookbook_lock_for(cookbook_name) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
252 253 254 |
# File 'lib/chef/policy_builder/policyfile.rb', line 252 def cookbook_lock_for(cookbook_name) cookbook_locks[cookbook_name] end |
#cookbook_locks ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
444 445 446 |
# File 'lib/chef/policy_builder/policyfile.rb', line 444 def cookbook_locks policy["cookbook_locks"] end |
#cookbooks_to_sync ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
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.
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 |
# File 'lib/chef/policy_builder/policyfile.rb', line 411 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
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
322 323 324 |
# File 'lib/chef/policy_builder/policyfile.rb', line 322 def deployment_group Chef::Config[:deployment_group] || raise(ConfigurationError, "Setting `deployment_group` is not configured.") end |
#expand_run_list ⇒ RunListExpansionIsh
Sets ‘run_list` on the node from the policy, sets `roles` and `recipes` attributes on the node accordingly.
172 173 174 175 176 177 178 179 |
# File 'lib/chef/policy_builder/policyfile.rb', line 172 def CookbookCacheCleaner.instance.skip_removal = true if named_run_list_requested? node.run_list(run_list) node.automatic_attrs[:roles] = [] node.automatic_attrs[:recipes] = run_list_expansion_ish.recipes run_list_expansion_ish end |
#finish_load_node(node) ⇒ Object
PolicyBuilder API ##
113 114 115 116 117 118 |
# File 'lib/chef/policy_builder/policyfile.rb', line 113 def finish_load_node(node) @node = node select_policy_name_and_group validate_policyfile events.policyfile_loaded(policy) end |
#http_api ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
454 455 456 |
# File 'lib/chef/policy_builder/policyfile.rb', line 454 def http_api @api_service ||= Chef::ServerAPI.new(config[:chef_server_url]) end |
#manifest_for(cookbook_name, lock_data) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
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.
435 436 437 438 439 440 441 |
# File 'lib/chef/policy_builder/policyfile.rb', line 435 def manifest_for(cookbook_name, lock_data) if Chef::Config[:policy_document_native_api] artifact_manifest_for(cookbook_name, lock_data) else compat_mode_manifest_for(cookbook_name, lock_data) end end |
#original_runlist ⇒ Object
Override run_list is not supported.
92 93 94 |
# File 'lib/chef/policy_builder/policyfile.rb', line 92 def original_runlist nil end |
#override_runlist ⇒ Object
Override run_list is not supported.
97 98 99 |
# File 'lib/chef/policy_builder/policyfile.rb', line 97 def override_runlist nil end |
#parse_recipe_spec(recipe_spec) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
242 243 244 245 246 247 248 249 |
# File 'lib/chef/policy_builder/policyfile.rb', line 242 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
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
268 269 270 271 272 |
# File 'lib/chef/policy_builder/policyfile.rb', line 268 def policy @policy ||= http_api.get(policyfile_location) rescue Net::HTTPServerException => e raise ConfigurationError, "Error loading policyfile from `#{policyfile_location}': #{e.class} - #{e.}" end |
#policy_group ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
334 335 336 |
# File 'lib/chef/policy_builder/policyfile.rb', line 334 def policy_group Chef::Config[:policy_group] end |
#policy_group_from_config ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
382 383 384 |
# File 'lib/chef/policy_builder/policyfile.rb', line 382 def policy_group_from_config Chef::Config[:policy_group] end |
#policy_group_from_json_attribs ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
372 373 374 |
# File 'lib/chef/policy_builder/policyfile.rb', line 372 def policy_group_from_json_attribs json_attribs["policy_group"] end |
#policy_group_from_node ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
392 393 394 |
# File 'lib/chef/policy_builder/policyfile.rb', line 392 def policy_group_from_node node.policy_group end |
#policy_name ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
339 340 341 |
# File 'lib/chef/policy_builder/policyfile.rb', line 339 def policy_name Chef::Config[:policy_name] end |
#policy_name_from_config ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
387 388 389 |
# File 'lib/chef/policy_builder/policyfile.rb', line 387 def policy_name_from_config Chef::Config[:policy_name] end |
#policy_name_from_json_attribs ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
377 378 379 |
# File 'lib/chef/policy_builder/policyfile.rb', line 377 def policy_name_from_json_attribs json_attribs["policy_name"] end |
#policy_name_from_node ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
397 398 399 |
# File 'lib/chef/policy_builder/policyfile.rb', line 397 def policy_name_from_node node.policy_name end |
#policyfile_location ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
275 276 277 278 279 280 281 282 |
# File 'lib/chef/policy_builder/policyfile.rb', line 275 def policyfile_location if Chef::Config[:policy_document_native_api] validate_policy_config! "policy_groups/#{policy_group}/policies/#{policy_name}" else "data/policyfiles/#{deployment_group}" end end |
#revision_id ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
449 450 451 |
# File 'lib/chef/policy_builder/policyfile.rb', line 449 def revision_id policy["revision_id"] end |
#run_list ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
257 258 259 260 261 262 263 264 265 |
# File 'lib/chef/policy_builder/policyfile.rb', line 257 def run_list if named_run_list_requested? named_run_list || raise(ConfigurationError, "Policy '#{retrieved_policy_name}' revision '#{revision_id}' does not have named_run_list '#{named_run_list_name}'" + "(available named_run_lists: [#{available_named_run_lists.join(', ')}])") else policy["run_list"] end 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
107 108 109 |
# File 'lib/chef/policy_builder/policyfile.rb', line 107 def run_list_expansion run_list_expansion_ish end |
#run_list_expansion_ish ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Sets up a RunListExpansionIsh object so that it can be used in place of a RunListExpansion object, to satisfy the API contract of #expand_run_list
225 226 227 228 229 230 231 |
# File 'lib/chef/policy_builder/policyfile.rb', line 225 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
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Generates an array of strings with recipe names including version and identifier info.
211 212 213 214 215 216 217 218 |
# File 'lib/chef/policy_builder/policyfile.rb', line 211 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 |
#select_policy_name_and_group ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Selects the ‘policy_name` and `policy_group` from the following sources in priority order:
-
JSON attribs (i.e., ‘-j JSON_FILE`)
-
‘Chef::Config`
-
The node object
The selected values are then copied to ‘Chef::Config` and the node.
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
# File 'lib/chef/policy_builder/policyfile.rb', line 353 def select_policy_name_and_group policy_name_to_set = policy_name_from_json_attribs || policy_name_from_config || policy_name_from_node policy_group_to_set = policy_group_from_json_attribs || policy_group_from_config || policy_group_from_node node.policy_name = policy_name_to_set node.policy_group = policy_group_to_set Chef::Config[:policy_name] = policy_name_to_set Chef::Config[:policy_group] = policy_group_to_set end |
#setup_run_context(specific_recipes = nil) ⇒ Chef::RunContext
Synchronizes cookbooks and initializes the run context object for the run.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/chef/policy_builder/policyfile.rb', line 151 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) cookbook_collection.validate! cookbook_collection.install_gems(events) run_context = Chef::RunContext.new(node, cookbook_collection, events) setup_chef_class(run_context) run_context.load(run_list_expansion_ish) setup_chef_class(run_context) run_context end |
#sync_cookbooks ⇒ Hash{String => Chef::CookbookManifest}
Synchronizes cookbooks. In a normal chef-client run, this is handled by #setup_run_context, but may be called directly in some circumstances.
186 187 188 189 190 191 192 193 194 195 |
# File 'lib/chef/policy_builder/policyfile.rb', line 186 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? ⇒ false
Whether or not this is a temporary policy. Since PolicyBuilder doesn’t support override_runlist, this is always false.
201 202 203 |
# File 'lib/chef/policy_builder/policyfile.rb', line 201 def temporary_policy? false end |
#validate_policy_config! ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
327 328 329 330 331 |
# File 'lib/chef/policy_builder/policyfile.rb', line 327 def validate_policy_config! raise ConfigurationError, "Setting `policy_group` is not configured." unless policy_group raise ConfigurationError, "Setting `policy_name` is not configured." unless policy_name 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.
289 290 291 292 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 289 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
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
312 313 314 315 316 317 |
# File 'lib/chef/policy_builder/policyfile.rb', line 312 def validate_recipe_spec(recipe_spec) parse_recipe_spec(recipe_spec) nil rescue PolicyfileError => e e. end |