Merge branch 'sethvargo/chef_and_tests'

This commit is contained in:
Seth Vargo 2014-10-30 13:43:34 -04:00
commit 67bf170040
16 changed files with 764 additions and 45 deletions

View File

@ -1,7 +1,7 @@
require File.expand_path("../base", __FILE__)
require "vagrant/util/which"
require_relative "base"
module VagrantPlugins
module Chef
module Config
@ -38,10 +38,14 @@ module VagrantPlugins
def validate(machine)
errors = _detected_errors
errors.concat(validate_base(machine))
errors << I18n.t("vagrant.config.chef.server_url_empty") if \
!chef_server_url || chef_server_url.strip == ""
errors << I18n.t("vagrant.config.chef.validation_key_path") if \
!validation_key_path
if chef_server_url.to_s.strip.empty?
errors << I18n.t("vagrant.config.chef.server_url_empty")
end
if validation_key_path.to_s.strip.empty?
errors << I18n.t("vagrant.config.chef.validation_key_path")
end
if delete_client || delete_node
if !Vagrant::Util::Which.which("knife")

View File

@ -1,4 +1,4 @@
require File.expand_path("../base", __FILE__)
require_relative "base"
module VagrantPlugins
module Chef
@ -65,10 +65,14 @@ module VagrantPlugins
def validate(machine)
errors = _detected_errors
errors.concat(validate_base(machine))
errors << I18n.t("vagrant.config.chef.cookbooks_path_empty") if \
!cookbooks_path || [cookbooks_path].flatten.empty?
errors << I18n.t("vagrant.config.chef.environment_path_required") if \
environment && environments_path.empty?
if [cookbooks_path].flatten.compact.empty?
errors << I18n.t("vagrant.config.chef.cookbooks_path_empty")
end
if environment && environments_path.empty?
errors << I18n.t("vagrant.config.chef.environment_path_required")
end
environments_path.each do |type, raw_path|
next if type != :host
@ -76,7 +80,8 @@ module VagrantPlugins
path = Pathname.new(raw_path).expand_path(machine.env.root_path)
if !path.directory?
errors << I18n.t("vagrant.config.chef.environment_path_missing",
path: raw_path.to_s)
path: raw_path.to_s
)
end
end
@ -93,6 +98,8 @@ module VagrantPlugins
# Make sure the path is an array
config = [config] if !config.is_a?(Array) || config.first.is_a?(Symbol)
return [] if config.flatten.compact.empty?
# Make sure all the paths are in the proper format
config.map do |path|
path = [:host, path] if !path.is_a?(Array)

View File

@ -0,0 +1,30 @@
require_relative "chef_solo"
module VagrantPlugins
module Chef
module Config
class ChefZero < ChefSolo
attr_accessor :nodes_path
def initialize
super
@nodes_path = UNSET_VALUE
end
def finalize!
super
@nodes_path = [] if @nodes_path == UNSET_VALUE
# Make sure the path is an array.
@nodes_path = prepare_folders_config(@nodes_path)
end
def validate(machine)
{ "chef zero provisioner" => super["chef solo provisioner"] }
end
end
end
end
end

View File

@ -2,11 +2,10 @@ require "pathname"
require "vagrant"
require_relative "command_builder"
module VagrantPlugins
module Chef
root = Pathname.new(File.expand_path("../", __FILE__))
autoload :CommandBuilder, root.join("command_builder")
class Plugin < Vagrant.plugin("2")
name "chef"
description <<-DESC
@ -15,24 +14,34 @@ module VagrantPlugins
DESC
config(:chef_solo, :provisioner) do
require File.expand_path("../config/chef_solo", __FILE__)
require_relative "config/chef_solo"
Config::ChefSolo
end
config(:chef_client, :provisioner) do
require File.expand_path("../config/chef_client", __FILE__)
require_relative "config/chef_client"
Config::ChefClient
end
config(:chef_zero, :provisioner) do
require_relative "config/chef_zero"
Config::ChefZero
end
provisioner(:chef_solo) do
require File.expand_path("../provisioner/chef_solo", __FILE__)
require_relative "provisioner/chef_solo"
Provisioner::ChefSolo
end
provisioner(:chef_client) do
require File.expand_path("../provisioner/chef_client", __FILE__)
require_relative "provisioner/chef_client"
Provisioner::ChefClient
end
provisioner(:chef_zero) do
require_relative "provisioner/chef_zero"
Provisioner::ChefZero
end
end
end
end

View File

@ -3,7 +3,7 @@ require 'pathname'
require 'vagrant'
require 'vagrant/util/subprocess'
require File.expand_path("../base", __FILE__)
require_relative "base"
module VagrantPlugins
module Chef

View File

@ -2,7 +2,7 @@ require "log4r"
require "vagrant/util/counter"
require File.expand_path("../base", __FILE__)
require_relative "base"
module VagrantPlugins
module Chef
@ -19,6 +19,7 @@ module VagrantPlugins
def initialize(machine, config)
super
@logger = Log4r::Logger.new("vagrant::provisioners::chef_solo")
@shared_folders = []
end
def configure(root_config)
@ -36,13 +37,11 @@ module VagrantPlugins
def provision
# Verify that the proper shared folders exist.
check = []
[@cookbook_folders, @role_folders, @data_bags_folders, @environments_folders].each do |folders|
folders.each do |type, local_path, remote_path|
# We only care about checking folders that have a local path, meaning
# they were shared from the local machine, rather than assumed to
# exist on the VM.
check << remote_path if local_path
end
@shared_folders.each do |type, local_path, remote_path|
# We only care about checking folders that have a local path, meaning
# they were shared from the local machine, rather than assumed to
# exist on the VM.
check << remote_path if local_path
end
chown_provisioning_folder
@ -113,20 +112,21 @@ module VagrantPlugins
root_config.vm.synced_folder(local_path, remote_path, opts)
end
end
@shared_folders += folders
end
def setup_solo_config
cookbooks_path = guest_paths(@cookbook_folders)
roles_path = guest_paths(@role_folders)
data_bags_path = guest_paths(@data_bags_folders).first
environments_path = guest_paths(@environments_folders).first
setup_config("provisioners/chef_solo/solo", "solo.rb", {
cookbooks_path: cookbooks_path,
setup_config("provisioners/chef_solo/solo", "solo.rb", solo_config)
end
def solo_config
{
cookbooks_path: guest_paths(@cookbook_folders),
recipe_url: @config.recipe_url,
roles_path: roles_path,
data_bags_path: data_bags_path,
environments_path: environments_path,
})
roles_path: guest_paths(@role_folders),
data_bags_path: guest_paths(@data_bags_folders).first,
environments_path: guest_paths(@environments_folders).first
}
end
def run_chef_solo

View File

@ -0,0 +1,34 @@
require "log4r"
require_relative "chef_solo"
module VagrantPlugins
module Chef
module Provisioner
# This class implements provisioning via chef-zero.
class ChefZero < ChefSolo
attr_reader :node_folders
def initialize(machine, config)
super
@logger = Log4r::Logger.new("vagrant::provisioners::chef_zero")
end
def configure(root_config)
super
@node_folders = expanded_folders(@config.nodes_path, "nodes")
share_folders(root_config, "csn", @node_folders)
end
def solo_config
super.merge(
local_mode: true,
node_path: guest_paths(@node_folders).first
)
end
end
end
end
end

View File

@ -1310,8 +1310,8 @@ en:
Cookbook path doesn't exist: %{path}
custom_config_path_missing: |-
Path specified for "custom_config_path" does not exist.
server_url_empty: "Chef server URL must be populated."
validation_key_path: "Validation key path must be valid path to your chef server validation key."
server_url_empty: "Chef Server URL must be populated."
validation_key_path: "Validation key path must be valid path to your Chef Server validation key."
loader:
bad_v1_key: |-
Unknown configuration section '%{key}'. If this section was part of

View File

@ -32,6 +32,13 @@ environment_path <%= environments_path.inspect %>
environment "<%= environment %>"
<% end -%>
<% if local_mode -%>
local_mode true
<% end -%>
<% if node_path -%>
node_path <%= node_path.inspect %>
<% end -%>
http_proxy <%= http_proxy.inspect %>
http_proxy_user <%= http_proxy_user.inspect %>
http_proxy_pass <%= http_proxy_pass.inspect %>

View File

@ -0,0 +1,262 @@
require_relative "../../../../base"
require Vagrant.source_root.join("plugins/provisioners/chef/config/base")
describe VagrantPlugins::Chef::Config::Base do
include_context "unit"
subject { described_class.new }
let(:machine) { double("machine") }
describe "#arguments" do
it "defaults to nil" do
subject.finalize!
expect(subject.arguments).to be(nil)
end
end
describe "#attempts" do
it "defaults to 1" do
subject.finalize!
expect(subject.attempts).to eq(1)
end
end
describe "#binary_path" do
it "defaults to nil" do
subject.finalize!
expect(subject.binary_path).to be(nil)
end
end
describe "#binary_env" do
it "defaults to nil" do
subject.finalize!
expect(subject.binary_env).to be(nil)
end
end
describe "#custom_config_path" do
it "defaults to nil" do
subject.finalize!
expect(subject.custom_config_path).to be(nil)
end
end
describe "#environment" do
it "defaults to nil" do
subject.finalize!
expect(subject.environment).to be(nil)
end
end
describe "#formatter" do
it "defaults to nil" do
subject.finalize!
expect(subject.formatter).to be(nil)
end
end
describe "#http_proxy" do
it "defaults to nil" do
subject.finalize!
expect(subject.http_proxy).to be(nil)
end
end
describe "#http_proxy_user" do
it "defaults to nil" do
subject.finalize!
expect(subject.http_proxy_user).to be(nil)
end
end
describe "#http_proxy_pass" do
it "defaults to nil" do
subject.finalize!
expect(subject.http_proxy_pass).to be(nil)
end
end
describe "#https_proxy" do
it "defaults to nil" do
subject.finalize!
expect(subject.https_proxy).to be(nil)
end
end
describe "#https_proxy_user" do
it "defaults to nil" do
subject.finalize!
expect(subject.https_proxy_user).to be(nil)
end
end
describe "#https_proxy_pass" do
it "defaults to nil" do
subject.finalize!
expect(subject.https_proxy_pass).to be(nil)
end
end
describe "#log_level" do
it "defaults to :info" do
subject.finalize!
expect(subject.log_level).to be(:info)
end
it "is converted to a symbol" do
subject.log_level = "foo"
subject.finalize!
expect(subject.log_level).to eq(:foo)
end
end
describe "#no_proxy" do
it "defaults to nil" do
subject.finalize!
expect(subject.no_proxy).to be(nil)
end
end
describe "#node_name" do
it "defaults to nil" do
subject.finalize!
expect(subject.node_name).to be(nil)
end
end
describe "#provisioning_path" do
it "defaults to a tmp_path" do
subject.finalize!
expect(subject.provisioning_path).to match(%r{/tmp/vagrant-chef-\d+})
end
end
describe "#file_backup_path" do
it "defaults to /var/chef/backup" do
subject.finalize!
expect(subject.file_backup_path).to eq("/var/chef/backup")
end
end
describe "#file_cache_path" do
it "defaults to /var/chef/cache" do
subject.finalize!
expect(subject.file_cache_path).to eq("/var/chef/cache")
end
end
describe "#verbose_logging" do
it "defaults to false" do
subject.finalize!
expect(subject.verbose_logging).to be(false)
end
end
describe "#run_list" do
it "defaults to an empty array" do
subject.finalize!
expect(subject.run_list).to be_a(Array)
expect(subject.run_list).to be_empty
end
end
describe "#json" do
it "defaults to an empty hash" do
subject.finalize!
expect(subject.json).to be_a(Hash)
expect(subject.json).to be_empty
end
end
describe "#add_recipe" do
context "when the prefix is given" do
it "adds the value to the run_list" do
subject.add_recipe("recipe[foo::bar]")
expect(subject.run_list).to eq %w(recipe[foo::bar])
end
end
context "when the prefix is not given" do
it "adds the prefixed value to the run_list" do
subject.add_recipe("foo::bar")
expect(subject.run_list).to eq %w(recipe[foo::bar])
end
end
end
describe "#add_role" do
context "when the prefix is given" do
it "adds the value to the run_list" do
subject.add_role("role[foo]")
expect(subject.run_list).to eq %w(role[foo])
end
end
context "when the prefix is not given" do
it "adds the prefixed value to the run_list" do
subject.add_role("foo")
expect(subject.run_list).to eq %w(role[foo])
end
end
end
describe "#validate_base" do
context "when #custom_config_path does not exist" do
let(:path) { "/path/to/file" }
before do
allow(File).to receive(:file?)
.with(path)
.and_return(false)
allow(machine).to receive(:env)
.and_return(double("env",
root_path: "",
))
end
it "returns an error" do
subject.custom_config_path = path
subject.finalize!
expect(subject.validate_base(machine))
.to eq ['Path specified for "custom_config_path" does not exist.']
end
end
end
describe "#merge" do
it "merges the json hash" do
a = described_class.new.tap do |i|
i.json = { "foo" => "bar" }
end
b = described_class.new.tap do |i|
i.json = { "zip" => "zap" }
end
result = a.merge(b)
expect(result.json).to eq(
"foo" => "bar",
"zip" => "zap",
)
end
it "appends the run_list array" do
a = described_class.new.tap do |i|
i.run_list = ["recipe[foo::bar]"]
end
b = described_class.new.tap do |i|
i.run_list = ["recipe[zip::zap]"]
end
result = a.merge(b)
expect(result.run_list).to eq %w(
recipe[foo::bar]
recipe[zip::zap]
)
end
end
end

View File

@ -0,0 +1,136 @@
require_relative "../../../../base"
require Vagrant.source_root.join("plugins/provisioners/chef/config/chef_client")
describe VagrantPlugins::Chef::Config::ChefClient do
include_context "unit"
subject { described_class.new }
let(:machine) { double("machine") }
describe "#chef_server_url" do
it "defaults to nil" do
subject.finalize!
expect(subject.chef_server_url).to be(nil)
end
end
describe "#client_key_path" do
it "defaults to /etc/chef/client.pem" do
subject.finalize!
expect(subject.client_key_path).to eq("/etc/chef/client.pem")
end
end
describe "#delete_client" do
it "defaults to false" do
subject.finalize!
expect(subject.delete_client).to be(false)
end
end
describe "#delete_node" do
it "defaults to false" do
subject.finalize!
expect(subject.delete_node).to be(false)
end
end
describe "#validation_key_path" do
it "defaults to nil" do
subject.finalize!
expect(subject.validation_key_path).to be(nil)
end
end
describe "#validation_client_name" do
it "defaults to chef-validator" do
subject.finalize!
expect(subject.validation_client_name).to eq("chef-validator")
end
end
describe "#validate" do
before do
allow(machine).to receive(:env)
.and_return(double("env",
root_path: "",
))
subject.chef_server_url = "https://example.com"
subject.validation_key_path = "/path/to/key.pem"
end
let(:result) { subject.validate(machine) }
let(:errors) { result["chef client provisioner"] }
context "when the chef_server_url is nil" do
it "returns an error" do
subject.chef_server_url = nil
subject.finalize!
expect(errors).to eq([I18n.t("vagrant.config.chef.server_url_empty")])
end
end
context "when the chef_server_url is blank" do
it "returns an error" do
subject.chef_server_url = " "
subject.finalize!
expect(errors).to eq([I18n.t("vagrant.config.chef.server_url_empty")])
end
end
context "when the validation_key_path is nil" do
it "returns an error" do
subject.validation_key_path = nil
subject.finalize!
expect(errors).to eq([I18n.t("vagrant.config.chef.validation_key_path")])
end
end
context "when the validation_key_path is blank" do
it "returns an error" do
subject.validation_key_path = " "
subject.finalize!
expect(errors).to eq([I18n.t("vagrant.config.chef.validation_key_path")])
end
end
context "when #delete_client is given" do
before { subject.delete_client = true }
context "when knife does not exist" do
before do
allow(Vagrant::Util::Which)
.to receive(:which)
.with("knife")
.and_return(nil)
end
it "returns an error" do
subject.finalize!
expect(errors).to eq([I18n.t("vagrant.chef_config_knife_not_found")])
end
end
end
context "when #delete_node is given" do
before { subject.delete_node = true }
context "when knife does not exist" do
before do
allow(Vagrant::Util::Which)
.to receive(:which)
.with("knife")
.and_return(nil)
end
it "returns an error" do
subject.finalize!
expect(errors).to eq([I18n.t("vagrant.chef_config_knife_not_found")])
end
end
end
end
end

View File

@ -0,0 +1,147 @@
require_relative "../../../../base"
require Vagrant.source_root.join("plugins/provisioners/chef/config/chef_solo")
describe VagrantPlugins::Chef::Config::ChefSolo do
include_context "unit"
subject { described_class.new }
let(:machine) { double("machine") }
describe "#cookbooks_path" do
it "defaults to something" do
subject.finalize!
expect(subject.cookbooks_path).to eq([
[:host, "cookbooks"],
[:vm, "cookbooks"],
])
end
end
describe "#data_bags_path" do
it "defaults to an empty array" do
subject.finalize!
expect(subject.data_bags_path).to be_a(Array)
expect(subject.data_bags_path).to be_empty
end
end
describe "#environments_path" do
it "defaults to an empty array" do
subject.finalize!
expect(subject.environments_path).to be_a(Array)
expect(subject.environments_path).to be_empty
end
it "merges deeply nested paths" do
subject.environments_path = ["/foo", "/bar", ["/zip"]]
subject.finalize!
expect(subject.environments_path)
.to eq([:host, :host, :host].zip %w(/foo /bar /zip))
end
end
describe "#recipe_url" do
it "defaults to nil" do
subject.finalize!
expect(subject.recipe_url).to be(nil)
end
end
describe "#roles_path" do
it "defaults to an empty array" do
subject.finalize!
expect(subject.roles_path).to be_a(Array)
expect(subject.roles_path).to be_empty
end
end
describe "#synced_folder_type" do
it "defaults to nil" do
subject.finalize!
expect(subject.synced_folder_type).to be(nil)
end
end
describe "#validate" do
before do
allow(machine).to receive(:env)
.and_return(double("env",
root_path: "",
))
subject.cookbooks_path = ["/cookbooks", "/more/cookbooks"]
end
let(:result) { subject.validate(machine) }
let(:errors) { result["chef solo provisioner"] }
context "when the cookbooks_path is nil" do
it "returns an error" do
subject.cookbooks_path = nil
subject.finalize!
expect(errors).to eq [I18n.t("vagrant.config.chef.cookbooks_path_empty")]
end
end
context "when the cookbooks_path is an empty array" do
it "returns an error" do
subject.cookbooks_path = []
subject.finalize!
expect(errors).to eq [I18n.t("vagrant.config.chef.cookbooks_path_empty")]
end
end
context "when the cookbooks_path is an array with nil" do
it "returns an error" do
subject.cookbooks_path = [nil, nil]
subject.finalize!
expect(errors).to eq [I18n.t("vagrant.config.chef.cookbooks_path_empty")]
end
end
context "when environments is given" do
before do
subject.environment = "production"
end
context "when the environments_path is nil" do
it "returns an error" do
subject.environments_path = nil
subject.finalize!
expect(errors).to eq [I18n.t("vagrant.config.chef.environment_path_required")]
end
end
context "when the environments_path is an empty array" do
it "returns an error" do
subject.environments_path = []
subject.finalize!
expect(errors).to eq [I18n.t("vagrant.config.chef.environment_path_required")]
end
end
context "when the environments_path is an array with nil" do
it "returns an error" do
subject.environments_path = [nil, nil]
subject.finalize!
expect(errors).to eq [I18n.t("vagrant.config.chef.environment_path_required")]
end
end
context "when the environments_path does not exist" do
it "returns an error" do
env_path = "/path/to/environments/that/will/never/exist"
subject.environments_path = env_path
subject.finalize!
expect(errors).to eq [
I18n.t("vagrant.config.chef.environment_path_missing",
path: env_path
)
]
end
end
end
end
end

View File

@ -0,0 +1,19 @@
require_relative "../../../../base"
require Vagrant.source_root.join("plugins/provisioners/chef/config/chef_zero")
describe VagrantPlugins::Chef::Config::ChefZero do
include_context "unit"
subject { described_class.new }
let(:machine) { double("machine") }
describe "#nodes_path" do
it "defaults to an array" do
subject.finalize!
expect(subject.nodes_path).to be_a(Array)
expect(subject.nodes_path).to be_empty
end
end
end

View File

@ -5,11 +5,11 @@ sidebar_current: "provisioning-chefcommon"
# Shared Chef Options
This page documents the list of available options that are available in
both the
[Chef solo](/v2/provisioning/chef_solo.html)
This page documents the list of available options that are available in the
[Chef Solo](/v2/provisioning/chef_solo.html),
[Chef Zero](/v2/provisioning/chef_zero.html)
and
[Chef client](/v2/provisioning/chef_client.html)
[Chef Client](/v2/provisioning/chef_client.html)
provisioners.
* `arguments` (string) - A list of additional arguments to pass on the

View File

@ -34,7 +34,7 @@ available below this section.
Note that only the Chef-solo specific options are shown below. There is
also a large set of [common options](/v2/provisioning/chef_common.html)
that are available with both the Chef Solo and Chef client provisioners.
that are available with all Chef provisioners.
* `cookbooks_path` (string or array) - A list of paths to where cookbooks
are stored. By default this is "cookbooks", expecting a cookbooks folder

View File

@ -0,0 +1,64 @@
---
page_title: "Chef Zero - Provisioning"
sidebar_current: "provisioning-chefzero"
---
# Chef Zero Provisioner
**Provisioner name: `chef_zero`**
The Chef Zero provisioner allows you to provision the guest using
[Chef](https://www.getchef.com/chef/), specifically with
[Chef Zero/local mode](https://docs.getchef.com/ctl_chef_client.html#run-in-local-mode).
This new provisioner is a middle ground between running a full blown
Chef Server and using the limited [Chef Solo](/v2/provisioning/chef_solo.html)
provisioner. It runs a local in-memory Chef Server and fakes the validation
and client key registration.
<div class="alert alert-warn">
<p>
<strong>Warning:</strong> If you're not familiar with Chef and Vagrant already,
I recommend starting with the <a href="/v2/provisioning/shell.html">shell
provisioner</a>. However, if you're comfortable with Vagrant already, Vagrant
is the best way to learn Chef.
</p>
</div>
## Options
This section lists the complete set of available options for the Chef Zero
provisioner. More detailed examples of how to use the provisioner are
available below this section.
Note that only the Chef Zero specific options are shown below, but all [Chef
Solo options](/v2/provisioning/chef_solo.html), including the [common Chef
provisioner options](/v2/provisioning/chef_common.html), are also inherited.
* `nodes_path` (string) - A path where the Chef nodes are stored. Be default,
no node path is set.
## Usage
The Chef Zero provisioner is configured basically the same way as the Chef Solo
provisioner. See the [Chef Solo documentations](/v2/provisioning/chef_solo.html)
for more information.
A basic example could look like this:
```ruby
Vagrant.configure("2") do |config|
config.vm.provision "chef_zero" do |chef|
# Specify the local paths where Chef data is stored
chef.cookbooks_path = "cookbooks"
chef.roles_path = "roles"
chef.nodes_path = "nodes"
# Add a recipe
chef.add_recipe "apache"
# Or maybe a role
chef.add_role "web"
end
end
```