Add Chef Apply provisioner
This commit is contained in:
parent
514101816b
commit
59eb0ad2e8
|
@ -0,0 +1,68 @@
|
||||||
|
module VagrantPlugins
|
||||||
|
module Chef
|
||||||
|
module Config
|
||||||
|
class ChefApply < Vagrant.plugin("2", :config)
|
||||||
|
extend Vagrant::Util::Counter
|
||||||
|
|
||||||
|
# The raw recipe text (as a string) to execute via chef-apply.
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :recipe
|
||||||
|
|
||||||
|
# The path (on the guest) where the uploaded apply recipe should be
|
||||||
|
# written (/tmp/vagrant-chef-apply-#.rb).
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :upload_path
|
||||||
|
|
||||||
|
# The Chef log level.
|
||||||
|
# @return [String]
|
||||||
|
attr_accessor :log_level
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@recipe = UNSET_VALUE
|
||||||
|
|
||||||
|
@log_level = UNSET_VALUE
|
||||||
|
@upload_path = UNSET_VALUE
|
||||||
|
end
|
||||||
|
|
||||||
|
def finalize!
|
||||||
|
@recipe = nil if @recipe == UNSET_VALUE
|
||||||
|
|
||||||
|
if @log_level == UNSET_VALUE
|
||||||
|
@log_level = :info
|
||||||
|
else
|
||||||
|
@log_level = @log_level.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
if @upload_path == UNSET_VALUE
|
||||||
|
counter = self.class.get_and_update_counter(:chef_apply)
|
||||||
|
@upload_path = "/tmp/vagrant-chef-apply-#{counter}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate(machine)
|
||||||
|
errors = _detected_errors
|
||||||
|
|
||||||
|
if missing(recipe)
|
||||||
|
errors << I18n.t("vagrant.provisioners.chef.recipe_empty")
|
||||||
|
end
|
||||||
|
|
||||||
|
if missing(log_level)
|
||||||
|
errors << I18n.t("vagrant.provisioners.chef.log_level_empty")
|
||||||
|
end
|
||||||
|
|
||||||
|
if missing(upload_path)
|
||||||
|
errors << I18n.t("vagrant.provisioners.chef.upload_path_empty")
|
||||||
|
end
|
||||||
|
|
||||||
|
{ "chef apply provisioner" => errors }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Determine if the given string is "missing" (blank)
|
||||||
|
# @return [true, false]
|
||||||
|
def missing(obj)
|
||||||
|
obj.to_s.strip.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,12 +10,12 @@ module VagrantPlugins
|
||||||
name "chef"
|
name "chef"
|
||||||
description <<-DESC
|
description <<-DESC
|
||||||
Provides support for provisioning your virtual machines with
|
Provides support for provisioning your virtual machines with
|
||||||
Chef via `chef-solo` or `chef-client`.
|
Chef via `chef-solo`, `chef-client`, or `chef-apply`.
|
||||||
DESC
|
DESC
|
||||||
|
|
||||||
config(:chef_solo, :provisioner) do
|
config(:chef_apply, :provisioner) do
|
||||||
require_relative "config/chef_solo"
|
require_relative "config/chef_apply"
|
||||||
Config::ChefSolo
|
Config::ChefApply
|
||||||
end
|
end
|
||||||
|
|
||||||
config(:chef_client, :provisioner) do
|
config(:chef_client, :provisioner) do
|
||||||
|
@ -23,14 +23,19 @@ module VagrantPlugins
|
||||||
Config::ChefClient
|
Config::ChefClient
|
||||||
end
|
end
|
||||||
|
|
||||||
|
config(:chef_solo, :provisioner) do
|
||||||
|
require_relative "config/chef_solo"
|
||||||
|
Config::ChefSolo
|
||||||
|
end
|
||||||
|
|
||||||
config(:chef_zero, :provisioner) do
|
config(:chef_zero, :provisioner) do
|
||||||
require_relative "config/chef_zero"
|
require_relative "config/chef_zero"
|
||||||
Config::ChefZero
|
Config::ChefZero
|
||||||
end
|
end
|
||||||
|
|
||||||
provisioner(:chef_solo) do
|
provisioner(:chef_apply) do
|
||||||
require_relative "provisioner/chef_solo"
|
require_relative "provisioner/chef_apply"
|
||||||
Provisioner::ChefSolo
|
Provisioner::ChefApply
|
||||||
end
|
end
|
||||||
|
|
||||||
provisioner(:chef_client) do
|
provisioner(:chef_client) do
|
||||||
|
@ -38,6 +43,11 @@ module VagrantPlugins
|
||||||
Provisioner::ChefClient
|
Provisioner::ChefClient
|
||||||
end
|
end
|
||||||
|
|
||||||
|
provisioner(:chef_solo) do
|
||||||
|
require_relative "provisioner/chef_solo"
|
||||||
|
Provisioner::ChefSolo
|
||||||
|
end
|
||||||
|
|
||||||
provisioner(:chef_zero) do
|
provisioner(:chef_zero) do
|
||||||
require_relative "provisioner/chef_zero"
|
require_relative "provisioner/chef_zero"
|
||||||
Provisioner::ChefZero
|
Provisioner::ChefZero
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
require "tempfile"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Chef
|
||||||
|
module Provisioner
|
||||||
|
class ChefApply < Vagrant.plugin("2", :provisioner)
|
||||||
|
def provision
|
||||||
|
command = "chef-apply"
|
||||||
|
command << " --log-level #{config.log_level}"
|
||||||
|
command << " #{config.upload_path}"
|
||||||
|
|
||||||
|
user = @machine.ssh_info[:username]
|
||||||
|
|
||||||
|
# Reset upload path permissions for the current ssh user
|
||||||
|
@machine.communicate.sudo("mkdir -p #{config.upload_path}")
|
||||||
|
@machine.communicate.sudo("chown -R #{user} #{config.upload_path}")
|
||||||
|
|
||||||
|
# Upload the recipe
|
||||||
|
upload_recipe
|
||||||
|
|
||||||
|
@machine.ui.info(I18n.t("vagrant.provisioners.chef.running_chef_apply",
|
||||||
|
script: config.path)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Execute it with sudo
|
||||||
|
@machine.communicate.sudo(command) do |type, data|
|
||||||
|
if [:stderr, :stdout].include?(type)
|
||||||
|
# Output the data with the proper color based on the stream.
|
||||||
|
color = (type == :stdout) ? :green : :red
|
||||||
|
|
||||||
|
# Chomp the data to avoid the newlines that the Chef outputs
|
||||||
|
@machine.env.ui.info(data.chomp, color: color, prefix: false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write the raw recipe contents to a tempfile and upload that to the
|
||||||
|
# machine.
|
||||||
|
def upload_recipe
|
||||||
|
# Write the raw recipe contents to a tempfile
|
||||||
|
file = Tempfile.new(["vagrant-chef-apply", ".rb"])
|
||||||
|
file.write(config.recipe)
|
||||||
|
file.rewind
|
||||||
|
|
||||||
|
# Upload the tempfile to the guest
|
||||||
|
destination = File.join(config.upload_path, "recipe.rb")
|
||||||
|
@machine.communicate.upload(file.path, destination)
|
||||||
|
ensure
|
||||||
|
# Delete our template
|
||||||
|
file.close
|
||||||
|
file.unlink
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1754,10 +1754,18 @@ en:
|
||||||
"The cookbook path '%{path}' doesn't exist. Ignoring..."
|
"The cookbook path '%{path}' doesn't exist. Ignoring..."
|
||||||
json: "Generating chef JSON and uploading..."
|
json: "Generating chef JSON and uploading..."
|
||||||
client_key_folder: "Creating folder to hold client key..."
|
client_key_folder: "Creating folder to hold client key..."
|
||||||
|
log_level_empty: |-
|
||||||
|
The Chef provisioner requires a log level. If you did not set a
|
||||||
|
log level, this is probably a bug and should be reported.
|
||||||
upload_validation_key: "Uploading chef client validation key..."
|
upload_validation_key: "Uploading chef client validation key..."
|
||||||
upload_encrypted_data_bag_secret_key: "Uploading chef encrypted data bag secret key..."
|
upload_encrypted_data_bag_secret_key: "Uploading chef encrypted data bag secret key..."
|
||||||
|
recipe_empty: |-
|
||||||
|
Chef Apply provisioning requires that the `config.chef.recipe` be set
|
||||||
|
to a string containing the recipe contents you want to execute on the
|
||||||
|
guest.
|
||||||
running_client: "Running chef-client..."
|
running_client: "Running chef-client..."
|
||||||
running_client_again: "Running chef-client again (failed to converge)..."
|
running_client_again: "Running chef-client again (failed to converge)..."
|
||||||
|
running_client_apply: "Running chef-apply..."
|
||||||
running_solo: "Running chef-solo..."
|
running_solo: "Running chef-solo..."
|
||||||
running_solo_again: "Running chef-solo again (failed to converge)..."
|
running_solo_again: "Running chef-solo again (failed to converge)..."
|
||||||
missing_shared_folders: |-
|
missing_shared_folders: |-
|
||||||
|
@ -1784,6 +1792,9 @@ en:
|
||||||
server_validation_key_doesnt_exist: |-
|
server_validation_key_doesnt_exist: |-
|
||||||
The validation key set for `config.chef.validation_key_path` does not exist! This
|
The validation key set for `config.chef.validation_key_path` does not exist! This
|
||||||
file needs to exist so it can be uploaded to the virtual machine.
|
file needs to exist so it can be uploaded to the virtual machine.
|
||||||
|
upload_path_empty: |-
|
||||||
|
The Chef Apply provisioner requires that the `config.chef.upload_path`
|
||||||
|
be set to a non-nil, non-empty value.
|
||||||
deleting_from_server: "Deleting %{deletable} \"%{name}\" from Chef server..."
|
deleting_from_server: "Deleting %{deletable} \"%{name}\" from Chef server..."
|
||||||
|
|
||||||
file:
|
file:
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
require_relative "../../../../base"
|
||||||
|
|
||||||
|
require Vagrant.source_root.join("plugins/provisioners/chef/config/chef_apply")
|
||||||
|
|
||||||
|
describe VagrantPlugins::Chef::Config::ChefApply do
|
||||||
|
include_context "unit"
|
||||||
|
|
||||||
|
subject { described_class.new }
|
||||||
|
|
||||||
|
let(:machine) { double("machine") }
|
||||||
|
|
||||||
|
def chef_error(key, options = {})
|
||||||
|
I18n.t("vagrant.provisioners.chef.#{key}", options)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#recipe" do
|
||||||
|
it "defaults to nil" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.recipe).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 "#upload_path" do
|
||||||
|
it "defaults to /tmp/vagrant-chef-apply.rb" do
|
||||||
|
subject.finalize!
|
||||||
|
expect(subject.upload_path).to match(%r{/tmp/vagrant-chef-apply-\d+})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#validate" do
|
||||||
|
before do
|
||||||
|
allow(machine).to receive(:env)
|
||||||
|
.and_return(double("env",
|
||||||
|
root_path: "",
|
||||||
|
))
|
||||||
|
|
||||||
|
subject.recipe = <<-EOH
|
||||||
|
package "foo"
|
||||||
|
EOH
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:result) { subject.validate(machine) }
|
||||||
|
let(:errors) { result["chef apply provisioner"] }
|
||||||
|
|
||||||
|
context "when the recipe is nil" do
|
||||||
|
it "returns an error" do
|
||||||
|
subject.recipe = nil
|
||||||
|
subject.finalize!
|
||||||
|
expect(errors).to include chef_error("recipe_empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the recipe is empty" do
|
||||||
|
it "returns an error" do
|
||||||
|
subject.recipe = " "
|
||||||
|
subject.finalize!
|
||||||
|
expect(errors).to include chef_error("recipe_empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the log_level is an empty array" do
|
||||||
|
it "returns an error" do
|
||||||
|
subject.log_level = " "
|
||||||
|
subject.finalize!
|
||||||
|
expect(errors).to include chef_error("log_level_empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the upload_path is nil" do
|
||||||
|
it "returns an error" do
|
||||||
|
subject.upload_path = nil
|
||||||
|
subject.finalize!
|
||||||
|
expect(errors).to include chef_error("upload_path_empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the upload_path is an empty array" do
|
||||||
|
it "returns an error" do
|
||||||
|
subject.upload_path = " "
|
||||||
|
subject.finalize!
|
||||||
|
expect(errors).to include chef_error("upload_path_empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue