require "digest/md5" require "securerandom" require "set" require "log4r" require "vagrant/util/counter" require_relative "chef_solo" module VagrantPlugins module Chef module Provisioner # This class implements provisioning via chef-zero. class ChefZero < ChefSolo def initialize(machine, config) super @logger = Log4r::Logger.new("vagrant::provisioners::chef_zero") end def provision install_chef # Verify that the proper shared folders exist. check = [] @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 verify_shared_folders(check) verify_binary(chef_binary_path("chef-client")) upload_encrypted_data_bag_secret setup_json setup_zero_config run_chef_zero delete_encrypted_data_bag_secret end def setup_zero_config setup_config("provisioners/chef_zero/zero", "client.rb", { local_mode: true, enable_reporting: false, cookbooks_path: guest_paths(@cookbook_folders), nodes_path: guest_paths(@node_folders), roles_path: guest_paths(@role_folders), data_bags_path: guest_paths(@data_bags_folders), environments_path: guest_paths(@environments_folders).first, }) end def run_chef_zero if @config.run_list && @config.run_list.empty? @machine.ui.warn(I18n.t("vagrant.chef_run_list_empty")) end command = CommandBuilder.command(:client, @config, windows: windows?, colored: @machine.env.ui.color?, local_mode: true, ) still_active = 259 #provisioner has asked chef to reboot @config.attempts.times do |attempt| exit_status = 0 while exit_status == 0 || exit_status == still_active if @machine.guest.capability?(:wait_for_reboot) @machine.guest.capability(:wait_for_reboot) elsif attempt > 0 sleep 10 @machine.communicate.wait_for_ready(@machine.config.vm.boot_timeout) end if attempt == 0 @machine.ui.info I18n.t("vagrant.provisioners.chef.running_zero") else @machine.ui.info I18n.t("vagrant.provisioners.chef.running_zero_again") end opts = { error_check: false, elevated: true } exit_status = @machine.communicate.sudo(command, opts) do |type, data| # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red data = data.chomp next if data.empty? @machine.ui.info(data, color: color) end # There is no need to run Chef again if it converges return if exit_status == 0 end 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}") if !@machine.communicate.test("test -d #{folder}", sudo: true) raise ChefError, :missing_shared_folders end end 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