diff options
Diffstat (limited to 'lib/chef/policy_builder/dynamic.rb')
-rw-r--r-- | lib/chef/policy_builder/dynamic.rb | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/lib/chef/policy_builder/dynamic.rb b/lib/chef/policy_builder/dynamic.rb new file mode 100644 index 0000000000..c9842ba532 --- /dev/null +++ b/lib/chef/policy_builder/dynamic.rb @@ -0,0 +1,186 @@ +# +# Author:: Daniel DeLeo (<dan@chef.io>) +# Copyright:: Copyright 2015 Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'forwardable' + +require 'chef/log' +require 'chef/rest' +require 'chef/run_context' +require 'chef/config' +require 'chef/node' +require 'chef/exceptions' + +class Chef + module PolicyBuilder + + # PolicyBuilder that selects either a Policyfile or non-Policyfile + # implementation based on the content of the node object. + class Dynamic + + extend Forwardable + + attr_reader :node + attr_reader :node_name + attr_reader :ohai_data + attr_reader :json_attribs + attr_reader :override_runlist + attr_reader :events + + def initialize(node_name, ohai_data, json_attribs, override_runlist, events) + @implementation = nil + + @node_name = node_name + @ohai_data = ohai_data + @json_attribs = json_attribs + @override_runlist = override_runlist + @events = events + + @node = nil + end + + ## PolicyBuilder API ## + + # Loads the node state from the server, then picks the correct + # implementation class based on the node and json_attribs. + # + # Calls #finish_load_node on the implementation object to complete the + # loading process. All subsequent lifecycle calls are delegated. + # + # @return [Chef::Node] the loaded node. + def load_node + events.node_load_start(node_name, config) + Chef::Log.debug("Building node object for #{node_name}") + + @node = + if Chef::Config[:solo] + Chef::Node.build(node_name) + else + Chef::Node.find_or_create(node_name) + end + select_implementation(node) + implementation.finish_load_node(node) + node + rescue Exception => e + events.node_load_failed(node_name, e, config) + raise + end + + ## Delegated Public API Methods ## + + ### Accessors ### + + def_delegator :implementation, :original_runlist + def_delegator :implementation, :run_context + def_delegator :implementation, :run_list_expansion + + ### Lifecycle Methods ### + + # @!method build_node + # + # Applies external attributes (e.g., from JSON file, environment, + # policyfile, etc.) and determines the correct expanded run list for the + # run. + # + # @return [Chef::Node] + def_delegator :implementation, :build_node + + # @!method setup_run_context + # + # Synchronizes cookbooks and initializes the run context object for the + # run. + # + # @return [Chef::RunContext] + def_delegator :implementation, :setup_run_context + + # @!method expanded_run_list + # + # Resolves the run list to a form containing only recipes and sets the + # `roles` and `recipes` automatic attributes on the node. + # + # @return [#recipes, #roles] A RunListExpansion or duck-type. + def_delegator :implementation, :expand_run_list + + # @!method sync_cookbooks + # + # Synchronizes cookbooks. In a normal chef-client run, this is handled by + # #setup_run_context, but may be called directly in some circumstances. + # + # @return [Hash{String => Chef::CookbookManifest}] A map of + # CookbookManifest objects by cookbook name. + def_delegator :implementation, :sync_cookbooks + + # @!method temporary_policy? + # + # Indicates whether the policy is temporary, which means an + # override_runlist was provided. Chef::Client uses this to decide whether + # to do the final node save at the end of the run or not. + # + # @return [true,false] + def_delegator :implementation, :temporary_policy? + + ## Internal Public API ## + + # Returns the selected implementation, or raises if not set. The + # implementation is set when #load_node is called. + # + # @return [PolicyBuilder::Policyfile, PolicyBuilder::ExpandNodeObject] + def implementation + @implementation or raise Exceptions::InvalidPolicybuilderCall, "#load_node must be called before other policy builder methods" + end + + # @api private + # + # Sets the implementation based on the content of the node, node JSON + # (i.e., the `-j JSON_FILE` data), and config. This is only public for + # testing purposes; production code should call #load_node instead. + def select_implementation(node) + if policyfile_set_in_config? || + policyfile_attribs_in_node_json? || + node_has_policyfile_attrs?(node) || + policyfile_compat_mode_config? + @implementation = Policyfile.new(node_name, ohai_data, json_attribs, override_runlist, events) + else + @implementation = ExpandNodeObject.new(node_name, ohai_data, json_attribs, override_runlist, events) + end + end + + def config + Chef::Config + end + + private + + def node_has_policyfile_attrs?(node) + node.policy_name || node.policy_group + end + + def policyfile_attribs_in_node_json? + json_attribs.key?("policy_name") || json_attribs.key?("policy_group") + end + + def policyfile_set_in_config? + config[:use_policyfile] || config[:policy_name] || config[:policy_group] + end + + def policyfile_compat_mode_config? + config[:deployment_group] && !config[:policy_document_native_api] + end + + end + end +end |