Merge pull request #4761 from mitchellh/sethvargo/chef_omnibus

Automatically install Chef when provisioning with Chef
This commit is contained in:
Seth Vargo 2014-11-10 11:59:27 -05:00
commit 1c06da04e6
23 changed files with 866 additions and 380 deletions

1
.gitignore vendored
View File

@ -48,3 +48,4 @@ website/docs/Rakefile
website/www/.sass-cache website/www/.sass-cache
website/www/build website/www/build
website/www/Rakefile website/www/Rakefile
exec/

View File

@ -32,6 +32,7 @@ IMPROVEMENTS:
more easily. Vagrant will login for you if you specify auth. [GH-4042] more easily. Vagrant will login for you if you specify auth. [GH-4042]
- providers/docker: `stop_timeout` can be used to modify the `docker stop` - providers/docker: `stop_timeout` can be used to modify the `docker stop`
timeout. [GH-4504] timeout. [GH-4504]
- provisioners/chef: Automatically install Chef when using a Chef provisioner.
- synced\_folders/nfs: Won't use `sudo` to write to /etc/exports if there - synced\_folders/nfs: Won't use `sudo` to write to /etc/exports if there
are write privileges. [GH-2643] are write privileges. [GH-2643]
- synced\_folders/smb: Credentials from one SMB will be copied to the rest. [GH-4675] - synced\_folders/smb: Credentials from one SMB will be copied to the rest. [GH-4675]

View File

@ -0,0 +1,19 @@
require_relative "../../omnibus"
module VagrantPlugins
module Chef
module Cap
module Debian
module ChefInstall
def self.chef_install(machine, version, prerelease)
machine.communicate.sudo("apt-get update -y -qq")
machine.communicate.sudo("apt-get install -y -qq curl")
command = Omnibus.build_command(version, prerelease)
machine.communicate.sudo(command)
end
end
end
end
end
end

View File

@ -0,0 +1,22 @@
module VagrantPlugins
module Chef
module Cap
module Linux
module ChefInstalled
# Check if Chef is installed at the given version.
# @return [true, false]
def self.chef_installed(machine, version)
knife = "/opt/chef/bin/knife"
command = "test -x #{knife}"
if version != :latest
command << "&& #{knife} --version | grep 'Chef: #{version}'"
end
machine.communicate.test(command, sudo: true)
end
end
end
end
end
end

View File

@ -0,0 +1,18 @@
require_relative "../../omnibus"
module VagrantPlugins
module Chef
module Cap
module Redhat
module ChefInstall
def self.chef_install(machine, version, prerelease)
machine.communicate.sudo("yum install -y -q curl")
command = Omnibus.build_command(version, prerelease)
machine.communicate.sudo(command)
end
end
end
end
end
end

View File

@ -6,134 +6,98 @@ module VagrantPlugins
class Base < Vagrant.plugin("2", :config) class Base < Vagrant.plugin("2", :config)
extend Vagrant::Util::Counter extend Vagrant::Util::Counter
attr_accessor :arguments # The path to Chef's bin/ directory.
attr_accessor :attempts # @return [String]
attr_accessor :binary_path attr_accessor :binary_path
# Arbitrary environment variables to set before running the Chef
# provisioner command.
# @return [String]
attr_accessor :binary_env attr_accessor :binary_env
attr_accessor :custom_config_path
attr_accessor :encrypted_data_bag_secret_key_path # Install Chef on the system if it does not exist. Default is true.
attr_accessor :environment # This is a trinary attribute (it can have three values):
attr_accessor :formatter #
attr_accessor :http_proxy # - true (bool) install Chef
attr_accessor :http_proxy_user # - false (bool) do not install Chef
attr_accessor :http_proxy_pass # - "force" (string) install Chef, even if it is already installed at
attr_accessor :https_proxy # the proper version
attr_accessor :https_proxy_user #
attr_accessor :https_proxy_pass # @return [true, false, String]
attr_accessor :json attr_accessor :install
# The Chef log level. See the Chef docs for acceptable values.
# @return [String, Symbol]
attr_accessor :log_level attr_accessor :log_level
attr_accessor :no_proxy
attr_accessor :node_name # Install a prerelease version of Chef.
attr_accessor :provisioning_path # @return [true, false]
attr_accessor :run_list attr_accessor :prerelease
attr_accessor :file_cache_path
attr_accessor :file_backup_path # The version of Chef to install. If Chef is already installed on the
attr_accessor :verbose_logging # system, the installed version is compared with the requested version.
# If they match, no action is taken. If they do not match, version of
# the value specified in this attribute will be installed over top of
# the existing version (a warning will be displayed).
#
# You can also specify "latest" (default), which will install the latest
# version of Chef on the system. In this case, Chef will use whatever
# version is on the system. To force the newest version of Chef to be
# installed on every provision, set the {#install} option to "force".
#
# @return [String]
attr_accessor :version
def initialize def initialize
super super
@arguments = UNSET_VALUE @binary_path = UNSET_VALUE
@attempts = UNSET_VALUE @binary_env = UNSET_VALUE
@binary_path = UNSET_VALUE @install = UNSET_VALUE
@binary_env = UNSET_VALUE @log_level = UNSET_VALUE
@custom_config_path = UNSET_VALUE @prerelease = UNSET_VALUE
@encrypted_data_bag_secret_key_path = UNSET_VALUE @version = UNSET_VALUE
@environment = UNSET_VALUE
@formatter = UNSET_VALUE
@http_proxy = UNSET_VALUE
@http_proxy_user = UNSET_VALUE
@http_proxy_pass = UNSET_VALUE
@https_proxy = UNSET_VALUE
@https_proxy_user = UNSET_VALUE
@https_proxy_pass = UNSET_VALUE
@log_level = UNSET_VALUE
@no_proxy = UNSET_VALUE
@node_name = UNSET_VALUE
@provisioning_path = UNSET_VALUE
@file_cache_path = UNSET_VALUE
@file_backup_path = UNSET_VALUE
@verbose_logging = UNSET_VALUE
@json = {}
@run_list = []
end
def encrypted_data_bag_secret=(value)
puts "DEPRECATION: Chef encrypted_data_bag_secret has no effect anymore."
puts "Remove this from your Vagrantfile since it'll be removed in the next"
puts "Vagrant version."
end end
def finalize! def finalize!
@arguments = nil if @arguments == UNSET_VALUE @binary_path = nil if @binary_path == UNSET_VALUE
@attempts = 1 if @attempts == UNSET_VALUE @binary_env = nil if @binary_env == UNSET_VALUE
@binary_path = nil if @binary_path == UNSET_VALUE @install = true if @install == UNSET_VALUE
@binary_env = nil if @binary_env == UNSET_VALUE @log_level = :info if @log_level == UNSET_VALUE
@custom_config_path = nil if @custom_config_path == UNSET_VALUE @prerelease = false if @prerelease == UNSET_VALUE
@environment = nil if @environment == UNSET_VALUE @version = :latest if @version == UNSET_VALUE
@formatter = nil if @formatter == UNSET_VALUE
@http_proxy = nil if @http_proxy == UNSET_VALUE
@http_proxy_user = nil if @http_proxy_user == UNSET_VALUE
@http_proxy_pass = nil if @http_proxy_pass == UNSET_VALUE
@https_proxy = nil if @https_proxy == UNSET_VALUE
@https_proxy_user = nil if @https_proxy_user == UNSET_VALUE
@https_proxy_pass = nil if @https_proxy_pass == UNSET_VALUE
@log_level = :info if @log_level == UNSET_VALUE
@no_proxy = nil if @no_proxy == UNSET_VALUE
@node_name = nil if @node_name == UNSET_VALUE
@provisioning_path = nil if @provisioning_path == UNSET_VALUE
@file_backup_path = "/var/chef/backup" if @file_backup_path == UNSET_VALUE
@file_cache_path = "/var/chef/cache" if @file_cache_path == UNSET_VALUE
@verbose_logging = false if @verbose_logging == UNSET_VALUE
if @encrypted_data_bag_secret_key_path == UNSET_VALUE # Make sure the install is a symbol if it's not a boolean
@encrypted_data_bag_secret_key_path = nil if @install.respond_to?(:to_sym)
@install = @install.to_sym
end
# Make sure the version is a symbol if it's not a boolean
if @version.respond_to?(:to_sym)
@version = @version.to_sym
end end
# Make sure the log level is a symbol # Make sure the log level is a symbol
@log_level = @log_level.to_sym @log_level = @log_level.to_sym
# Set the default provisioning path to be a unique path in /tmp
if !@provisioning_path
counter = self.class.get_and_update_counter(:chef_config)
@provisioning_path = "/tmp/vagrant-chef-#{counter}"
end
end end
def merge(other) # Like validate, but returns a list of errors to append.
super.tap do |result| #
result.instance_variable_set(:@json, @json.merge(other.json)) # @return [Array<String>]
result.instance_variable_set(:@run_list, (@run_list + other.run_list))
end
end
# Just like the normal configuration "validate" method except that
# it returns an array of errors that should be merged into some
# other error accumulator.
def validate_base(machine) def validate_base(machine)
errors = _detected_errors errors = _detected_errors
if @custom_config_path if missing?(log_level)
expanded = File.expand_path(@custom_config_path, machine.env.root_path) errors << I18n.t("vagrant.provisioners.chef.log_level_empty")
if !File.file?(expanded)
errors << I18n.t("vagrant.config.chef.custom_config_path_missing")
end
end end
errors errors
end end
# Adds a recipe to the run list # Determine if the given string is "missing" (blank)
def add_recipe(name) # @return [true, false]
name = "recipe[#{name}]" unless name =~ /^recipe\[(.+?)\]$/ def missing?(obj)
run_list << name obj.to_s.strip.empty?
end
# Adds a role to the run list
def add_role(name)
name = "role[#{name}]" unless name =~ /^role\[(.+?)\]$/
run_list << name
end end
end end
end end

View File

@ -0,0 +1,137 @@
require "vagrant/util/counter"
require_relative "base"
module VagrantPlugins
module Chef
module Config
# This is the config base for Chef provisioners that need a full Chef
# Runner object, like chef-solo or chef-client. For provisioners like
# chef-apply, these options are not valid
class BaseRunner < Base
attr_accessor :arguments
attr_accessor :attempts
attr_accessor :custom_config_path
attr_accessor :encrypted_data_bag_secret_key_path
attr_accessor :environment
attr_accessor :formatter
attr_accessor :http_proxy
attr_accessor :http_proxy_user
attr_accessor :http_proxy_pass
attr_accessor :https_proxy
attr_accessor :https_proxy_user
attr_accessor :https_proxy_pass
attr_accessor :json
attr_accessor :no_proxy
attr_accessor :node_name
attr_accessor :provisioning_path
attr_accessor :run_list
attr_accessor :file_cache_path
attr_accessor :file_backup_path
attr_accessor :verbose_logging
def initialize
super
@arguments = UNSET_VALUE
@attempts = UNSET_VALUE
@custom_config_path = UNSET_VALUE
# /etc/chef/client.rb config options
@encrypted_data_bag_secret_key_path = UNSET_VALUE
@environment = UNSET_VALUE
@formatter = UNSET_VALUE
@http_proxy = UNSET_VALUE
@http_proxy_user = UNSET_VALUE
@http_proxy_pass = UNSET_VALUE
@https_proxy = UNSET_VALUE
@https_proxy_user = UNSET_VALUE
@https_proxy_pass = UNSET_VALUE
@no_proxy = UNSET_VALUE
@node_name = UNSET_VALUE
@provisioning_path = UNSET_VALUE
@file_cache_path = UNSET_VALUE
@file_backup_path = UNSET_VALUE
@verbose_logging = UNSET_VALUE
# Runner options
@json = {}
@run_list = []
end
def encrypted_data_bag_secret=(value)
puts "DEPRECATION: Chef encrypted_data_bag_secret has no effect anymore."
puts "Remove this from your Vagrantfile since it'll be removed in the next"
puts "Vagrant version."
end
def finalize!
super
@arguments = nil if @arguments == UNSET_VALUE
@attempts = 1 if @attempts == UNSET_VALUE
@custom_config_path = nil if @custom_config_path == UNSET_VALUE
@environment = nil if @environment == UNSET_VALUE
@formatter = nil if @formatter == UNSET_VALUE
@http_proxy = nil if @http_proxy == UNSET_VALUE
@http_proxy_user = nil if @http_proxy_user == UNSET_VALUE
@http_proxy_pass = nil if @http_proxy_pass == UNSET_VALUE
@https_proxy = nil if @https_proxy == UNSET_VALUE
@https_proxy_user = nil if @https_proxy_user == UNSET_VALUE
@https_proxy_pass = nil if @https_proxy_pass == UNSET_VALUE
@no_proxy = nil if @no_proxy == UNSET_VALUE
@node_name = nil if @node_name == UNSET_VALUE
@provisioning_path = nil if @provisioning_path == UNSET_VALUE
@file_backup_path = "/var/chef/backup" if @file_backup_path == UNSET_VALUE
@file_cache_path = "/var/chef/cache" if @file_cache_path == UNSET_VALUE
@verbose_logging = false if @verbose_logging == UNSET_VALUE
if @encrypted_data_bag_secret_key_path == UNSET_VALUE
@encrypted_data_bag_secret_key_path = nil
end
# Set the default provisioning path to be a unique path in /tmp
if !@provisioning_path
counter = self.class.get_and_update_counter(:chef_config)
@provisioning_path = "/tmp/vagrant-chef-#{counter}"
end
end
def merge(other)
super.tap do |result|
result.instance_variable_set(:@json, @json.merge(other.json))
result.instance_variable_set(:@run_list, (@run_list + other.run_list))
end
end
# Just like the normal configuration "validate" method except that
# it returns an array of errors that should be merged into some
# other error accumulator.
def validate_base(machine)
errors = super
if @custom_config_path
expanded = File.expand_path(@custom_config_path, machine.env.root_path)
if !File.file?(expanded)
errors << I18n.t("vagrant.config.chef.custom_config_path_missing")
end
end
errors
end
# Adds a recipe to the run list
def add_recipe(name)
name = "recipe[#{name}]" unless name =~ /^recipe\[(.+?)\]$/
run_list << name
end
# Adds a role to the run list
def add_role(name)
name = "role[#{name}]" unless name =~ /^role\[(.+?)\]$/
run_list << name
end
end
end
end
end

View File

@ -1,9 +1,9 @@
require_relative "base"
module VagrantPlugins module VagrantPlugins
module Chef module Chef
module Config module Config
class ChefApply < Vagrant.plugin("2", :config) class ChefApply < Base
extend Vagrant::Util::Counter
# The raw recipe text (as a string) to execute via chef-apply. # The raw recipe text (as a string) to execute via chef-apply.
# @return [String] # @return [String]
attr_accessor :recipe attr_accessor :recipe
@ -13,25 +13,17 @@ module VagrantPlugins
# @return [String] # @return [String]
attr_accessor :upload_path attr_accessor :upload_path
# The Chef log level.
# @return [String]
attr_accessor :log_level
def initialize def initialize
@recipe = UNSET_VALUE super
@log_level = UNSET_VALUE @recipe = UNSET_VALUE
@upload_path = UNSET_VALUE @upload_path = UNSET_VALUE
end end
def finalize! def finalize!
@recipe = nil if @recipe == UNSET_VALUE super
if @log_level == UNSET_VALUE @recipe = nil if @recipe == UNSET_VALUE
@log_level = :info
else
@log_level = @log_level.to_sym
end
if @upload_path == UNSET_VALUE if @upload_path == UNSET_VALUE
counter = self.class.get_and_update_counter(:chef_apply) counter = self.class.get_and_update_counter(:chef_apply)
@ -40,28 +32,18 @@ module VagrantPlugins
end end
def validate(machine) def validate(machine)
errors = _detected_errors errors = validate_base(machine)
if missing(recipe) if missing?(recipe)
errors << I18n.t("vagrant.provisioners.chef.recipe_empty") errors << I18n.t("vagrant.provisioners.chef.recipe_empty")
end end
if missing(log_level) if missing?(upload_path)
errors << I18n.t("vagrant.provisioners.chef.log_level_empty")
end
if missing(upload_path)
errors << I18n.t("vagrant.provisioners.chef.upload_path_empty") errors << I18n.t("vagrant.provisioners.chef.upload_path_empty")
end end
{ "chef apply provisioner" => errors } { "chef apply provisioner" => errors }
end 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
end end

View File

@ -1,16 +1,33 @@
require "vagrant/util/which" require "vagrant/util/which"
require_relative "base" require_relative "base_runner"
module VagrantPlugins module VagrantPlugins
module Chef module Chef
module Config module Config
class ChefClient < Base class ChefClient < BaseRunner
# The URL endpoint to the Chef Server.
# @return [String]
attr_accessor :chef_server_url attr_accessor :chef_server_url
# The path on disk to the Chef client key,
# @return [String]
attr_accessor :client_key_path attr_accessor :client_key_path
# Delete the client key when the VM is destroyed. Default is false.
# @return [true, false]
attr_accessor :delete_client attr_accessor :delete_client
# Delete the node when the VM is destroyed. Default is false.
# @return [true, false]
attr_accessor :delete_node attr_accessor :delete_node
# The path to the validation key on disk.
# @return [String]
attr_accessor :validation_key_path attr_accessor :validation_key_path
# The name of the validation client.
# @return [String]
attr_accessor :validation_client_name attr_accessor :validation_client_name
def initialize def initialize
@ -36,8 +53,7 @@ module VagrantPlugins
end end
def validate(machine) def validate(machine)
errors = _detected_errors errors = validate_base(machine)
errors.concat(validate_base(machine))
if chef_server_url.to_s.strip.empty? if chef_server_url.to_s.strip.empty?
errors << I18n.t("vagrant.config.chef.server_url_empty") errors << I18n.t("vagrant.config.chef.server_url_empty")

View File

@ -1,25 +1,60 @@
require_relative "base" require_relative "base_runner"
module VagrantPlugins module VagrantPlugins
module Chef module Chef
module Config module Config
class ChefSolo < Base class ChefSolo < BaseRunner
# The path on disk where Chef cookbooks are stored.
# Default is "cookbooks".
# @return [String]
attr_accessor :cookbooks_path attr_accessor :cookbooks_path
# The path where data bags are stored on disk.
# @return [String]
attr_accessor :data_bags_path attr_accessor :data_bags_path
# The path where environments are stored on disk.
# @return [String]
attr_accessor :environments_path attr_accessor :environments_path
# A URL download a remote recipe from. Note: you should use chef-apply
# instead.
#
# @deprecated
#
# @return [String]
attr_accessor :recipe_url attr_accessor :recipe_url
# The path where roles are stored on disk.
# @return [String]
attr_accessor :roles_path attr_accessor :roles_path
# The type of synced folders to use.
# @return [String]
attr_accessor :synced_folder_type attr_accessor :synced_folder_type
def initialize def initialize
super super
@cookbooks_path = UNSET_VALUE @cookbooks_path = UNSET_VALUE
@data_bags_path = UNSET_VALUE @data_bags_path = UNSET_VALUE
@environments_path = UNSET_VALUE @environments_path = UNSET_VALUE
@recipe_url = UNSET_VALUE @recipe_url = UNSET_VALUE
@roles_path = UNSET_VALUE @roles_path = UNSET_VALUE
@synced_folder_type = UNSET_VALUE @synced_folder_type = UNSET_VALUE
end
# @deprecated This is deprecated in Chef and will be removed in Chef 12.
def recipe_url=(value)
puts "DEPRECATION: The 'recipe_url' setting for the Chef Solo"
puts "provisioner is deprecated. This value will be removed in"
puts "Chef 12. It is recommended you use the Chef Apply provisioner"
puts "instead. The 'recipe_url' setting will be removed in the next"
puts "version of Vagrant."
if value
@recipe_url = value
end
end end
def nfs=(value) def nfs=(value)
@ -63,8 +98,7 @@ module VagrantPlugins
end end
def validate(machine) def validate(machine)
errors = _detected_errors errors = validate_base(machine)
errors.concat(validate_base(machine))
if [cookbooks_path].flatten.compact.empty? if [cookbooks_path].flatten.compact.empty?
errors << I18n.t("vagrant.config.chef.cookbooks_path_empty") errors << I18n.t("vagrant.config.chef.cookbooks_path_empty")

View File

@ -0,0 +1,45 @@
module VagrantPlugins
module Chef
class Installer
def initialize(machine, options = {})
@machine = machine
@version = options.fetch(:version, :latest)
@prerelease = options.fetch(:prerelease, :latest)
@force = options.fetch(:force, false)
end
# This handles verifying the Chef installation, installing it if it was
# requested, and so on. This method will raise exceptions if things are
# wrong.
def ensure_installed
# If the guest cannot check if Chef is installed, just exit printing a
# warning...
if !@machine.guest.capability?(:chef_installed)
@machine.ui.warn(I18n.t("vagrant.chef_cant_detect"))
return
end
if !should_install_chef?
@machine.ui.info(I18n.t("vagrant.chef_already_installed",
version: @version.to_s))
return
end
@machine.ui.detail(I18n.t("vagrant.chef_installing",
version: @version.to_s))
@machine.guest.capability(:chef_install, @version, @prerelease)
if !@machine.guest.capability(:chef_installed, @version)
raise Provisioner::Base::ChefError, :install_failed
end
end
# Determine if Chef should be installed. Chef is installed if the "force"
# option is given or if the guest does not have Chef installed at the
# proper version.
def should_install_chef?
@force || !@machine.guest.capability(:chef_installed, @version)
end
end
end
end

View File

@ -0,0 +1,28 @@
module VagrantPlugins
module Chef
module Omnibus
OMNITRUCK = "https://www.getchef.com/chef/install.sh".freeze
# Read more about the Omnibus installer here:
# https://docs.getchef.com/install_omnibus.html
def build_command(version, prerelease = false)
command = "curl -sL #{OMNITRUCK} | sudo bash"
if prerelease || version != :latest
command << " -s --"
end
if prerelease
command << " -p"
end
if version != :latest
command << " -v \"#{version}\""
end
command
end
module_function :build_command
end
end
end

View File

@ -52,6 +52,21 @@ module VagrantPlugins
require_relative "provisioner/chef_zero" require_relative "provisioner/chef_zero"
Provisioner::ChefZero Provisioner::ChefZero
end end
guest_capability(:linux, :chef_installed) do
require_relative "cap/linux/chef_installed"
Cap::Linux::ChefInstalled
end
guest_capability(:debian, :chef_install) do
require_relative "cap/debian/chef_install"
Cap::Debian::ChefInstall
end
guest_capability(:redhat, :chef_install) do
require_relative "cap/redhat/chef_install"
Cap::Redhat::ChefInstall
end
end end
end end
end end

View File

@ -2,6 +2,8 @@ require 'tempfile'
require "vagrant/util/template_renderer" require "vagrant/util/template_renderer"
require_relative "../installer"
module VagrantPlugins module VagrantPlugins
module Chef module Chef
module Provisioner module Provisioner
@ -13,6 +15,24 @@ module VagrantPlugins
error_namespace("vagrant.provisioners.chef") error_namespace("vagrant.provisioners.chef")
end end
def initialize(machine, config)
super
@logger = Log4r::Logger.new("vagrant::provisioners::chef")
end
def install_chef
return if !config.install
@logger.info("Checking for Chef installation...")
installer = Installer.new(@machine,
force: config.install == :force,
version: config.version,
prerelease: config.prerelease,
)
installer.ensure_installed
end
def verify_binary(binary) def verify_binary(binary)
# Checks for the existence of chef binary and error if it # Checks for the existence of chef binary and error if it
# doesn't exist. # doesn't exist.
@ -20,7 +40,8 @@ module VagrantPlugins
"which #{binary}", "which #{binary}",
error_class: ChefError, error_class: ChefError,
error_key: :chef_not_detected, error_key: :chef_not_detected,
binary: binary) binary: binary,
)
end end
# This returns the command to run Chef for the given client # This returns the command to run Chef for the given client

View File

@ -1,13 +1,18 @@
require "tempfile" require "tempfile"
require_relative "base"
module VagrantPlugins module VagrantPlugins
module Chef module Chef
module Provisioner module Provisioner
class ChefApply < Vagrant.plugin("2", :provisioner) class ChefApply < Base
def provision def provision
install_chef
verify_binary(chef_binary_path("chef-apply"))
command = "chef-apply" command = "chef-apply"
command << " --log-level #{config.log_level}" command << " \"#{target_recipe_path}\""
command << " #{config.upload_path}" command << " --log_level #{config.log_level}"
user = @machine.ssh_info[:username] user = @machine.ssh_info[:username]
@ -18,7 +23,7 @@ module VagrantPlugins
# Upload the recipe # Upload the recipe
upload_recipe upload_recipe
@machine.ui.info(I18n.t("vagrant.provisioners.chef.running_chef_apply", @machine.ui.info(I18n.t("vagrant.provisioners.chef.running_apply",
script: config.path) script: config.path)
) )
@ -34,6 +39,12 @@ module VagrantPlugins
end end
end end
# The destination (on the guest) where the recipe will live
# @return [String]
def target_recipe_path
File.join(config.upload_path, "recipe.rb")
end
# Write the raw recipe contents to a tempfile and upload that to the # Write the raw recipe contents to a tempfile and upload that to the
# machine. # machine.
def upload_recipe def upload_recipe
@ -43,8 +54,7 @@ module VagrantPlugins
file.rewind file.rewind
# Upload the tempfile to the guest # Upload the tempfile to the guest
destination = File.join(config.upload_path, "recipe.rb") @machine.communicate.upload(file.path, target_recipe_path)
@machine.communicate.upload(file.path, destination)
ensure ensure
# Delete our template # Delete our template
file.close file.close

View File

@ -18,6 +18,7 @@ module VagrantPlugins
end end
def provision def provision
install_chef
verify_binary(chef_binary_path("chef-client")) verify_binary(chef_binary_path("chef-client"))
chown_provisioning_folder chown_provisioning_folder
create_client_key_folder create_client_key_folder

View File

@ -35,6 +35,7 @@ module VagrantPlugins
end end
def provision def provision
install_chef
# Verify that the proper shared folders exist. # Verify that the proper shared folders exist.
check = [] check = []
@shared_folders.each do |type, local_path, remote_path| @shared_folders.each do |type, local_path, remote_path|

View File

@ -89,14 +89,42 @@ Vagrant.configure(2) do |config|
# puppet.manifest_file = "default.pp" # puppet.manifest_file = "default.pp"
# end # end
# Enable provisioning with chef solo, specifying a cookbooks path, roles # Enable provisioning with Chef Solo, specifying a cookbooks path, roles
# path, and data_bags path (all relative to this Vagrantfile), and adding # path, and data_bags path (all relative to this Vagrantfile), and adding
# some recipes and/or roles. # some recipes and/or roles.
# #
# config.vm.provision "chef_solo" do |chef| # config.vm.provision "chef_solo" do |chef|
# chef.cookbooks_path = "../my-recipes/cookbooks" # chef.cookbooks_path = "~/chef/cookbooks"
# chef.roles_path = "../my-recipes/roles" # chef.roles_path = "~/chef/roles"
# chef.data_bags_path = "../my-recipes/data_bags" # chef.data_bags_path = "~/chef/data_bags"
#
# chef.add_recipe "mysql"
# chef.add_role "web"
#
# chef.json = { mysql_password: "foo" }
# end
#
# Chef Solo will automatically install the latest version of Chef for you.
# This can be configured in the provisioner block:
#
# config.vm.provision "chef_solo" do |chef|
# chef.version = "11.16.4"
# end
#
# Alternative you can disable the installation of Chef entirely:
#
# config.vm.provision "chef_solo" do |chef|
# chef.install = false
# end
# Enable provisioning with Chef Zero. The Chef Zero provisioner accepts the
# exact same parameter as the Chef Solo provisioner:
#
# config.vm.provision "chef_zero" do |chef|
# chef.cookbooks_path = "~/chef/cookbooks"
# chef.roles_path = "~/chef/roles"
# chef.data_bags_path = "~/chef/data_bags"
#
# chef.add_recipe "mysql" # chef.add_recipe "mysql"
# chef.add_role "web" # chef.add_role "web"
# #
@ -104,10 +132,10 @@ Vagrant.configure(2) do |config|
# chef.json = { mysql_password: "foo" } # chef.json = { mysql_password: "foo" }
# end # end
# Enable provisioning with chef server, specifying the chef server URL, # Enable provisioning with Chef Server, specifying the chef server URL,
# and the path to the validation key (relative to this Vagrantfile). # and the path to the validation key (relative to this Vagrantfile).
# #
# The Opscode Platform uses HTTPS. Substitute your organization for # The Hosted Chef platform uses HTTPS. Substitute your organization for
# ORGNAME in the URL and validation key. # ORGNAME in the URL and validation key.
# #
# If you have your own Chef Server, use the appropriate URL, which may be # If you have your own Chef Server, use the appropriate URL, which may be
@ -115,15 +143,44 @@ Vagrant.configure(2) do |config|
# validation key to validation.pem. # validation key to validation.pem.
# #
# config.vm.provision "chef_client" do |chef| # config.vm.provision "chef_client" do |chef|
# chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME" # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
# chef.validation_key_path = "ORGNAME-validator.pem" # chef.validation_key_path = "ORGNAME-validator.pem"
# end # end
# #
# If you're using the Opscode platform, your validator client is # If you're using the Hosted Chef platform, your validator client is
# ORGNAME-validator, replacing ORGNAME with your organization name. # ORGNAME-validator, replacing ORGNAME with your organization name.
# #
# If you have your own Chef Server, the default validation client name is # If you have your own Chef Server, the default validation client name is
# chef-validator, unless you changed the configuration. # chef-validator, unless you changed the configuration.
# #
# chef.validation_client_name = "ORGNAME-validator" # chef.validation_client_name = "ORGNAME-validator"
#
# Chef Client will automatically install the latest version of Chef for you.
# This can be configured in the provisioner block:
#
# config.vm.provision "chef_client" do |chef|
# chef.version = "11.16.4"
# end
#
# Alternative you can disable the installation of Chef entirely:
#
# config.vm.provision "chef_client" do |chef|
# chef.install = false
# end
# Enable provisioning with Chef Apply, specifying an inline recipe to execute
# on the target system.
#
# config.vm.provision "chef_apply" do |chef|
# chef.recipe = <<-RECIPE
# package "curl"
# RECIPE
# end
#
# Chef Apply will automatically install the latest version of Chef for you.
# This can be configured in the provisioner block:
#
# config.vm.provision "chef_apply" do |chef|
# chef.version = "11.16.4"
# end
end end

View File

@ -87,6 +87,14 @@ en:
CFEngine running in "single run" mode. Will execute one file. CFEngine running in "single run" mode. Will execute one file.
cfengine_single_run_execute: |- cfengine_single_run_execute: |-
Executing run file for CFEngine... Executing run file for CFEngine...
chef_cant_detect: |-
Vagrant does not support detecting whether Chef is installed
for the guest OS running in the machine. Vagrant will assume it is
installed and attempt to continue.
chef_already_installed: |-
Detected Chef (%{version}) is already installed
chef_installing: |-
Installing Chef (%{version})...
chef_client_cleanup_failed: |- chef_client_cleanup_failed: |-
Cleaning up the '%{deletable}' for Chef failed. The stdout and Cleaning up the '%{deletable}' for Chef failed. The stdout and
stderr are shown below. Vagrant will continue destroying the machine, stderr are shown below. Vagrant will continue destroying the machine,
@ -1754,6 +1762,16 @@ 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..."
install_failed: |-
Vagrant could not detect Chef on the guest! Even after Vagrant
attempted to install Chef, it could still not find Chef on the system.
Please make sure you are connected to the Internet and can access
Chef's package distribution servers. If you already have Chef
installed on this guest, you can disable the automatic Chef detection
by setting the 'install' option in the Chef configuration section of
your Vagrantfile:
chef.install = false
log_level_empty: |- log_level_empty: |-
The Chef provisioner requires a log level. If you did not set a The Chef provisioner requires a log level. If you did not set a
log level, this is probably a bug and should be reported. log level, this is probably a bug and should be reported.
@ -1765,7 +1783,7 @@ en:
guest. 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_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: |-

View File

@ -0,0 +1,255 @@
require_relative "../../../../base"
require Vagrant.source_root.join("plugins/provisioners/chef/config/base_runner")
describe VagrantPlugins::Chef::Config::BaseRunner 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 "#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 "#encrypted_data_bag_secret_key_path" do
it "defaults to nil" do
subject.finalize!
expect(subject.encrypted_data_bag_secret_key_path).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

@ -9,20 +9,6 @@ describe VagrantPlugins::Chef::Config::Base do
let(:machine) { double("machine") } 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 describe "#binary_path" do
it "defaults to nil" do it "defaults to nil" do
subject.finalize! subject.finalize!
@ -37,66 +23,16 @@ describe VagrantPlugins::Chef::Config::Base do
end end
end end
describe "#custom_config_path" do describe "#install" do
it "defaults to nil" do it "defaults to true" do
subject.finalize! subject.finalize!
expect(subject.custom_config_path).to be(nil) expect(subject.install).to be(true)
end end
end
describe "#environment" do it "is converted to a symbol" do
it "defaults to nil" do subject.install = "force"
subject.finalize! subject.finalize!
expect(subject.environment).to be(nil) expect(subject.install).to eq(:force)
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
end end
@ -113,150 +49,23 @@ describe VagrantPlugins::Chef::Config::Base do
end end
end end
describe "#no_proxy" do describe "#prerelease" do
it "defaults to nil" do it "defaults to true" do
subject.finalize! subject.finalize!
expect(subject.no_proxy).to be(nil) expect(subject.prerelease).to be(false)
end end
end end
describe "#node_name" do describe "#version" do
it "defaults to nil" do it "defaults to :latest" do
subject.finalize! subject.finalize!
expect(subject.node_name).to be(nil) expect(subject.version).to eq(:latest)
end end
end
describe "#provisioning_path" do it "converts the string 'latest' to a symbol" do
it "defaults to a tmp_path" do subject.version = "latest"
subject.finalize! subject.finalize!
expect(subject.provisioning_path).to match(%r{/tmp/vagrant-chef-\d+}) expect(subject.version).to eq(:latest)
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 end
end end

View File

@ -20,19 +20,6 @@ describe VagrantPlugins::Chef::Config::ChefApply do
end end
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 describe "#upload_path" do
it "defaults to /tmp/vagrant-chef-apply.rb" do it "defaults to /tmp/vagrant-chef-apply.rb" do
subject.finalize! subject.finalize!

View File

@ -0,0 +1,45 @@
require_relative "../../../base"
require Vagrant.source_root.join("plugins/provisioners/chef/omnibus")
describe VagrantPlugins::Chef::Omnibus, :focus do
let(:prefix) { "curl -sL #{described_class.const_get(:OMNITRUCK)}" }
let(:version) { :latest }
let(:prerelease) { false }
let(:build_command) { described_class.build_command(version, prerelease) }
context "when prerelease is given" do
let(:prerelease) { true }
it "returns the correct command" do
expect(build_command).to eq("#{prefix} | sudo bash -s -- -p")
end
end
context "when version is :latest" do
let(:version) { :latest }
it "returns the correct command" do
expect(build_command).to eq("#{prefix} | sudo bash")
end
end
context "when version is a string" do
let(:version) { "1.2.3" }
it "returns the correct command" do
expect(build_command).to eq("#{prefix} | sudo bash -s -- -v \"1.2.3\"")
end
end
context "when prerelease and version are given" do
let(:version) { "1.2.3" }
let(:prerelease) { true }
it "returns the correct command" do
expect(build_command).to eq("#{prefix} | sudo bash -s -- -p -v \"1.2.3\"")
end
end
end