lib/kitchen/provisioner/policyfile_zero.rb
# # Author:: Fletcher Nichol (<fnichol@nichol.ca>) # # Copyright (C) 2013, Fletcher Nichol # # 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 "kitchen/provisioner/chef_base" # TODO: chef-cli and kitchen can only co-exist if kitchen and chef-cli agree on # the version of mixlib-shellout to use. Kitchen currently locked at 1.4, # chef-cli is on 2.x require_relative "../../chef-cli/policyfile_services/export_repo" module Kitchen module Provisioner class Base # PolicyfileZero needs to access the base behavior of creating the # sandbox directory without invoking the behavior of # ChefBase#create_sandbox, because that will trigger the use of # Chef::CommonSandbox, which we need to override. alias_method :create_sandbox_directory, :create_sandbox end class PolicyfileSandbox < Chef::CommonSandbox # Stub #prepare_cookbooks because we have implemented this in the # provisioner, below. If a Berksfile is present, the default # implementation will try to run Berkshelf, which can lead to dependency # issues since berks is not yet using Solve 2.x. See also # PolicyfileZero#load_needed_dependencies! which is stubbed to prevent # berks from loading. def prepare_cookbooks; end end # Policyfile + Chef Zero provisioner. # # @author Daniel DeLeo <dan@chef.io> class PolicyfileZero < ChefBase # This provisioner will forcibly set the following config options: # * `use_policyfile`: `true` # * `versioned_cookbooks`: `true` # * `deployment_group`: `POLICY_NAME-local` # Since it makes no sense to modify these, they are hardcoded elsewhere. default_config :client_rb, {} default_config :json_attributes, true default_config :named_run_list, nil default_config :chef_zero_host, nil default_config :chef_zero_port, 8889 default_config :policyfile, "Policyfile.rb" default_config :chef_client_path do |provisioner| provisioner .remote_path_join(%W{#{provisioner[:chef_omnibus_root]} bin chef-client}) .tap { |path| path.concat(".bat") if provisioner.windows_os? } end default_config :ruby_bindir do |provisioner| provisioner .remote_path_join(%W{#{provisioner[:chef_omnibus_root]} embedded bin}) end # (see Base#finalize_config!) def finalize_config!(*args) super banner("Using policyfile mode for chef-client") end # (see Base#create_sandbox) def create_sandbox create_sandbox_directory PolicyfileSandbox.new(config, sandbox_path, instance).populate prepare_cookbooks prepare_validation_pem prepare_client_rb end # (see Base#run_command) def run_command level = config[:log_level] == :info ? :auto : config[:log_level] cmd = "#{sudo(config[:chef_client_path])} --local-mode" .tap { |str| str.insert(0, "& ") if powershell_shell? } args = [ "--config #{config[:root_path]}/client.rb", "--log_level #{level}", "--force-formatter", "--no-color", ] if config[:chef_zero_port] args << "--chef-zero-port #{config[:chef_zero_port]}" end if config[:log_file] args << "--logfile #{config[:log_file]}" end if config[:named_run_list] args << "--named-run-list #{config[:named_run_list]}" end wrap_shell_code( [cmd, *args].join(" ") .tap { |str| str.insert(0, reload_ps1_path) if windows_os? } ) end # We don't want to load Berkshelf or Librarian; Policyfile is managing # dependencies, so these can only cause trouble. def load_needed_dependencies!; end private # Overrides behavior of parent class so that dna.json isn't created; we # don't need it. # # @api private def prepare_json; end # Copies the policyfile's cookbooks to the sandbox. # # @api private def prepare_cookbooks Kitchen.mutex.synchronize do policy_exporter.run end end # An instance of ChefCLI::PolicyfileServices::ExportRepo, configured with # the sandbox path. Calling `#run` on this copies the cookbooks to the # sandbox. Calling `#policy_name` returns the policy's name. # # @api private def policy_exporter # Must force this because TK by default copies the current cookbook to the sandbox # See ChefCLI::PolicyfileServices::ExportRepo#assert_export_dir_clean! @policy_exporter ||= ChefCLI::PolicyfileServices::ExportRepo.new(policyfile: config[:policyfile], export_dir: sandbox_path, force: true) end # Writes a fake (but valid) validation.pem into the sandbox directory. # # @api private def prepare_validation_pem info("Preparing validation.pem") debug("Using a dummy validation.pem") source = File.join(Kitchen.source_root, %w{support dummy-validation.pem}) FileUtils.cp(source, File.join(sandbox_path, "validation.pem")) end # Writes a client.rb configuration file to the sandbox directory. # # @api private def prepare_client_rb data = default_config_rb.merge(config[:client_rb]) data["use_policyfile"] = true data["versioned_cookbooks"] = true data["policy_name"] = policy_exporter.policy_name data["policy_group"] = "local" data["policy_document_native_api"] = true info("Preparing client.rb") debug("Creating client.rb from #{data.inspect}") File.open(File.join(sandbox_path, "client.rb"), "wb") do |file| file.write(format_config_file(data)) end end end end end