From dec5dcdda3f79f2908d05ce84eb77991a999527f Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Tue, 20 May 2014 00:05:46 +0300 Subject: [PATCH 01/14] provisioners/chef: Add `chef_zero` provisioner Add Chef Zero provisioner using `local_mode` configuration for chef-solo/chef-client. --- plugins/provisioners/chef/config/chef_zero.rb | 30 ++++++++++++++++ plugins/provisioners/chef/plugin.rb | 10 ++++++ .../chef/provisioner/chef_solo.rb | 34 +++++++++---------- .../chef/provisioner/chef_zero.rb | 34 +++++++++++++++++++ templates/provisioners/chef_solo/solo.erb | 7 ++++ 5 files changed, 98 insertions(+), 17 deletions(-) create mode 100644 plugins/provisioners/chef/config/chef_zero.rb create mode 100644 plugins/provisioners/chef/provisioner/chef_zero.rb diff --git a/plugins/provisioners/chef/config/chef_zero.rb b/plugins/provisioners/chef/config/chef_zero.rb new file mode 100644 index 000000000..ca7c5618b --- /dev/null +++ b/plugins/provisioners/chef/config/chef_zero.rb @@ -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 diff --git a/plugins/provisioners/chef/plugin.rb b/plugins/provisioners/chef/plugin.rb index afe068047..0a771f300 100644 --- a/plugins/provisioners/chef/plugin.rb +++ b/plugins/provisioners/chef/plugin.rb @@ -24,6 +24,11 @@ module VagrantPlugins Config::ChefClient end + config(:chef_zero, :provisioner) do + require File.expand_path("../config/chef_zero", __FILE__) + Config::ChefZero + end + provisioner(:chef_solo) do require File.expand_path("../provisioner/chef_solo", __FILE__) Provisioner::ChefSolo @@ -33,6 +38,11 @@ module VagrantPlugins require File.expand_path("../provisioner/chef_client", __FILE__) Provisioner::ChefClient end + + provisioner(:chef_zero) do + require File.expand_path("../provisioner/chef_zero", __FILE__) + Provisioner::ChefZero + end end end end diff --git a/plugins/provisioners/chef/provisioner/chef_solo.rb b/plugins/provisioners/chef/provisioner/chef_solo.rb index bf66c99d5..e93ba7f5b 100644 --- a/plugins/provisioners/chef/provisioner/chef_solo.rb +++ b/plugins/provisioners/chef/provisioner/chef_solo.rb @@ -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 diff --git a/plugins/provisioners/chef/provisioner/chef_zero.rb b/plugins/provisioners/chef/provisioner/chef_zero.rb new file mode 100644 index 000000000..8ccae6130 --- /dev/null +++ b/plugins/provisioners/chef/provisioner/chef_zero.rb @@ -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 diff --git a/templates/provisioners/chef_solo/solo.erb b/templates/provisioners/chef_solo/solo.erb index fd52fff91..b7d08a65b 100644 --- a/templates/provisioners/chef_solo/solo.erb +++ b/templates/provisioners/chef_solo/solo.erb @@ -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 %> From 2d78f1a8168c379dce6acdd471f774f296674006 Mon Sep 17 00:00:00 2001 From: Teemu Matilainen Date: Sat, 11 Oct 2014 22:59:52 +0300 Subject: [PATCH 02/14] website/docs: Add documentation for the Chef Zero provisioner --- .../v2/provisioning/chef_common.html.md | 8 +-- .../source/v2/provisioning/chef_solo.html.md | 2 +- .../source/v2/provisioning/chef_zero.html.md | 65 +++++++++++++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 website/docs/source/v2/provisioning/chef_zero.html.md diff --git a/website/docs/source/v2/provisioning/chef_common.html.md b/website/docs/source/v2/provisioning/chef_common.html.md index 1a41691ed..ed776ad0c 100644 --- a/website/docs/source/v2/provisioning/chef_common.html.md +++ b/website/docs/source/v2/provisioning/chef_common.html.md @@ -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 diff --git a/website/docs/source/v2/provisioning/chef_solo.html.md b/website/docs/source/v2/provisioning/chef_solo.html.md index cb8e34ac8..d916f4fe2 100644 --- a/website/docs/source/v2/provisioning/chef_solo.html.md +++ b/website/docs/source/v2/provisioning/chef_solo.html.md @@ -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 diff --git a/website/docs/source/v2/provisioning/chef_zero.html.md b/website/docs/source/v2/provisioning/chef_zero.html.md new file mode 100644 index 000000000..ad5435642 --- /dev/null +++ b/website/docs/source/v2/provisioning/chef_zero.html.md @@ -0,0 +1,65 @@ +--- +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. + +
+

+ Warning: If you're not familiar with Chef and Vagrant already, + I recommend starting with the shell + provisioner. However, if you're comfortable with Vagrant already, Vagrant + is the best way to learn Chef. + +

+
+ +## 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 +``` From 1169c80a725a4147b9b21951aecb262cfbddd9c3 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 11:21:56 -0400 Subject: [PATCH 03/14] Use require_relative for faster loading require_relative is an order of magnitude faster on Windows --- plugins/provisioners/chef/config/chef_client.rb | 4 ++-- plugins/provisioners/chef/config/chef_solo.rb | 2 +- plugins/provisioners/chef/plugin.rb | 17 ++++++++--------- .../chef/provisioner/chef_client.rb | 2 +- .../provisioners/chef/provisioner/chef_solo.rb | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/plugins/provisioners/chef/config/chef_client.rb b/plugins/provisioners/chef/config/chef_client.rb index 9684d2b0e..e1585c084 100644 --- a/plugins/provisioners/chef/config/chef_client.rb +++ b/plugins/provisioners/chef/config/chef_client.rb @@ -1,7 +1,7 @@ -require File.expand_path("../base", __FILE__) - require "vagrant/util/which" +require_relative "base" + module VagrantPlugins module Chef module Config diff --git a/plugins/provisioners/chef/config/chef_solo.rb b/plugins/provisioners/chef/config/chef_solo.rb index 59c3b858e..e8e5c8e04 100644 --- a/plugins/provisioners/chef/config/chef_solo.rb +++ b/plugins/provisioners/chef/config/chef_solo.rb @@ -1,4 +1,4 @@ -require File.expand_path("../base", __FILE__) +require_relative "base" module VagrantPlugins module Chef diff --git a/plugins/provisioners/chef/plugin.rb b/plugins/provisioners/chef/plugin.rb index 0a771f300..817de36a3 100644 --- a/plugins/provisioners/chef/plugin.rb +++ b/plugins/provisioners/chef/plugin.rb @@ -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,32 +14,32 @@ 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 File.expand_path("../config/chef_zero", __FILE__) + 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 File.expand_path("../provisioner/chef_zero", __FILE__) + require_relative "provisioner/chef_zero" Provisioner::ChefZero end end diff --git a/plugins/provisioners/chef/provisioner/chef_client.rb b/plugins/provisioners/chef/provisioner/chef_client.rb index d91b3c22c..9b0c2341f 100644 --- a/plugins/provisioners/chef/provisioner/chef_client.rb +++ b/plugins/provisioners/chef/provisioner/chef_client.rb @@ -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 diff --git a/plugins/provisioners/chef/provisioner/chef_solo.rb b/plugins/provisioners/chef/provisioner/chef_solo.rb index e93ba7f5b..a8c1e84d3 100644 --- a/plugins/provisioners/chef/provisioner/chef_solo.rb +++ b/plugins/provisioners/chef/provisioner/chef_solo.rb @@ -2,7 +2,7 @@ require "log4r" require "vagrant/util/counter" -require File.expand_path("../base", __FILE__) +require_relative "base" module VagrantPlugins module Chef From 721edf70b97df46ff1c497b8bece049acaf073b1 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 11:22:26 -0400 Subject: [PATCH 04/14] Make Chef Zero lowercase in validation (conforms to other keys) --- plugins/provisioners/chef/config/chef_zero.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/provisioners/chef/config/chef_zero.rb b/plugins/provisioners/chef/config/chef_zero.rb index ca7c5618b..993a3fab9 100644 --- a/plugins/provisioners/chef/config/chef_zero.rb +++ b/plugins/provisioners/chef/config/chef_zero.rb @@ -22,7 +22,7 @@ module VagrantPlugins end def validate(machine) - { "Chef Zero provisioner" => super["chef solo provisioner"] } + { "chef zero provisioner" => super["chef solo provisioner"] } end end end From f009db610141b727d924655156312a7affd7c116 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 11:22:38 -0400 Subject: [PATCH 05/14] Add tests for Chef base Config --- .../provisioners/chef/config/base_test.rb | 262 ++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 test/unit/plugins/provisioners/chef/config/base_test.rb diff --git a/test/unit/plugins/provisioners/chef/config/base_test.rb b/test/unit/plugins/provisioners/chef/config/base_test.rb new file mode 100644 index 000000000..1f8fab9b0 --- /dev/null +++ b/test/unit/plugins/provisioners/chef/config/base_test.rb @@ -0,0 +1,262 @@ +require_relative "../../../../base" + +require Vagrant.source_root.join("plugins/provisioners/chef/config/base") + +describe VagrantPlugins::Chef::Config::Base, :focus 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 From 8b3ec500c8a51f067460abb446c40b2cd17327da Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 11:41:50 -0400 Subject: [PATCH 06/14] Remove :focus tag (accidentally left from previous commit) --- test/unit/plugins/provisioners/chef/config/base_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/plugins/provisioners/chef/config/base_test.rb b/test/unit/plugins/provisioners/chef/config/base_test.rb index 1f8fab9b0..d56025f5f 100644 --- a/test/unit/plugins/provisioners/chef/config/base_test.rb +++ b/test/unit/plugins/provisioners/chef/config/base_test.rb @@ -2,7 +2,7 @@ require_relative "../../../../base" require Vagrant.source_root.join("plugins/provisioners/chef/config/base") -describe VagrantPlugins::Chef::Config::Base, :focus do +describe VagrantPlugins::Chef::Config::Base do include_context "unit" subject { described_class.new } From 6814f9c97dc035f07aac4eeaf8bfda08bf8d7dc0 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 11:41:59 -0400 Subject: [PATCH 07/14] Capitalize Chef Server in errors --- templates/locales/en.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 333091fbb..929a5c338 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -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 From 7e71d72db8cdb92662bc522c0b4b879c97e4a9f1 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 11:42:15 -0400 Subject: [PATCH 08/14] Check for empty chef_server_url and validation_key_path --- plugins/provisioners/chef/config/chef_client.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/provisioners/chef/config/chef_client.rb b/plugins/provisioners/chef/config/chef_client.rb index e1585c084..4805310b3 100644 --- a/plugins/provisioners/chef/config/chef_client.rb +++ b/plugins/provisioners/chef/config/chef_client.rb @@ -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") From 9c56061fa9c9c411145fb732a53f3bc9dea292d1 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 11:42:25 -0400 Subject: [PATCH 09/14] Add tests for Chef Client config --- .../chef/config/chef_client_test.rb | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 test/unit/plugins/provisioners/chef/config/chef_client_test.rb diff --git a/test/unit/plugins/provisioners/chef/config/chef_client_test.rb b/test/unit/plugins/provisioners/chef/config/chef_client_test.rb new file mode 100644 index 000000000..17f2dc321 --- /dev/null +++ b/test/unit/plugins/provisioners/chef/config/chef_client_test.rb @@ -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 From af9177550a97511bd5cf1403ec1e02da0ca7626b Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 12:00:45 -0400 Subject: [PATCH 10/14] Properly handle empty/nil values in Chef Solo config --- plugins/provisioners/chef/config/chef_solo.rb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/plugins/provisioners/chef/config/chef_solo.rb b/plugins/provisioners/chef/config/chef_solo.rb index e8e5c8e04..524596962 100644 --- a/plugins/provisioners/chef/config/chef_solo.rb +++ b/plugins/provisioners/chef/config/chef_solo.rb @@ -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) From a8a35757ee284c76a6d52e717d0fe84d1bbdf075 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 12:00:51 -0400 Subject: [PATCH 11/14] Add tests for Chef Solo config --- .../chef/config/chef_solo_test.rb | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 test/unit/plugins/provisioners/chef/config/chef_solo_test.rb diff --git a/test/unit/plugins/provisioners/chef/config/chef_solo_test.rb b/test/unit/plugins/provisioners/chef/config/chef_solo_test.rb new file mode 100644 index 000000000..d6a2a03af --- /dev/null +++ b/test/unit/plugins/provisioners/chef/config/chef_solo_test.rb @@ -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 From f5b9044e39c5b54e8884b917622b3b155b24f4f8 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 12:02:29 -0400 Subject: [PATCH 12/14] Add tests for Chef Zero config --- .../chef/config/chef_zero_test.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/unit/plugins/provisioners/chef/config/chef_zero_test.rb diff --git a/test/unit/plugins/provisioners/chef/config/chef_zero_test.rb b/test/unit/plugins/provisioners/chef/config/chef_zero_test.rb new file mode 100644 index 000000000..6149a3bde --- /dev/null +++ b/test/unit/plugins/provisioners/chef/config/chef_zero_test.rb @@ -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 From ea3c6fe902ef66e0a40b1a0e11464d72f6b86004 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 12:05:15 -0400 Subject: [PATCH 13/14] Use spaces in website docs --- .../docs/source/v2/provisioning/chef_zero.html.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/website/docs/source/v2/provisioning/chef_zero.html.md b/website/docs/source/v2/provisioning/chef_zero.html.md index ad5435642..f716d3ff9 100644 --- a/website/docs/source/v2/provisioning/chef_zero.html.md +++ b/website/docs/source/v2/provisioning/chef_zero.html.md @@ -17,13 +17,12 @@ provisioner. It runs a local in-memory Chef Server and fakes the validation and client key registration.
-

- Warning: If you're not familiar with Chef and Vagrant already, - I recommend starting with the shell - provisioner. However, if you're comfortable with Vagrant already, Vagrant - is the best way to learn Chef. - -

+

+ Warning: If you're not familiar with Chef and Vagrant already, + I recommend starting with the shell + provisioner. However, if you're comfortable with Vagrant already, Vagrant + is the best way to learn Chef. +

## Options From 3b416db29919df524fd579521f18b4e10718bbe7 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 30 Oct 2014 12:07:42 -0400 Subject: [PATCH 14/14] Tabs -> Spaces --- .../provisioners/chef/config/base_test.rb | 326 +++++++++--------- 1 file changed, 163 insertions(+), 163 deletions(-) diff --git a/test/unit/plugins/provisioners/chef/config/base_test.rb b/test/unit/plugins/provisioners/chef/config/base_test.rb index d56025f5f..062c48b08 100644 --- a/test/unit/plugins/provisioners/chef/config/base_test.rb +++ b/test/unit/plugins/provisioners/chef/config/base_test.rb @@ -10,253 +10,253 @@ describe VagrantPlugins::Chef::Config::Base do let(:machine) { double("machine") } describe "#arguments" do - it "defaults to nil" do - subject.finalize! - expect(subject.arguments).to be(nil) - end + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 "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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 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 + 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 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 + 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" } + 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) + before do + allow(File).to receive(:file?) + .with(path) + .and_return(false) - allow(machine).to receive(:env) - .and_return(double("env", - root_path: "", - )) - end + allow(machine).to receive(:env) + .and_return(double("env", + root_path: "", + )) + end - it "returns an error" do - subject.custom_config_path = path - subject.finalize! + 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 + 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 + 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 + 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 + 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 + result = a.merge(b) + expect(result.run_list).to eq %w( + recipe[foo::bar] + recipe[zip::zap] + ) + end end end