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