Automatically install Chef when provisioning with Chef

This commit is contained in:
Seth Vargo 2014-10-31 16:06:05 -04:00
parent b7c03ddbe2
commit f232dc38c9
15 changed files with 287 additions and 18 deletions

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

@ -30,6 +30,10 @@ module VagrantPlugins
# @return [String, Symbol]
attr_accessor :log_level
# Install a prerelease version of Chef.
# @return [true, false]
attr_accessor :prerelease
# The version of Chef to install. If Chef is already installed on the
# system, the installed version is compared with the requested version.
# If they match, no action is taken. If they do not match, version of
@ -51,6 +55,7 @@ module VagrantPlugins
@binary_env = UNSET_VALUE
@install = UNSET_VALUE
@log_level = UNSET_VALUE
@prerelease = UNSET_VALUE
@version = UNSET_VALUE
end
@ -59,8 +64,14 @@ module VagrantPlugins
@binary_env = nil if @binary_env == UNSET_VALUE
@install = true if @install == UNSET_VALUE
@log_level = :info if @log_level == UNSET_VALUE
@prerelease = false if @prerelease == UNSET_VALUE
@version = :latest if @version == UNSET_VALUE
# Make sure the install is a symbol if it's not a boolean
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

View File

@ -1,3 +1,5 @@
require_relative "base"
module VagrantPlugins
module Chef
module Config

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"
Provisioner::ChefZero
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

View File

@ -2,6 +2,8 @@ require 'tempfile'
require "vagrant/util/template_renderer"
require_relative "../installer"
module VagrantPlugins
module Chef
module Provisioner
@ -13,6 +15,24 @@ module VagrantPlugins
error_namespace("vagrant.provisioners.chef")
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)
# Checks for the existence of chef binary and error if it
# doesn't exist.
@ -20,7 +40,8 @@ module VagrantPlugins
"which #{binary}",
error_class: ChefError,
error_key: :chef_not_detected,
binary: binary)
binary: binary,
)
end
# This returns the command to run Chef for the given client

View File

@ -1,13 +1,18 @@
require "tempfile"
require_relative "base"
module VagrantPlugins
module Chef
module Provisioner
class ChefApply < Vagrant.plugin("2", :provisioner)
class ChefApply < Base
def provision
install_chef
verify_binary(chef_binary_path("chef-apply"))
command = "chef-apply"
command << " --log-level #{config.log_level}"
command << " #{config.upload_path}"
command << " \"#{target_recipe_path}\""
command << " --log_level #{config.log_level}"
user = @machine.ssh_info[:username]
@ -18,7 +23,7 @@ module VagrantPlugins
# Upload the 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)
)
@ -34,6 +39,12 @@ module VagrantPlugins
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
# machine.
def upload_recipe
@ -43,8 +54,7 @@ module VagrantPlugins
file.rewind
# Upload the tempfile to the guest
destination = File.join(config.upload_path, "recipe.rb")
@machine.communicate.upload(file.path, destination)
@machine.communicate.upload(file.path, target_recipe_path)
ensure
# Delete our template
file.close

View File

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

View File

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

View File

@ -87,6 +87,14 @@ en:
CFEngine running in "single run" mode. Will execute one file.
cfengine_single_run_execute: |-
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: |-
Cleaning up the '%{deletable}' for Chef failed. The stdout and
stderr are shown below. Vagrant will continue destroying the machine,
@ -1754,6 +1762,16 @@ en:
"The cookbook path '%{path}' doesn't exist. Ignoring..."
json: "Generating chef JSON and uploading..."
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: |-
The Chef provisioner requires a log level. If you did not set a
log level, this is probably a bug and should be reported.
@ -1765,7 +1783,7 @@ en:
guest.
running_client: "Running chef-client..."
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_again: "Running chef-solo again (failed to converge)..."
missing_shared_folders: |-

View File

@ -28,6 +28,12 @@ describe VagrantPlugins::Chef::Config::Base do
subject.finalize!
expect(subject.install).to be(true)
end
it "is converted to a symbol" do
subject.install = "force"
subject.finalize!
expect(subject.install).to eq(:force)
end
end
describe "#log_level" do
@ -43,6 +49,13 @@ describe VagrantPlugins::Chef::Config::Base do
end
end
describe "#prerelease" do
it "defaults to true" do
subject.finalize!
expect(subject.prerelease).to be(false)
end
end
describe "#version" do
it "defaults to :latest" do
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