Skip to content
This repository has been archived by the owner on Jul 14, 2021. It is now read-only.

Commit

Permalink
Merge branch 'policyfile-kitchen-integration'
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsdeleo committed Dec 5, 2014
2 parents 970f380 + ee323b2 commit 0cd8dd9
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 14 deletions.
35 changes: 27 additions & 8 deletions lib/chef-dk/policyfile_services/export_repo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,13 @@ def initialize(policyfile: nil, export_dir: nil, root_dir: nil, force: false)
@storage_config = Policyfile::StorageConfig.new.use_policyfile(policyfile_full_path)
end

def policy_name
policyfile_lock.name
end

def run
assert_lockfile_exists!
assert_export_dir_empty!
assert_export_dir_clean!

validate_lockfile
write_updated_lockfile
Expand Down Expand Up @@ -82,10 +86,12 @@ def export
private

def create_repo_structure
FileUtils.rm_rf(export_dir)
FileUtils.rm_rf(cookbooks_dir)
FileUtils.rm_rf(policyfiles_data_bag_dir)

FileUtils.mkdir_p(export_dir)
FileUtils.mkdir_p(File.join(export_dir, "cookbooks"))
FileUtils.mkdir_p(File.join(export_dir, "data_bags", "policyfiles"))
FileUtils.mkdir_p(cookbooks_dir)
FileUtils.mkdir_p(policyfiles_data_bag_dir)
end

def copy_cookbooks
Expand Down Expand Up @@ -157,17 +163,30 @@ def assert_lockfile_exists!
end
end

def assert_export_dir_empty!
entries = Dir.glob(File.join(export_dir, "*"))
if !force_export? && !entries.empty?
raise ExportDirNotEmpty, "Export dir (#{export_dir}) not empty. Refusing to export."
def assert_export_dir_clean!
if !force_export? && !conflicting_fs_entries.empty?
msg = "Export dir (#{export_dir}) not clean. Refusing to export. (Conflicting files: #{conflicting_fs_entries.join(', ')})"
raise ExportDirNotEmpty, msg
end
end

def force_export?
@force_export
end

def conflicting_fs_entries
Dir.glob(File.join(cookbooks_dir, "*")) +
Dir.glob(File.join(policyfiles_data_bag_dir, "*"))
end

def cookbooks_dir
File.join(export_dir, "cookbooks")
end

def policyfiles_data_bag_dir
File.join(export_dir, "data_bags", "policyfiles")
end

end

end
Expand Down
149 changes: 149 additions & 0 deletions lib/kitchen/provisioner/policyfile_zero.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# -*- encoding: utf-8 -*-
#
# 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-dk and kitchen can only co-exist if kitchen and chef-dk agree on
# the version of mixlib-shellout to use. Kitchen currently locked at 1.x,
# chef-dk is on 2.x
require 'chef-dk/policyfile_services/export_repo'

module Kitchen

module Provisioner

# Policyfile + Chef Zero provisioner.
#
# @author Daniel DeLeo <dan@chef.io>
class PolicyfileZero < ChefBase

# This provsioner 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 :ruby_bindir, "/opt/chef/embedded/bin"

# Policyfile mode does not support the `-j dna.json` option to
# `chef-client`.
default_config :json_attributes, false
default_config :chef_zero_port, 8889

default_config :chef_client_path do |provisioner|
File.join(provisioner[:chef_omnibus_root], %w[bin chef-client])
end

# Emit a warning that Policyfile stuff is still experimental.
#
# (see Base#finalize_config!)
def finalize_config!(*args)
super
banner("Using experimental policyfile mode for chef-client")
warn("The Policyfile feature is under active development.")
warn("For best results, always use the latest chef-client version")
end

# (see Base#create_sandbox)
def create_sandbox
super
prepare_validation_pem
prepare_client_rb
end

# (see Base#run_command)
def run_command
level = config[:log_level] == :info ? :auto : config[:log_level]
chef_client_bin = sudo(config[:chef_client_path])

cmd = "#{chef_client_bin} --local-mode"
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

Util.wrap_command([cmd, *args].join(" "))
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 ChefDK::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
@policy_exporter ||= ChefDK::PolicyfileServices::ExportRepo.new(export_dir: sandbox_path)
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["deployment_group"] = "#{policy_exporter.policy_name}-local"

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
58 changes: 52 additions & 6 deletions spec/unit/policyfile_services/export_repo_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@
expect(lock.cookbook_locks).to have_key("local-cookbook")
end

it "delegates #policy_name to the lockfile" do
expect(export_service.policy_name).to eq("install-example")
end

describe "writing updates to the policyfile lock" do

let(:updated_lockfile_io) { StringIO.new }
Expand Down Expand Up @@ -231,21 +235,59 @@

end

context "When the export dir is already populated" do
context "When the export dir has non-conflicting content" do

let(:file_in_export_dir) { File.join(export_dir, "some_random_cruft") }

let(:extra_data_bag_dir) { File.join(export_dir, "data_bags", "extraneous") }

let(:extra_data_bag_item) { File.join(extra_data_bag_dir, "an_item.json") }

before do
FileUtils.mkdir_p(export_dir)
File.open(file_in_export_dir, "wb+") { |f| f.print "some random cruft" }
FileUtils.mkdir_p(extra_data_bag_dir)
File.open(extra_data_bag_item, "wb+") { |f| f.print "some random cruft" }
end

it "ignores the non-conflicting content and exports" do
export_service.run

expect(File).to exist(file_in_export_dir)
expect(File).to exist(extra_data_bag_item)

expect(File).to be_directory(File.join(export_dir, "cookbooks"))
expect(File).to be_directory(File.join(export_dir, "data_bags"))
end
end

context "When the export dir has conflicting content" do

let(:non_conflicting_file_in_export_dir) { File.join(export_dir, "some_random_cruft") }

let(:cookbooks_dir) { File.join(export_dir, "cookbooks") }

let(:file_in_cookbooks_dir) { File.join(cookbooks_dir, "some_random_cruft") }

let(:policyfiles_data_bag_dir) { File.join(export_dir, "data_bags", "policyfiles") }

let(:extra_policyfile_data_item) { File.join(policyfiles_data_bag_dir, "leftover-policy.json") }

before do
FileUtils.mkdir_p(export_dir)
FileUtils.mkdir_p(cookbooks_dir)
FileUtils.mkdir_p(policyfiles_data_bag_dir)
File.open(non_conflicting_file_in_export_dir, "wb+") { |f| f.print "some random cruft" }
File.open(file_in_cookbooks_dir, "wb+") { |f| f.print "some random cruft" }
File.open(extra_policyfile_data_item, "wb+") { |f| f.print "some random cruft" }
end

it "raises a PolicyfileExportRepoError" do
message = "Export dir (#{export_dir}) not empty. Refusing to export."
message = "Export dir (#{export_dir}) not clean. Refusing to export. (Conflicting files: #{file_in_cookbooks_dir}, #{extra_policyfile_data_item})"
expect { export_service.run }.to raise_error(ChefDK::ExportDirNotEmpty, message)
expect(File).to be_file(file_in_export_dir)
expect(File).to_not exist(File.join(export_dir, "cookbooks"))
expect(File).to_not exist(File.join(export_dir, "data_bags"))
expect(File).to exist(non_conflicting_file_in_export_dir)
expect(File).to exist(file_in_cookbooks_dir)
expect(File).to exist(extra_policyfile_data_item)
end

context "and the force option is set" do
Expand All @@ -255,7 +297,11 @@
it "clears the export dir and exports" do
export_service.run

expect(File).to_not exist(file_in_export_dir)
expect(File).to_not exist(file_in_cookbooks_dir)
expect(File).to_not exist(extra_policyfile_data_item)

expect(File).to exist(non_conflicting_file_in_export_dir)

expect(File).to be_directory(File.join(export_dir, "cookbooks"))
expect(File).to be_directory(File.join(export_dir, "data_bags"))
end
Expand Down

0 comments on commit 0cd8dd9

Please sign in to comment.