Merge pull request #1626 from raadad/salty-vagrant-provisioner
Salt provisioner.
This commit is contained in:
commit
93d5182230
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,114 @@
|
||||||
|
require "i18n"
|
||||||
|
require "vagrant"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Salt
|
||||||
|
class Config < Vagrant.plugin("2", :config)
|
||||||
|
|
||||||
|
## salty-vagrant options
|
||||||
|
attr_accessor :minion_config
|
||||||
|
attr_accessor :minion_key
|
||||||
|
attr_accessor :minion_pub
|
||||||
|
attr_accessor :master_config
|
||||||
|
attr_accessor :master_key
|
||||||
|
attr_accessor :master_pub
|
||||||
|
attr_accessor :run_highstate
|
||||||
|
attr_accessor :always_install
|
||||||
|
attr_accessor :accept_keys
|
||||||
|
attr_accessor :bootstrap_script
|
||||||
|
attr_accessor :verbose
|
||||||
|
attr_accessor :seed_master
|
||||||
|
attr_reader :pillar_data
|
||||||
|
|
||||||
|
## bootstrap options
|
||||||
|
attr_accessor :temp_config_dir
|
||||||
|
attr_accessor :install_type
|
||||||
|
attr_accessor :install_args
|
||||||
|
attr_accessor :install_master
|
||||||
|
attr_accessor :install_syndic
|
||||||
|
attr_accessor :no_minion
|
||||||
|
attr_accessor :bootstrap_options
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@minion_config = UNSET_VALUE
|
||||||
|
@minion_key = UNSET_VALUE
|
||||||
|
@minion_pub = UNSET_VALUE
|
||||||
|
@master_config = UNSET_VALUE
|
||||||
|
@master_key = UNSET_VALUE
|
||||||
|
@master_pub = UNSET_VALUE
|
||||||
|
@run_highstate = UNSET_VALUE
|
||||||
|
@always_install = UNSET_VALUE
|
||||||
|
@accept_keys = UNSET_VALUE
|
||||||
|
@bootstrap_script = UNSET_VALUE
|
||||||
|
@verbose = UNSET_VALUE
|
||||||
|
@seed_master = UNSET_VALUE
|
||||||
|
@pillar_data = UNSET_VALUE
|
||||||
|
@temp_config_dir = UNSET_VALUE
|
||||||
|
@install_type = UNSET_VALUE
|
||||||
|
@install_args = UNSET_VALUE
|
||||||
|
@install_master = UNSET_VALUE
|
||||||
|
@install_syndic = UNSET_VALUE
|
||||||
|
@no_minion = UNSET_VALUE
|
||||||
|
@bootstrap_options = UNSET_VALUE
|
||||||
|
end
|
||||||
|
|
||||||
|
def finalize!
|
||||||
|
@minion_config = nil if @minion_config == UNSET_VALUE
|
||||||
|
@minion_key = nil if @minion_key == UNSET_VALUE
|
||||||
|
@minion_pub = nil if @minion_pub == UNSET_VALUE
|
||||||
|
@master_config = nil if @master_config == UNSET_VALUE
|
||||||
|
@master_key = nil if @master_key == UNSET_VALUE
|
||||||
|
@master_pub = nil if @master_pub == UNSET_VALUE
|
||||||
|
@run_highstate = nil if @run_highstate == UNSET_VALUE
|
||||||
|
@always_install = nil if @always_install == UNSET_VALUE
|
||||||
|
@accept_keys = nil if @accept_keys == UNSET_VALUE
|
||||||
|
@bootstrap_script = nil if @bootstrap_script == UNSET_VALUE
|
||||||
|
@verbose = nil if @verbose == UNSET_VALUE
|
||||||
|
@seed_master = nil if @seed_master == UNSET_VALUE
|
||||||
|
@pillar_data = {} if @pillar_data == UNSET_VALUE
|
||||||
|
@temp_config_dir = nil if @temp_config_dir == UNSET_VALUE
|
||||||
|
@install_type = nil if @install_type == UNSET_VALUE
|
||||||
|
@install_args = nil if @install_args == UNSET_VALUE
|
||||||
|
@install_master = nil if @install_master == UNSET_VALUE
|
||||||
|
@install_syndic = nil if @install_syndic == UNSET_VALUE
|
||||||
|
@no_minion = nil if @no_minion == UNSET_VALUE
|
||||||
|
@bootstrap_options = nil if @bootstrap_options == UNSET_VALUE
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def pillar(data)
|
||||||
|
@pillar_data = {} if @pillar_data == UNSET_VALUE
|
||||||
|
@pillar_data.deep_merge!(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate(machine)
|
||||||
|
errors = []
|
||||||
|
if @minion_key || @minion_pub
|
||||||
|
if !@minion_key || !@minion_pub
|
||||||
|
errors << @minion_pub
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @master_key && @master_pub
|
||||||
|
if !@minion_key && !@minion_pub
|
||||||
|
errors << I18n.t("salt.missing_key")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @accept_keys && @no_minion
|
||||||
|
errors << I18n.t("salt.accept_key_no_minion")
|
||||||
|
elsif @accept_keys && !@install_master
|
||||||
|
errors << I18n.t("salt.accept_key_no_master")
|
||||||
|
end
|
||||||
|
|
||||||
|
if @install_master && !@no_minion && !@accept_keys && !@seed_master && @run_highstate
|
||||||
|
errors << I18n.t("salt.must_accept_keys")
|
||||||
|
end
|
||||||
|
|
||||||
|
return {"salt" => errors}
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,11 @@
|
||||||
|
require "vagrant"
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Salt
|
||||||
|
module Errors
|
||||||
|
class SaltError < Vagrant::Errors::VagrantError
|
||||||
|
error_namespace("salt")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,31 @@
|
||||||
|
begin
|
||||||
|
require "vagrant"
|
||||||
|
rescue LoadError
|
||||||
|
raise "The Vagrant Salt plugin must be run within Vagrant."
|
||||||
|
end
|
||||||
|
|
||||||
|
if Vagrant::VERSION < "1.1.0"
|
||||||
|
raise "Please install vagrant-salt gem <=0.3.4 for Vagrant < 1.1.0"
|
||||||
|
end
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Salt
|
||||||
|
class Plugin < Vagrant.plugin("2")
|
||||||
|
name "salt"
|
||||||
|
description <<-DESC
|
||||||
|
Provisions virtual machines using SaltStack
|
||||||
|
DESC
|
||||||
|
|
||||||
|
config(:salt, :provisioner) do
|
||||||
|
require File.expand_path("../config", __FILE__)
|
||||||
|
Config
|
||||||
|
end
|
||||||
|
|
||||||
|
provisioner(:salt) do
|
||||||
|
require File.expand_path("../provisioner", __FILE__)
|
||||||
|
Provisioner
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,322 @@
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
module VagrantPlugins
|
||||||
|
module Salt
|
||||||
|
class Provisioner < Vagrant.plugin("2", :provisioner)
|
||||||
|
def provision
|
||||||
|
upload_configs
|
||||||
|
upload_keys
|
||||||
|
run_bootstrap_script
|
||||||
|
|
||||||
|
# if @config.seed_master and @config.install_master
|
||||||
|
# seed_master
|
||||||
|
# end
|
||||||
|
|
||||||
|
if @config.accept_keys
|
||||||
|
@machine.env.ui.warn "ATTENTION: 'salt.accept_keys' is deprecated. Please use salt.seed_master to upload your minion keys"
|
||||||
|
accept_keys
|
||||||
|
end
|
||||||
|
|
||||||
|
call_highstate
|
||||||
|
end
|
||||||
|
|
||||||
|
def seed_master
|
||||||
|
@machine.env.ui.info 'Uploading %d keys to /etc/salt/pki/master/minions/' % config.seed_master.length
|
||||||
|
staged_keys = keys('minions_pre')
|
||||||
|
@config.seed_master.each do |name, keyfile|
|
||||||
|
if staged_keys.include? name
|
||||||
|
@machine.env.ui.warn "Accepting staged key: %s" %name
|
||||||
|
@machine.communicate.sudo("salt-key -a %s" %name)
|
||||||
|
next
|
||||||
|
end
|
||||||
|
sourcepath = expanded_path(keyfile).to_s
|
||||||
|
dest = '/tmp/seed-%s.pub' %name
|
||||||
|
|
||||||
|
@machine.communicate.upload(sourcepath, dest)
|
||||||
|
@machine.communicate.sudo("mv /tmp/seed-%s.pub /etc/salt/pki/master/minions/%s" %[name, name])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return a list of accepted keys
|
||||||
|
def keys(group='minions')
|
||||||
|
out = @machine.communicate.sudo("salt-key --out json") do |type, output|
|
||||||
|
begin
|
||||||
|
if type == :stdout
|
||||||
|
out = JSON::load(output)
|
||||||
|
break out[group]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
## Utilities
|
||||||
|
def expanded_path(rel_path)
|
||||||
|
Pathname.new(rel_path).expand_path(@machine.env.root_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def binaries_found
|
||||||
|
## Determine States, ie: install vs configure
|
||||||
|
desired_binaries = []
|
||||||
|
if !@config.no_minion
|
||||||
|
desired_binaries.push('salt-minion')
|
||||||
|
desired_binaries.push('salt-call')
|
||||||
|
end
|
||||||
|
|
||||||
|
if @config.install_master
|
||||||
|
desired_binaries.push('salt-master')
|
||||||
|
end
|
||||||
|
|
||||||
|
if @config.install_syndic
|
||||||
|
desired_binaries.push('salt-syndic')
|
||||||
|
end
|
||||||
|
|
||||||
|
found = true
|
||||||
|
for binary in desired_binaries
|
||||||
|
@machine.env.ui.info "Checking if %s is installed" % binary
|
||||||
|
if !@machine.communicate.test("which %s" % binary)
|
||||||
|
@machine.env.ui.info "%s was not found." % binary
|
||||||
|
found = false
|
||||||
|
else
|
||||||
|
@machine.env.ui.info "%s found" % binary
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return found
|
||||||
|
end
|
||||||
|
|
||||||
|
def need_configure
|
||||||
|
@config.minion_config or @config.minion_key
|
||||||
|
end
|
||||||
|
|
||||||
|
def need_install
|
||||||
|
if @config.always_install
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return !binaries_found()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def temp_config_dir
|
||||||
|
return @config.temp_config_dir || "/tmp"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generates option string for bootstrap script
|
||||||
|
def bootstrap_options(install, configure, config_dir)
|
||||||
|
options = ""
|
||||||
|
|
||||||
|
## Any extra options passed to bootstrap
|
||||||
|
if @config.bootstrap_options
|
||||||
|
options = "%s %s" % [options, @config.bootstrap_options]
|
||||||
|
end
|
||||||
|
|
||||||
|
if configure
|
||||||
|
options = "%s -c %s" % [options, config_dir]
|
||||||
|
end
|
||||||
|
|
||||||
|
if @config.seed_master and @config.install_master
|
||||||
|
seed_dir = "/tmp/minion-seed-keys"
|
||||||
|
@machine.communicate.sudo("mkdir -p -m777 #{seed_dir}")
|
||||||
|
@config.seed_master.each do |name, keyfile|
|
||||||
|
sourcepath = expanded_path(keyfile).to_s
|
||||||
|
dest = "#{seed_dir}/seed-#{name}.pub"
|
||||||
|
@machine.communicate.upload(sourcepath, dest)
|
||||||
|
end
|
||||||
|
options = "#{options} -k #{seed_dir}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if configure and !install
|
||||||
|
options = "%s -C" % options
|
||||||
|
else
|
||||||
|
|
||||||
|
if @config.install_master
|
||||||
|
options = "%s -M" % options
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if @config.install_syndic
|
||||||
|
options = "%s -S" % options
|
||||||
|
end
|
||||||
|
|
||||||
|
if @config.no_minion
|
||||||
|
options = "%s -N" % options
|
||||||
|
end
|
||||||
|
|
||||||
|
if @config.install_type
|
||||||
|
options = "%s %s" % [options, @config.install_type]
|
||||||
|
end
|
||||||
|
|
||||||
|
if @config.install_args
|
||||||
|
options = "%s %s" % [options, @config.install_args]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if @config.verbose
|
||||||
|
@machine.env.ui.info "Using Bootstrap Options: %s" % options
|
||||||
|
end
|
||||||
|
|
||||||
|
return options
|
||||||
|
end
|
||||||
|
|
||||||
|
## Actions
|
||||||
|
# Get pillar string to pass with the salt command
|
||||||
|
def get_pillar
|
||||||
|
" pillar='#{@config.pillar_data.to_json}'" if !@config.pillar_data.empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Copy master and minion configs to VM
|
||||||
|
def upload_configs
|
||||||
|
if @config.minion_config
|
||||||
|
@machine.env.ui.info "Copying salt minion config to vm."
|
||||||
|
@machine.communicate.upload(expanded_path(@config.minion_config).to_s, temp_config_dir + "/minion")
|
||||||
|
end
|
||||||
|
|
||||||
|
if @config.master_config
|
||||||
|
@machine.env.ui.info "Copying salt master config to vm."
|
||||||
|
@machine.communicate.upload(expanded_path(@config.master_config).to_s, temp_config_dir + "/master")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Copy master and minion keys to VM
|
||||||
|
def upload_keys
|
||||||
|
if @config.minion_key and @config.minion_pub
|
||||||
|
@machine.env.ui.info "Uploading minion keys."
|
||||||
|
@machine.communicate.upload(expanded_path(@config.minion_key).to_s, temp_config_dir + "/minion.pem")
|
||||||
|
@machine.communicate.upload(expanded_path(@config.minion_pub).to_s, temp_config_dir + "/minion.pub")
|
||||||
|
end
|
||||||
|
|
||||||
|
if @config.master_key and @config.master_pub
|
||||||
|
@machine.env.ui.info "Uploading master keys."
|
||||||
|
@machine.communicate.upload(expanded_path(@config.master_key).to_s, temp_config_dir + "/master.pem")
|
||||||
|
@machine.communicate.upload(expanded_path(@config.master_pub).to_s, temp_config_dir + "/master.pub")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get bootstrap file location, bundled or custom
|
||||||
|
def get_bootstrap
|
||||||
|
if @config.bootstrap_script
|
||||||
|
bootstrap_abs_path = expanded_path(@config.bootstrap_script)
|
||||||
|
else
|
||||||
|
bootstrap_abs_path = Pathname.new("../bootstrap-salt.sh").expand_path(__FILE__)
|
||||||
|
end
|
||||||
|
return bootstrap_abs_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Determine if we are configure and/or installing, then do either
|
||||||
|
def run_bootstrap_script
|
||||||
|
install = need_install()
|
||||||
|
configure = need_configure()
|
||||||
|
config_dir = temp_config_dir()
|
||||||
|
options = bootstrap_options(install, configure, config_dir)
|
||||||
|
|
||||||
|
if configure or install
|
||||||
|
if configure and !install
|
||||||
|
@machine.env.ui.info "Salt binaries found. Configuring only."
|
||||||
|
else
|
||||||
|
@machine.env.ui.info "Bootstrapping Salt... (this may take a while)"
|
||||||
|
end
|
||||||
|
|
||||||
|
bootstrap_path = get_bootstrap()
|
||||||
|
bootstrap_destination = File.join(config_dir, "bootstrap_salt.sh")
|
||||||
|
@machine.communicate.upload(bootstrap_path.to_s, bootstrap_destination)
|
||||||
|
@machine.communicate.sudo("chmod +x %s" % bootstrap_destination)
|
||||||
|
bootstrap = @machine.communicate.sudo("%s %s" % [bootstrap_destination, options]) do |type, data|
|
||||||
|
if data[0] == "\n"
|
||||||
|
# Remove any leading newline but not whitespace. If we wanted to
|
||||||
|
# remove newlines and whitespace we would have used data.lstrip
|
||||||
|
data = data[1..-1]
|
||||||
|
end
|
||||||
|
if @config.verbose
|
||||||
|
@machine.env.ui.info(data.rstrip)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if !bootstrap
|
||||||
|
raise Salt::Errors::SaltError, :bootstrap_failed
|
||||||
|
end
|
||||||
|
|
||||||
|
if configure and !install
|
||||||
|
@machine.env.ui.info "Salt successfully configured!"
|
||||||
|
elsif configure and install
|
||||||
|
@machine.env.ui.info "Salt successfully configured and installed!"
|
||||||
|
elsif !configure and install
|
||||||
|
@machine.env.ui.info "Salt successfully installed!"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@machine.env.ui.info "Salt did not need installing or configuring."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# DEPRECATED
|
||||||
|
def accept_keys
|
||||||
|
if !@machine.communicate.test("which salt-key")
|
||||||
|
@machine.env.ui.info "Salt-key not installed!"
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
key_staged = false
|
||||||
|
|
||||||
|
keys = keys()
|
||||||
|
if keys.length > 0
|
||||||
|
@machine.env.ui.info "Minion keys registered:"
|
||||||
|
keys.each do |name|
|
||||||
|
@machine.env.ui.info " - %s" %name
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
@machine.env.ui.info "Waiting for minion key..."
|
||||||
|
|
||||||
|
attempts = 0
|
||||||
|
while !key_staged
|
||||||
|
attempts += 1
|
||||||
|
numkeys = @machine.communicate.sudo("salt-key -l pre | wc -l") do |type, rawoutput|
|
||||||
|
begin
|
||||||
|
if type == :stdout
|
||||||
|
output = Integer(rawoutput)
|
||||||
|
if output > 1
|
||||||
|
key_staged = true
|
||||||
|
end
|
||||||
|
break output
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
if attempts > 10
|
||||||
|
raise Salt::Errors::SaltError, "No keys staged"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if key_staged
|
||||||
|
@machine.env.ui.info "Adding %s key(s) for minion(s)" %numkeys
|
||||||
|
@machine.communicate.sudo("salt-key -A")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_highstate
|
||||||
|
if @config.run_highstate
|
||||||
|
@machine.env.ui.info "Calling state.highstate... (this may take a while)"
|
||||||
|
if @config.install_master
|
||||||
|
@machine.communicate.sudo("salt '*' saltutil.sync_all")
|
||||||
|
@machine.communicate.sudo("salt '*' state.highstate --verbose#{get_pillar}") do |type, data|
|
||||||
|
if @config.verbose
|
||||||
|
@machine.env.ui.info(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@machine.communicate.sudo("salt-call saltutil.sync_all")
|
||||||
|
@machine.communicate.sudo("salt-call state.highstate -l debug#{get_pillar}") do |type, data|
|
||||||
|
if @config.verbose
|
||||||
|
@machine.env.ui.info(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
@machine.env.ui.info "run_highstate set to false. Not running state.highstate."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue