2012-04-19 04:53:19 +00:00
|
|
|
require "log4r"
|
|
|
|
|
2013-01-14 00:41:32 +00:00
|
|
|
require "vagrant/util/counter"
|
|
|
|
|
2012-04-19 04:53:19 +00:00
|
|
|
require File.expand_path("../base", __FILE__)
|
|
|
|
|
|
|
|
module VagrantPlugins
|
|
|
|
module Chef
|
|
|
|
module Provisioner
|
|
|
|
# This class implements provisioning via chef-solo.
|
|
|
|
class ChefSolo < Base
|
|
|
|
extend Vagrant::Util::Counter
|
|
|
|
include Vagrant::Util::Counter
|
|
|
|
|
|
|
|
attr_reader :cookbook_folders
|
|
|
|
attr_reader :role_folders
|
|
|
|
attr_reader :data_bags_folders
|
|
|
|
|
2013-01-14 00:41:32 +00:00
|
|
|
def initialize(machine, config)
|
2012-04-19 04:53:19 +00:00
|
|
|
super
|
|
|
|
@logger = Log4r::Logger.new("vagrant::provisioners::chef_solo")
|
|
|
|
end
|
|
|
|
|
2013-01-14 00:41:32 +00:00
|
|
|
def configure(root_config)
|
|
|
|
@cookbook_folders = expanded_folders(@config.cookbooks_path, "cookbooks")
|
|
|
|
@role_folders = expanded_folders(@config.roles_path, "roles")
|
|
|
|
@data_bags_folders = expanded_folders(@config.data_bags_path, "data_bags")
|
2012-04-19 04:53:19 +00:00
|
|
|
|
2013-01-14 00:41:32 +00:00
|
|
|
share_folders(root_config, "csc", @cookbook_folders)
|
|
|
|
share_folders(root_config, "csr", @role_folders)
|
|
|
|
share_folders(root_config, "csdb", @data_bags_folders)
|
2012-04-19 04:53:19 +00:00
|
|
|
end
|
|
|
|
|
2013-01-14 00:41:32 +00:00
|
|
|
def provision
|
2012-04-19 04:53:19 +00:00
|
|
|
# Verify that the proper shared folders exist.
|
|
|
|
check = []
|
|
|
|
[@cookbook_folders, @role_folders, @data_bags_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
|
|
|
|
end
|
|
|
|
|
|
|
|
verify_shared_folders(check)
|
|
|
|
|
|
|
|
verify_binary(chef_binary_path("chef-solo"))
|
|
|
|
chown_provisioning_folder
|
2013-01-14 00:41:32 +00:00
|
|
|
upload_encrypted_data_bag_secret if @config.encrypted_data_bag_secret_key_path
|
2012-04-19 04:53:19 +00:00
|
|
|
setup_json
|
|
|
|
setup_solo_config
|
|
|
|
run_chef_solo
|
|
|
|
end
|
|
|
|
|
|
|
|
# Converts paths to a list of properly expanded paths with types.
|
|
|
|
def expanded_folders(paths, appended_folder=nil)
|
|
|
|
return [] if paths.nil?
|
|
|
|
|
|
|
|
# Convert the path to an array if it is a string or just a single
|
|
|
|
# path element which contains the folder location (:host or :vm)
|
|
|
|
paths = [paths] if paths.is_a?(String) || paths.first.is_a?(Symbol)
|
|
|
|
|
|
|
|
results = []
|
|
|
|
paths.each do |path|
|
|
|
|
path = [:host, path] if !path.is_a?(Array)
|
|
|
|
type, path = path
|
|
|
|
|
|
|
|
# Create the local/remote path based on whether this is a host
|
|
|
|
# or VM path.
|
|
|
|
local_path = nil
|
|
|
|
remote_path = nil
|
|
|
|
if type == :host
|
|
|
|
# Get the expanded path that the host path points to
|
2013-01-14 00:41:32 +00:00
|
|
|
local_path = File.expand_path(path, @machine.env.root_path)
|
2012-04-19 04:53:19 +00:00
|
|
|
|
|
|
|
# Super hacky but if we're expanded the default cookbook paths,
|
|
|
|
# and one of the host paths doesn't exist, then just ignore it,
|
|
|
|
# because that is fine.
|
2013-01-14 00:41:32 +00:00
|
|
|
if paths.equal?(@config._default_cookbook_path) && !File.directory?(local_path)
|
2012-04-19 04:53:19 +00:00
|
|
|
@logger.info("'cookbooks' folder doesn't exist on defaults. Ignoring.")
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
# Path exists on the host, setup the remote path
|
2013-01-14 00:41:32 +00:00
|
|
|
remote_path = "#{@config.provisioning_path}/chef-solo-#{get_and_update_counter(:cookbooks_path)}"
|
2012-04-19 04:53:19 +00:00
|
|
|
else
|
|
|
|
# Path already exists on the virtual machine. Expand it
|
|
|
|
# relative to where we're provisioning.
|
2013-01-14 00:41:32 +00:00
|
|
|
remote_path = File.expand_path(path, @config.provisioning_path)
|
2012-04-19 04:53:19 +00:00
|
|
|
|
|
|
|
# Remove drive letter if running on a windows host. This is a bit
|
|
|
|
# of a hack but is the most portable way I can think of at the moment
|
|
|
|
# to achieve this. Otherwise, Vagrant attempts to share at some crazy
|
|
|
|
# path like /home/vagrant/c:/foo/bar
|
|
|
|
remote_path = remote_path.gsub(/^[a-zA-Z]:/, "")
|
|
|
|
end
|
|
|
|
|
|
|
|
# If we have specified a folder name to append then append it
|
|
|
|
remote_path += "/#{appended_folder}" if appended_folder
|
|
|
|
|
|
|
|
# Append the result
|
|
|
|
results << [type, local_path, remote_path]
|
|
|
|
end
|
|
|
|
|
|
|
|
results
|
|
|
|
end
|
|
|
|
|
|
|
|
# Shares the given folders with the given prefix. The folders should
|
|
|
|
# be of the structure resulting from the `expanded_folders` function.
|
2013-01-14 00:41:32 +00:00
|
|
|
def share_folders(root_config, prefix, folders)
|
2012-04-19 04:53:19 +00:00
|
|
|
folders.each do |type, local_path, remote_path|
|
|
|
|
if type == :host
|
2013-01-14 00:41:32 +00:00
|
|
|
root_config.vm.share_folder(
|
2012-08-21 23:57:17 +00:00
|
|
|
"v-#{prefix}-#{self.class.get_and_update_counter(:shared_folder)}",
|
2013-01-14 00:41:32 +00:00
|
|
|
remote_path, local_path, :nfs => @config.nfs)
|
2012-04-19 04:53:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def upload_encrypted_data_bag_secret
|
2013-01-14 00:41:32 +00:00
|
|
|
@machine.env.ui.info I18n.t("vagrant.provisioners.chef.upload_encrypted_data_bag_secret_key")
|
|
|
|
@machine.communicate.upload(encrypted_data_bag_secret_key_path,
|
|
|
|
@config.encrypted_data_bag_secret)
|
2012-04-19 04:53:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def setup_solo_config
|
|
|
|
cookbooks_path = guest_paths(@cookbook_folders)
|
|
|
|
roles_path = guest_paths(@role_folders).first
|
|
|
|
data_bags_path = guest_paths(@data_bags_folders).first
|
|
|
|
|
|
|
|
setup_config("provisioners/chef_solo/solo", "solo.rb", {
|
2013-01-14 00:41:32 +00:00
|
|
|
:node_name => @config.node_name,
|
|
|
|
:provisioning_path => @config.provisioning_path,
|
2012-04-19 04:53:19 +00:00
|
|
|
:cookbooks_path => cookbooks_path,
|
2013-01-14 00:41:32 +00:00
|
|
|
:recipe_url => @config.recipe_url,
|
2012-04-19 04:53:19 +00:00
|
|
|
:roles_path => roles_path,
|
|
|
|
:data_bags_path => data_bags_path,
|
2013-01-14 00:41:32 +00:00
|
|
|
:encrypted_data_bag_secret => @config.encrypted_data_bag_secret,
|
2012-04-19 04:53:19 +00:00
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
def run_chef_solo
|
2013-01-14 00:41:32 +00:00
|
|
|
command_env = @config.binary_env ? "#{@config.binary_env} " : ""
|
|
|
|
command_args = @config.arguments ? " #{@config.arguments}" : ""
|
|
|
|
command = "#{command_env}#{chef_binary_path("chef-solo")} -c #{@config.provisioning_path}/solo.rb -j #{@config.provisioning_path}/dna.json #{command_args}"
|
2012-04-19 04:53:19 +00:00
|
|
|
|
2013-01-14 00:41:32 +00:00
|
|
|
@config.attempts.times do |attempt|
|
2012-04-19 04:53:19 +00:00
|
|
|
if attempt == 0
|
2013-01-14 00:41:32 +00:00
|
|
|
@machine.env.ui.info I18n.t("vagrant.provisioners.chef.running_solo")
|
2012-04-19 04:53:19 +00:00
|
|
|
else
|
2013-01-14 00:41:32 +00:00
|
|
|
@machine.env.ui.info I18n.t("vagrant.provisioners.chef.running_solo_again")
|
2012-04-19 04:53:19 +00:00
|
|
|
end
|
|
|
|
|
2013-01-14 00:41:32 +00:00
|
|
|
exit_status = @machine.communicate.sudo(command, :error_check => false) do |type, data|
|
2012-04-19 04:53:19 +00:00
|
|
|
# Output the data with the proper color based on the stream.
|
|
|
|
color = type == :stdout ? :green : :red
|
|
|
|
|
|
|
|
# Note: Be sure to chomp the data to avoid the newlines that the
|
|
|
|
# Chef outputs.
|
2013-01-14 00:41:32 +00:00
|
|
|
@machine.env.ui.info(data.chomp, :color => color, :prefix => false)
|
2012-04-19 04:53:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# There is no need to run Chef again if it converges
|
|
|
|
return if exit_status == 0
|
|
|
|
end
|
|
|
|
|
|
|
|
# If we reached this point then Chef never converged! Error.
|
|
|
|
raise ChefError, :no_convergence
|
|
|
|
end
|
|
|
|
|
|
|
|
def verify_shared_folders(folders)
|
|
|
|
folders.each do |folder|
|
|
|
|
@logger.debug("Checking for shared folder: #{folder}")
|
2013-01-14 00:41:32 +00:00
|
|
|
if !@machine.communicate.test("test -d #{folder}")
|
2012-04-19 04:53:19 +00:00
|
|
|
raise ChefError, :missing_shared_folders
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def encrypted_data_bag_secret_key_path
|
2013-01-14 00:41:32 +00:00
|
|
|
File.expand_path(@config.encrypted_data_bag_secret_key_path, @machine.env.root_path)
|
2012-04-19 04:53:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
# Extracts only the remote paths from a list of folders
|
|
|
|
def guest_paths(folders)
|
|
|
|
folders.map { |parts| parts[2] }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|