class Kitchen::Provisioner::Chef::CommonSandbox

@api private
@author Fletcher Nichol <fnichol@nichol.ca>
Chef-related provisioners.
Internal object to manage common sandbox preparation for

def all_files_in_cookbooks

Other tags:
    Api: - private

Returns:
  • (Array) - an array of absolute paths to files
def all_files_in_cookbooks
  Util.list_directory(tmpbooks_dir, include_dot: true, recurse: true)
    .select { |fn| File.file?(fn) }
end

def berksfile

Other tags:
    Api: - private

Returns:
  • (String) - an absolute path to a Berksfile, relative to the
def berksfile
  basename = config[:berksfile_path] || "Berksfile"
  File.expand_path(basename, config[:kitchen_root])
end

def cookbooks_dir

Other tags:
    Api: - private

Returns:
  • (String) - an absolute path to a cookbooks/ directory, relative
def cookbooks_dir
  File.join(config[:kitchen_root], "cookbooks")
end

def cp_cookbooks

Other tags:
    Api: - private
def cp_cookbooks
  info("Preparing cookbooks from project directory")
  debug("Using cookbooks from #{cookbooks_dir}")
  FileUtils.mkdir_p(tmpbooks_dir)
  FileUtils.cp_r(File.join(cookbooks_dir, "."), tmpbooks_dir)
  cp_site_cookbooks if File.directory?(site_cookbooks_dir)
  cp_this_cookbook if File.exist?(metadata_rb)
end

def cp_site_cookbooks

Other tags:
    Api: - private
def cp_site_cookbooks
  info("Preparing site-cookbooks from project directory")
  debug("Using cookbooks from #{site_cookbooks_dir}")
  FileUtils.mkdir_p(tmpsitebooks_dir)
  FileUtils.cp_r(File.join(site_cookbooks_dir, "."), tmpsitebooks_dir)
end

def cp_this_cookbook

Other tags:
    Api: - private
def cp_this_cookbook
  info("Preparing current project directory as a cookbook")
  debug("Using metadata.rb from #{metadata_rb}")
  cb_name = MetadataChopper.extract(metadata_rb).first || raise(UserError,
    "The metadata.rb does not define the 'name' key." \
      " Please add: `name '<cookbook_name>'` to metadata.rb and retry")
  cb_path = File.join(tmpbooks_dir, cb_name)
  glob = Util.list_directory(config[:kitchen_root])
  FileUtils.mkdir_p(cb_path)
  FileUtils.cp_r(glob, cb_path)
end

def filter_only_cookbook_files

Other tags:
    Api: - private
def filter_only_cookbook_files
  info("Removing non-cookbook files before transfer")
  FileUtils.rm(all_files_in_cookbooks - only_cookbook_files)
  Util.list_directory(tmpbooks_dir, recurse: true)
    .reverse_each { |fn| FileUtils.rmdir(fn) if File.directory?(fn) && Dir.empty?(fn) }
end

def initialize(config, sandbox_path, instance)

Parameters:
  • instance (Instance) -- an instance
  • sandbox_path (String) -- path to local sandbox directory
  • config (Hash) -- configuration hash
def initialize(config, sandbox_path, instance)
  @config = config
  @sandbox_path = sandbox_path
  @instance = instance
end

def logger

Other tags:
    Api: - private

Returns:
  • (Logger) - the instance's logger or Test Kitchen's common
def logger
  instance ? instance.logger : Kitchen.logger
end

def make_fake_cookbook

Other tags:
    Api: - private
def make_fake_cookbook
  info("Policyfile, Berksfile, cookbooks/, or metadata.rb not found " \
    "so Chef Infra Client will run, but do nothing. Is this intended?")
  name = File.basename(config[:kitchen_root])
  fake_cb = File.join(tmpbooks_dir, name)
  FileUtils.mkdir_p(fake_cb)
  File.open(File.join(fake_cb, "metadata.rb"), "wb") do |file|
    file.write(%{name "#{name}"\n})
  end
end

def metadata_rb

Other tags:
    Api: - private

Returns:
  • (String) - an absolute path to a metadata.rb, relative to the
def metadata_rb
  File.join(config[:kitchen_root], "metadata.rb")
end

def only_cookbook_files

Other tags:
    Api: - private

Returns:
  • (Array) - an array of absolute paths to files
def only_cookbook_files
  glob = File.join("*", "{#{config[:cookbook_files_glob]}}")
  Util.safe_glob(tmpbooks_dir, glob, File::FNM_DOTMATCH)
    .select { |fn| File.file?(fn) && ! %w{. ..}.include?(fn) }
end

def policyfile

Other tags:
    Api: - private

Returns:
  • (String) - an absolute path to a Policyfile, relative to the
def policyfile
  basename = config[:policyfile_path] || config[:policyfile] || "Policyfile.rb"
  File.expand_path(basename, config[:kitchen_root])
end

def populate

Populate the sandbox.
def populate
  prepare_json
  prepare_cache
  prepare_cookbooks
  prepare(:data)
  prepare(:data_bags)
  prepare(:environments)
  prepare(:nodes)
  prepare(:roles)
  prepare(:clients)
  prepare(
    :secret,
    type: :file,
    dest_name: "encrypted_data_bag_secret",
    key_name: :encrypted_data_bag_secret_key_path
  )
end

def prepare(component, opts = {})

Other tags:
    Api: - private

Options Hash: (**opts)
  • :dest_name (String) -- the destination file or directory
  • :key_name (Symbol) -- the key name in the config hash from
  • :type (Symbol) -- whether the component is a directory or

Parameters:
  • opts (Hash) -- optional configuration
  • component (Symbol, String) -- a component name such as `:node`
def prepare(component, opts = {})
  opts = { type: :directory }.merge(opts)
  key_name = opts.fetch(:key_name, "#{component}_path")
  src = config[key_name.to_sym]
  return if src.nil?
  info("Preparing #{component}")
  debug("Using #{component} from #{src}")
  dest = File.join(sandbox_path, opts.fetch(:dest_name, component.to_s))
  case opts[:type]
  when :directory
    FileUtils.mkdir_p(dest)
    Array(src).each { |dir| FileUtils.cp_r(Util.list_directory(dir), dest) }
  when :file
    FileUtils.mkdir_p(File.dirname(dest))
    Array(src).each { |file| FileUtils.cp_r(file, dest) }
  end
end

def prepare_cache

Other tags:
    Api: - private
def prepare_cache
  FileUtils.mkdir_p(File.join(sandbox_path, "cache"))
end

def prepare_cookbooks

Other tags:
    Api: - private
def prepare_cookbooks
  if File.exist?(policyfile)
    resolve_with_policyfile
  elsif File.exist?(berksfile)
    resolve_with_berkshelf
  elsif File.directory?(cookbooks_dir)
    cp_cookbooks
  elsif File.exist?(metadata_rb)
    cp_this_cookbook
  else
    make_fake_cookbook
  end
  filter_only_cookbook_files
end

def prepare_json

Other tags:
    Api: - private
def prepare_json
  dna = if File.exist?(policyfile)
          update_dna_for_policyfile
        else
          config[:attributes].merge(run_list: config[:run_list])
        end
  info("Preparing dna.json")
  debug("Creating dna.json from #{dna.inspect}")
  File.open(File.join(sandbox_path, "dna.json"), "wb") do |file|
    file.write(dna.to_json)
  end
end

def resolve_with_berkshelf

Other tags:
    Api: - private
def resolve_with_berkshelf
  Kitchen.mutex.synchronize do
    Chef::Berkshelf.new(berksfile, tmpbooks_dir,
      logger:,
      always_update: config[:always_update_cookbooks]).resolve
  end
end

def resolve_with_policyfile

Other tags:
    Api: - private
def resolve_with_policyfile
  Kitchen.mutex.synchronize do
    Chef::Policyfile.new(
      policyfile, sandbox_path,
      logger:,
      always_update: config[:always_update_cookbooks],
      policy_group: config[:policy_group],
      license: config[:chef_license]
    ).resolve
  end
end

def site_cookbooks_dir

Other tags:
    Api: - private

Returns:
  • (String) - an absolute path to a site-cookbooks/ directory,
def site_cookbooks_dir
  File.join(config[:kitchen_root], "site-cookbooks")
end

def tmpbooks_dir

Other tags:
    Api: - private

Returns:
  • (String) - an absolute path to a cookbooks/ directory in the
def tmpbooks_dir
  File.join(sandbox_path, "cookbooks")
end

def tmpsitebooks_dir

Other tags:
    Api: - private

Returns:
  • (String) - an absolute path to a site cookbooks directory in the
def tmpsitebooks_dir
  File.join(sandbox_path, "cookbooks")
end

def update_dna_for_policyfile

def update_dna_for_policyfile
  policy = Chef::Policyfile.new(
    policyfile, sandbox_path,
    logger:,
    always_update: config[:always_update_cookbooks],
    policy_group:,
    license: config[:chef_license]
  )
  Kitchen.mutex.synchronize do
    policy.compile
  end
  policy_name = JSON.parse(File.read(policy.lockfile))["name"]
  policy_group = config[:policy_group] || "local"
  config[:attributes].merge(policy_name:, policy_group:)
end