kitchen-salt 0.0.24 → 0.0.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/kitchen-salt/pillars.rb +73 -0
- data/lib/kitchen-salt/states.rb +107 -0
- data/lib/kitchen-salt/util.rb +69 -0
- data/lib/kitchen-salt/version.rb +1 -1
- data/lib/kitchen/provisioner/dependencies.erb +99 -0
- data/lib/kitchen/provisioner/formula-fetch.sh +82 -0
- data/lib/kitchen/provisioner/install.erb +82 -0
- data/lib/kitchen/provisioner/install_win.erb +32 -0
- data/lib/kitchen/provisioner/minion.erb +13 -0
- data/lib/kitchen/provisioner/repository-setup.sh +26 -0
- data/lib/kitchen/provisioner/salt_solo.rb +182 -366
- metadata +12 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2508f6579671b6cfbb285f7024faa9c4265645d9
|
|
4
|
+
data.tar.gz: f506596a5019cea93aaada988025bcc1b691a9d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8af0c3817e2fd7e454eb7343dd2b9d013caa7e42a228c53a2fe46231390bb8b1f73b9aa08a59d74ce6e5c565039ae53ec21979c424a8cc99b9b74986cf8fa6ed
|
|
7
|
+
data.tar.gz: 34f6645a153a85fd2126491768c2402776d4235f31e132b5e7e75de757cd40e63de05119b9d4c2330698732ba4ff199e3bbcc43391d8841f11ef29be2ee4918d
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Kitchen
|
|
2
|
+
module Salt
|
|
3
|
+
module Pillars
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def prepare_pillars
|
|
7
|
+
info("Preparing pillars into #{config[:salt_pillar_root]}")
|
|
8
|
+
|
|
9
|
+
pillars = config[:pillars]
|
|
10
|
+
pillars_from_files = config[:'pillars-from-files']
|
|
11
|
+
debug("Pillars Hash: #{pillars}")
|
|
12
|
+
|
|
13
|
+
if pillars.nil? && pillars_from_files.nil?
|
|
14
|
+
if not config[:local_salt_root].nil?
|
|
15
|
+
pillars_location = File.join(config[:local_salt_root], 'pillar')
|
|
16
|
+
sandbox_pillar_path = File.join(sandbox_path, config[:salt_pillar_root])
|
|
17
|
+
cp_r_with_filter(pillars_location, sandbox_pillar_path, config[:salt_copy_filter])
|
|
18
|
+
return
|
|
19
|
+
end
|
|
20
|
+
return
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# we get a hash with all the keys converted to symbols, salt doesn't like this
|
|
24
|
+
# to convert all the keys back to strings again
|
|
25
|
+
pillars = unsymbolize(pillars)
|
|
26
|
+
debug("unsymbolized pillars hash: #{pillars}")
|
|
27
|
+
|
|
28
|
+
# write out each pillar (we get key/contents pairs)
|
|
29
|
+
prepare_pillar_files(pillars)
|
|
30
|
+
|
|
31
|
+
# copy the pillars from files straight across, as YAML.load/to_yaml and
|
|
32
|
+
# munge multiline strings
|
|
33
|
+
unless pillars_from_files.nil?
|
|
34
|
+
prepare_pillars_from_files(pillars_from_files)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def prepare_pillar_files(pillars)
|
|
39
|
+
pillars.each do |key, contents|
|
|
40
|
+
# convert the hash to yaml
|
|
41
|
+
pillar = contents.to_yaml
|
|
42
|
+
|
|
43
|
+
# .to_yaml will produce ! '*' for a key, Salt doesn't like this either
|
|
44
|
+
pillar.gsub!(/(!\s'\*')/, "'*'")
|
|
45
|
+
|
|
46
|
+
# generate the filename
|
|
47
|
+
sandbox_pillar_path = File.join(sandbox_path, config[:salt_pillar_root], key)
|
|
48
|
+
|
|
49
|
+
debug("Rendered pillar yaml for #{key}:\n #{pillar}")
|
|
50
|
+
write_raw_file(sandbox_pillar_path, pillar)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def copy_pillar(key, srcfile)
|
|
55
|
+
debug("Copying external pillar: #{key}, #{srcfile}")
|
|
56
|
+
# generate the filename
|
|
57
|
+
sandbox_pillar_path = File.join(sandbox_path, config[:salt_pillar_root], key)
|
|
58
|
+
# create the directory where the pillar file will go
|
|
59
|
+
FileUtils.mkdir_p(File.dirname(sandbox_pillar_path))
|
|
60
|
+
# copy the file across
|
|
61
|
+
FileUtils.copy srcfile, sandbox_pillar_path
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def prepare_pillars_from_files(pillars)
|
|
65
|
+
external_pillars = unsymbolize(pillars)
|
|
66
|
+
debug("external_pillars (unsymbolize): #{external_pillars}")
|
|
67
|
+
external_pillars.each do |key, srcfile|
|
|
68
|
+
copy_pillar(key, srcfile)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
module Kitchen
|
|
2
|
+
module Salt
|
|
3
|
+
module States
|
|
4
|
+
private
|
|
5
|
+
|
|
6
|
+
def prepare_state_top
|
|
7
|
+
info('Preparing state_top')
|
|
8
|
+
|
|
9
|
+
sandbox_state_top_path = File.join(sandbox_path, config[:salt_state_top])
|
|
10
|
+
|
|
11
|
+
if config[:state_top_from_file] == false
|
|
12
|
+
# use the top.sls embedded in .kitchen.yml
|
|
13
|
+
|
|
14
|
+
# we get a hash with all the keys converted to symbols, salt doesn't like this
|
|
15
|
+
# to convert all the keys back to strings again
|
|
16
|
+
state_top_content = unsymbolize(config[:state_top]).to_yaml
|
|
17
|
+
# .to_yaml will produce ! '*' for a key, Salt doesn't like this either
|
|
18
|
+
state_top_content.gsub!(/(!\s'\*')/, "'*'")
|
|
19
|
+
else
|
|
20
|
+
# load a top.sls from disk
|
|
21
|
+
if config[:local_salt_root].nil?
|
|
22
|
+
top_file = 'top.sls'
|
|
23
|
+
else
|
|
24
|
+
top_file = File.join(config[:local_salt_root], 'salt/top.sls')
|
|
25
|
+
end
|
|
26
|
+
state_top_content = File.read(top_file)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
write_raw_file(sandbox_state_top_path, state_top_content)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def prepare_states
|
|
33
|
+
if config[:state_collection] || config[:is_file_root] || !config[:local_salt_root].nil?
|
|
34
|
+
prepare_state_collection
|
|
35
|
+
else
|
|
36
|
+
prepare_formula config[:kitchen_root], config[:formula]
|
|
37
|
+
prepare_vendor_states
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def prepare_vendor_states
|
|
43
|
+
vendor_path = config[:vendor_path]
|
|
44
|
+
|
|
45
|
+
unless vendor_path.nil?
|
|
46
|
+
if Pathname.new(vendor_path).exist?
|
|
47
|
+
Dir[File.join(vendor_path, '*')].each do |d|
|
|
48
|
+
prepare_formula vendor_path, File.basename(d)
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
# :vendor_path was set, but not valid
|
|
52
|
+
raise UserError, "kitchen-salt: Invalid vendor_path set: #{vendor_path}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def prepare_formula_dir(path, subdir)
|
|
58
|
+
src = File.join(path, subdir)
|
|
59
|
+
|
|
60
|
+
if File.directory?(src)
|
|
61
|
+
debug("prepare_formula_dir: #{src} exists, copying..")
|
|
62
|
+
subdir_path = File.join(sandbox_path, config[:salt_file_root], subdir)
|
|
63
|
+
FileUtils.mkdir_p(subdir_path)
|
|
64
|
+
cp_r_with_filter(src, subdir_path, config[:salt_copy_filter])
|
|
65
|
+
else
|
|
66
|
+
debug("prepare_formula_dir: #{src} doesn't exist, skipping.")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def prepare_formula(path, formula)
|
|
71
|
+
info("Preparing formula: #{formula} from #{path}")
|
|
72
|
+
debug("Using config #{config}")
|
|
73
|
+
|
|
74
|
+
formula_dir = File.join(sandbox_path, config[:salt_file_root], formula)
|
|
75
|
+
FileUtils.mkdir_p(formula_dir)
|
|
76
|
+
cp_r_with_filter(File.join(path, formula), formula_dir, config[:salt_copy_filter])
|
|
77
|
+
|
|
78
|
+
# copy across the _modules etc directories for python implementation
|
|
79
|
+
%w(_modules _states _grains _renderers _returners).each do |extrapath|
|
|
80
|
+
prepare_formula_dir(path, extrapath)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def prepare_state_collection
|
|
85
|
+
info('Preparing state collection')
|
|
86
|
+
collection_name = config[:collection_name]
|
|
87
|
+
formula = config[:formula]
|
|
88
|
+
|
|
89
|
+
if collection_name.nil? && formula.nil?
|
|
90
|
+
info('neither collection_name or formula have been set, assuming this is a pre-built collection')
|
|
91
|
+
collection_name = ''
|
|
92
|
+
elsif collection_name.nil?
|
|
93
|
+
collection_name = formula
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if config[:local_salt_root].nil?
|
|
97
|
+
states_location = config[:kitchen_root]
|
|
98
|
+
else
|
|
99
|
+
states_location = File.join(config[:local_salt_root], 'salt')
|
|
100
|
+
end
|
|
101
|
+
collection_dir = File.join(sandbox_path, config[:salt_file_root], collection_name)
|
|
102
|
+
FileUtils.mkdir_p(collection_dir)
|
|
103
|
+
cp_r_with_filter(states_location, collection_dir, config[:salt_copy_filter])
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'find'
|
|
2
|
+
|
|
3
|
+
module Kitchen
|
|
4
|
+
module Salt
|
|
5
|
+
module Util
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def unsymbolize(obj)
|
|
9
|
+
if obj.is_a? Hash
|
|
10
|
+
obj.each_with_object({}) do |(k, v), a|
|
|
11
|
+
a[k.to_s] = unsymbolize(v)
|
|
12
|
+
a
|
|
13
|
+
end
|
|
14
|
+
elsif obj.is_a? Array
|
|
15
|
+
obj.each_with_object([]) do |e, a|
|
|
16
|
+
a << unsymbolize(e)
|
|
17
|
+
a
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
obj
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cp_r_with_filter(source_paths, target_path, filter = [])
|
|
25
|
+
debug("cp_r_with_filter:source_paths = #{source_paths}")
|
|
26
|
+
debug("cp_r_with_filter:target_path = #{target_path}")
|
|
27
|
+
debug("cp_r_with_filter:filter = #{filter}")
|
|
28
|
+
|
|
29
|
+
Array(source_paths).each do |source_path|
|
|
30
|
+
_cp_r_with_filter(source_path, target_path, filter)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def _cp_r_with_filter(source_path, target_path, filter = [])
|
|
35
|
+
Find.find(source_path) do |source|
|
|
36
|
+
target = source.sub(/^#{source_path}/, target_path)
|
|
37
|
+
debug("cp_r_with_filter:source = #{source}")
|
|
38
|
+
debug("cp_r_with_filter:target = #{target}")
|
|
39
|
+
filtered = filter.include?(File.basename(source))
|
|
40
|
+
if File.directory? source
|
|
41
|
+
if filtered
|
|
42
|
+
debug("Found #{source} in #{filter}, pruning it from the Find")
|
|
43
|
+
Find.prune
|
|
44
|
+
end
|
|
45
|
+
FileUtils.mkdir_p target unless File.exist? target
|
|
46
|
+
|
|
47
|
+
FileUtils.cp_r "#{source}/.", target if File.symlink? source
|
|
48
|
+
elsif filtered
|
|
49
|
+
debug("Found #{source} in #{filter}, not copying file")
|
|
50
|
+
else
|
|
51
|
+
FileUtils.copy source, target
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def write_raw_file(name, contents)
|
|
57
|
+
FileUtils.mkdir_p(File.dirname(name))
|
|
58
|
+
File.open(name, 'wb') do |file|
|
|
59
|
+
file.write(contents)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def write_hash_file(name, contents)
|
|
64
|
+
raw_contents = unsymbolize(contents).to_yaml
|
|
65
|
+
write_raw_file(name, raw_contents)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/lib/kitchen-salt/version.rb
CHANGED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
<%=
|
|
2
|
+
|
|
3
|
+
def install_dependencies
|
|
4
|
+
script = ''
|
|
5
|
+
|
|
6
|
+
script += <<-INSTALL
|
|
7
|
+
test ! -e "$(dirname $0)/formula-fetch.sh" || . "$(dirname $0)/formula-fetch.sh"
|
|
8
|
+
test ! -e "$(dirname $0)/repository-setup.sh" || . "$(dirname $0)/repository-setup.sh"
|
|
9
|
+
|
|
10
|
+
export SALT_ROOT="#{config[:root_path]}/#{config[:salt_file_root]}";
|
|
11
|
+
mkdir -p "${SALT_ROOT}";
|
|
12
|
+
INSTALL
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# setup apt
|
|
16
|
+
config[:vendor_repo].select{|x| x[:type]=='apt'}.each do |repo|
|
|
17
|
+
id =repo[:url].gsub(/[htp:\/.]/,'')
|
|
18
|
+
arch=repo[:arch] || '[arch=amd64]'
|
|
19
|
+
rurl=repo[:url]
|
|
20
|
+
comp=repo[:components] || 'main'
|
|
21
|
+
dist=repo[:distribution] || '$DISTRIB_CODENAME'
|
|
22
|
+
rkey=repo[:key_url]
|
|
23
|
+
script += <<-INSTALL
|
|
24
|
+
apt_repo_add "#{id}" "#{arch}" "#{rurl}" "#{comp}" "#{dist}" "#{rkey}";
|
|
25
|
+
INSTALL
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# setup ppa
|
|
29
|
+
config[:vendor_repo].select{|x| x[:type]=='ppa'}.each do |repo|
|
|
30
|
+
script += <<-INSTALL
|
|
31
|
+
#{sudo('add-apt-repository')} "ppa:#{repo[:name]}" -y;
|
|
32
|
+
INSTALL
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# TODO, setup yum repo
|
|
36
|
+
|
|
37
|
+
# update resources
|
|
38
|
+
config[:vendor_repo].map{|x| x[:type]}.uniq.each do |type|
|
|
39
|
+
case type
|
|
40
|
+
when 'apt'
|
|
41
|
+
script += <<-INSTALL
|
|
42
|
+
#{sudo('apt-get')} update -q;
|
|
43
|
+
sleep 10;
|
|
44
|
+
INSTALL
|
|
45
|
+
when 'yum'
|
|
46
|
+
script += <<-INSTALL
|
|
47
|
+
#{sudo('yum')} update;
|
|
48
|
+
sleep 10;
|
|
49
|
+
INSTALL
|
|
50
|
+
when 'spm'
|
|
51
|
+
script += <<-INSTALL
|
|
52
|
+
#{sudo('spm')} update_repo;
|
|
53
|
+
INSTALL
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# install formulas
|
|
58
|
+
config[:dependencies].select{|dependency| dependency.has_key?(:repo)}.each do |formula|
|
|
59
|
+
#unless config[:vendor_repo].has_key?(formula[:repo])
|
|
60
|
+
# raise UserError, "kitchen-salt: Invalid dependency formula :repo, no such vendor_repo '#{formula[:repo]}' specified."
|
|
61
|
+
#end
|
|
62
|
+
case formula[:repo]
|
|
63
|
+
when 'git'
|
|
64
|
+
script += <<-INSTALL
|
|
65
|
+
fetchGitFormula #{formula[:source]} "#{formula[:name]}" "#{formula[:branch] || 'master'}"
|
|
66
|
+
INSTALL
|
|
67
|
+
when 'spm'
|
|
68
|
+
# TODO: SPM should know the sandbox_path is /tmp/kitchen/etc
|
|
69
|
+
script += <<-INSTALL
|
|
70
|
+
#{sudo('spm')} install #{formula[:package]||formula[:name]};
|
|
71
|
+
INSTALL
|
|
72
|
+
when 'yum'
|
|
73
|
+
script += <<-INSTALL
|
|
74
|
+
#{sudo('yum')} install -y #{formula[:package]||formula[:name]};
|
|
75
|
+
INSTALL
|
|
76
|
+
when 'apt'
|
|
77
|
+
script += <<-INSTALL
|
|
78
|
+
#{sudo('apt-get')} install -y #{formula[:package]||formula[:name]};
|
|
79
|
+
INSTALL
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
script += <<-INSTALL
|
|
83
|
+
linkFormulas "$SALT_ROOT"
|
|
84
|
+
#{sudo('chown')} kitchen.kitchen -R /usr/share/salt-formulas;
|
|
85
|
+
#{sudo('chown')} kitchen.kitchen -R "$SALT_ROOT";
|
|
86
|
+
ls -la "$SALT_ROOT";
|
|
87
|
+
INSTALL
|
|
88
|
+
return script
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
<<-INSTALL
|
|
93
|
+
#!/bin/bash
|
|
94
|
+
|
|
95
|
+
echo "Install External Dependencies";
|
|
96
|
+
#{install_dependencies}
|
|
97
|
+
|
|
98
|
+
INSTALL
|
|
99
|
+
%>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Usage:
|
|
4
|
+
# ./formula-fetch.sh <Formula URL> <Name> <Branch>
|
|
5
|
+
#
|
|
6
|
+
# Example:
|
|
7
|
+
# GIT_FORMULAS_PATH=.vendor/formulas ./formula-fetch.sh https://github.com/salt-formulas/salt-formula-salt
|
|
8
|
+
# --
|
|
9
|
+
# GIT_FORMULAS_PATH=/usr/share/salt-formulas/env/_formulas
|
|
10
|
+
# xargs -n1 ./formula-fetch.sh < dependencies.txt
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Parse git dependencies from metadata.yml
|
|
14
|
+
# $1 - path to <formula>/metadata.yml
|
|
15
|
+
# sample to output:
|
|
16
|
+
# https://github.com/salt-formulas/salt-formula-git git
|
|
17
|
+
# https://github.com/salt-formulas/salt-formula-salt salt
|
|
18
|
+
function fetchDependencies() {
|
|
19
|
+
METADATA="$1";
|
|
20
|
+
grep -E "^dependencies:" "$METADATA" >/dev/null || return 0
|
|
21
|
+
# shellcheck disable=SC2086
|
|
22
|
+
(python - "$METADATA" | while read -r dep; do fetchGitFormula $dep; done) <<-DEPS
|
|
23
|
+
import sys,yaml
|
|
24
|
+
for dep in yaml.load(open(sys.argv[1], "ro"))["dependencies"]:
|
|
25
|
+
print("{source} {name}").format(**dep)
|
|
26
|
+
DEPS
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Fetch formula from git repo
|
|
30
|
+
# $1 - formula git repo url
|
|
31
|
+
# $2 - formula name (optional)
|
|
32
|
+
# $3 - branch (optional)
|
|
33
|
+
function fetchGitFormula() {
|
|
34
|
+
test -n "${FETCHED}" || declare -a FETCHED=()
|
|
35
|
+
export GIT_FORMULAS_PATH=${GIT_FORMULAS_PATH:-/usr/share/salt-formulas/env/_formulas}
|
|
36
|
+
mkdir -p "$GIT_FORMULAS_PATH"
|
|
37
|
+
if [ -n "$1" ]; then
|
|
38
|
+
source="$1"
|
|
39
|
+
name="$2"
|
|
40
|
+
test -n "$name" || name="${source//*salt-formula-}"
|
|
41
|
+
test -z "$3" && branch=master || branch=$3
|
|
42
|
+
if ! [[ "${FETCHED[*]}" =~ $name ]]; then # dependency not yet fetched
|
|
43
|
+
echo "Fetching: $name"
|
|
44
|
+
if test -e "$GIT_FORMULAS_PATH/$name"; then
|
|
45
|
+
pushd "$GIT_FORMULAS_PATH/$name" &>/dev/null
|
|
46
|
+
test ! -e .git || git pull -r
|
|
47
|
+
popd &>/dev/null
|
|
48
|
+
else
|
|
49
|
+
echo "git clone $source $GIT_FORMULAS_PATH/$name -b $branch"
|
|
50
|
+
git clone "$source" "$GIT_FORMULAS_PATH/$name" -b "$branch"
|
|
51
|
+
fi
|
|
52
|
+
# install dependencies
|
|
53
|
+
FETCHED+=($name)
|
|
54
|
+
fetchDependencies "$GIT_FORMULAS_PATH/$name/metadata.yml"
|
|
55
|
+
fi
|
|
56
|
+
else
|
|
57
|
+
echo Usage: fetchGitFormula "<git repo>" "[local formula directory name]" "[branch]"
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function linkFormulas() {
|
|
62
|
+
# OPTIONAL: Link formulas from git/pkg
|
|
63
|
+
|
|
64
|
+
SALT_ROOT=$1
|
|
65
|
+
SALT_ENV=${2:-/usr/share/salt-formulas/env}
|
|
66
|
+
|
|
67
|
+
# form git, development versions
|
|
68
|
+
find "$SALT_ENV"/_formulas -maxdepth 1 -mindepth 1 -type d -print0| xargs -0 -n1 basename | xargs -I{} \
|
|
69
|
+
ln -fs "$SALT_ENV"/_formulas/{}/{} "$SALT_ROOT"/{};
|
|
70
|
+
|
|
71
|
+
# form pkgs
|
|
72
|
+
find "$SALT_ENV" -path "*_formulas*" -prune -o -name "*" -maxdepth 1 -mindepth 1 -type d -print0| xargs -0 -n1 basename | xargs -I{} \
|
|
73
|
+
ln -fs "$SALT_ENV"/{} "$SALT_ROOT"/{};
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
# detect if file is being sourced
|
|
78
|
+
[[ "$0" != "${BASH_SOURCE[@]}" ]] || {
|
|
79
|
+
# if executed, run implicit function
|
|
80
|
+
fetchGitFormula "${@}"
|
|
81
|
+
}
|
|
82
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
|
|
2
|
+
<%=
|
|
3
|
+
salt_install = config[:salt_install]
|
|
4
|
+
salt_url = config[:salt_bootstrap_url]
|
|
5
|
+
bootstrap_options = config[:salt_bootstrap_options]
|
|
6
|
+
salt_version = config[:salt_version]
|
|
7
|
+
salt_apt_repo = config[:salt_apt_repo]
|
|
8
|
+
salt_apt_repo_key = config[:salt_apt_repo_key]
|
|
9
|
+
salt_ppa = config[:salt_ppa]
|
|
10
|
+
|
|
11
|
+
<<-INSTALL
|
|
12
|
+
sh -c '
|
|
13
|
+
#{Util.shell_helpers}
|
|
14
|
+
|
|
15
|
+
# what version of salt is installed?
|
|
16
|
+
SALT_VERSION=`salt-call --version | cut -d " " -f 2`
|
|
17
|
+
set +x
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if [ -z "${SALT_VERSION}" -a "#{salt_install}" = "bootstrap" ]
|
|
21
|
+
then
|
|
22
|
+
do_download #{salt_url} /tmp/bootstrap-salt.sh
|
|
23
|
+
#{sudo('sh')} /tmp/bootstrap-salt.sh #{bootstrap_options}
|
|
24
|
+
elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "apt" ]
|
|
25
|
+
then
|
|
26
|
+
if [ ! $(command -v lsb_release &>/dev/null) ]; then
|
|
27
|
+
. /etc/lsb-release
|
|
28
|
+
else
|
|
29
|
+
DISTRIB_CODENAME=$(lsb_release -s -c)
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
echo "-----> Configuring apt repo for salt #{salt_version}"
|
|
33
|
+
echo "deb #{salt_apt_repo}/#{salt_version} ${DISTRIB_CODENAME} main" | #{sudo('tee')} /etc/apt/sources.list.d/salt-#{salt_version}.list
|
|
34
|
+
|
|
35
|
+
do_download #{salt_apt_repo_key} /tmp/repo.key
|
|
36
|
+
#{sudo('apt-key')} add /tmp/repo.key
|
|
37
|
+
|
|
38
|
+
#{sudo('apt-get')} update
|
|
39
|
+
sleep 10
|
|
40
|
+
echo "-----> Installing salt-minion (#{salt_version})"
|
|
41
|
+
#{sudo('apt-get')} install -y python-support
|
|
42
|
+
#{sudo('apt-get')} install -y salt-minion salt-common
|
|
43
|
+
elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "distrib" ]
|
|
44
|
+
then
|
|
45
|
+
#{sudo('apt-get')} update
|
|
46
|
+
#{sudo('apt-get')} install -y salt-minion
|
|
47
|
+
elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "ppa" ]
|
|
48
|
+
then
|
|
49
|
+
#{sudo('apt-add-repository')} -y #{salt_ppa}
|
|
50
|
+
#{sudo('apt-get')} update
|
|
51
|
+
#{sudo('apt-get')} install -y salt-minion salt-common
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# check again, now that an install of some form should have happened
|
|
55
|
+
SALT_VERSION=`salt-call --version | cut -d " " -f 2`
|
|
56
|
+
|
|
57
|
+
if [ -z "${SALT_VERSION}" ]
|
|
58
|
+
then
|
|
59
|
+
echo "No salt-minion installed, install must have failed!!"
|
|
60
|
+
echo "salt_install = #{salt_install}"
|
|
61
|
+
echo "salt_url = #{salt_url}"
|
|
62
|
+
echo "bootstrap_options = #{bootstrap_options}"
|
|
63
|
+
echo "salt_version = #{salt_version}"
|
|
64
|
+
echo "salt_apt_repo = #{salt_apt_repo}"
|
|
65
|
+
echo "salt_apt_repo_key = #{salt_apt_repo_key}"
|
|
66
|
+
echo "salt_ppa = #{salt_ppa}"
|
|
67
|
+
exit 2
|
|
68
|
+
elif [ "${SALT_VERSION}" = "#{salt_version}" -o "#{salt_version}" = "latest" ]
|
|
69
|
+
then
|
|
70
|
+
echo "You asked for #{salt_version} and you have ${SALT_VERSION} installed, sweet!"
|
|
71
|
+
elif [ ! -z "${SALT_VERSION}" -a "#{salt_install}" = "bootstrap" ]
|
|
72
|
+
then
|
|
73
|
+
echo "You asked for bootstrap install and you have got ${SALT_VERSION}, hope thats ok!"
|
|
74
|
+
else
|
|
75
|
+
echo "You asked for #{salt_version} and you have got ${SALT_VERSION} installed, dunno how to fix that, sorry!"
|
|
76
|
+
exit 2
|
|
77
|
+
fi
|
|
78
|
+
|
|
79
|
+
#{install_chef}
|
|
80
|
+
'
|
|
81
|
+
INSTALL
|
|
82
|
+
%>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<%=
|
|
2
|
+
salt_install = config[:salt_install]
|
|
3
|
+
salt_url = config[:salt_bootstrap_url]
|
|
4
|
+
bootstrap_options = config[:salt_bootstrap_options]
|
|
5
|
+
salt_version = config[:salt_version]
|
|
6
|
+
|
|
7
|
+
<<-POWERSHELL
|
|
8
|
+
if (Test-Path c:\\salt\\salt-call.bat) {
|
|
9
|
+
$installed_version = $(c:\\salt\\salt-call.bat --version).split(' ')[1]
|
|
10
|
+
}
|
|
11
|
+
if (-Not $(Test-Path c:\\temp)) {
|
|
12
|
+
New-Item -Path c:\\temp -itemtype directory
|
|
13
|
+
}
|
|
14
|
+
if (-Not $installed_version -And "#{salt_install}" -eq "bootstrap") {
|
|
15
|
+
(New-Object net.webclient).DownloadFile("#{salt_url}", "c:\\temp\\salt_bootstrap.ps1")
|
|
16
|
+
#{sudo('powershell')} c:\\temp\\salt_bootstrap.ps1 #{bootstrap_options}
|
|
17
|
+
}
|
|
18
|
+
$installed_version = $(c:\\salt\\salt-call.bat --version).split(' ')[1]
|
|
19
|
+
if (-Not $installed_version) {
|
|
20
|
+
write-host "No salt-minion installed, install must have failed!!"
|
|
21
|
+
write-host "salt_install = #{salt_install}"
|
|
22
|
+
write-host "salt_url = #{salt_url}"
|
|
23
|
+
write-host "bootstrap_options = #{bootstrap_options}"
|
|
24
|
+
} elseif ($installed_version -And "#{salt_install}" -eq "bootstrap") {
|
|
25
|
+
write-host "You asked for bootstrap install and you have got $installed_version, hope that's ok!"
|
|
26
|
+
} else {
|
|
27
|
+
write-host "You asked for #{salt_version} and you have got $installed_version installed, dunno ho to fix that, sorry!"
|
|
28
|
+
exit 2
|
|
29
|
+
}
|
|
30
|
+
#{install_chef}
|
|
31
|
+
POWERSHELL
|
|
32
|
+
%>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
state_top: top.sls
|
|
2
|
+
|
|
3
|
+
file_client: local
|
|
4
|
+
|
|
5
|
+
file_roots:
|
|
6
|
+
<%= config[:salt_env] %>:
|
|
7
|
+
|
|
8
|
+
- <%= File.join(config[:root_path], (windows_os? ? config[:salt_file_root].gsub('/', '\\') : config[:salt_file_root])) %>
|
|
9
|
+
- <%= File.join(config[:root_path], (windows_os? ? config[:salt_file_root].gsub('/', '\\') : config[:salt_file_root]), 'spm') %>
|
|
10
|
+
|
|
11
|
+
pillar_roots:
|
|
12
|
+
<%= config[:salt_env] %>:
|
|
13
|
+
- <%= File.join(config[:root_path], (windows_os? ? config[:salt_pillar_root].gsub('/', '\\') : config[:salt_pillar_root])) %>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
function apt_repo_add {
|
|
4
|
+
id="$1"
|
|
5
|
+
arch="$2"
|
|
6
|
+
rurl="$3"
|
|
7
|
+
comp="$4"
|
|
8
|
+
dist="$5"
|
|
9
|
+
rkey="$6"
|
|
10
|
+
test -e /tmp/apt_repo_vendor_"${id}".key || {
|
|
11
|
+
echo "-----> Configuring formula apt vendor_repo ${rurl}"
|
|
12
|
+
eval "$(cat /etc/lsb-release)"
|
|
13
|
+
if curl -k "${rkey}" -o /tmp/apt_repo_vendor_"${id}".key; then
|
|
14
|
+
echo "deb ${arch} ${rurl} ${dist} ${comp}" | tee /etc/apt/sources.list.d/vendor-repo.list
|
|
15
|
+
apt-key add /tmp/apt_repo_vendor_"${id}".key
|
|
16
|
+
fi
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
# detect if file is being sourced
|
|
21
|
+
[[ "$0" != "${BASH_SOURCE[@]}" ]] || {
|
|
22
|
+
# if executed, run implicit function
|
|
23
|
+
#apt_repo_add "${@}"
|
|
24
|
+
echo 'Usage: apt_repo_add "custom id" "arch" "repo url" "components" "distribution" "repo gpg key"';
|
|
25
|
+
}
|
|
26
|
+
|
|
@@ -17,163 +17,122 @@
|
|
|
17
17
|
# limitations under the License.
|
|
18
18
|
|
|
19
19
|
require 'kitchen/provisioner/base'
|
|
20
|
-
require '
|
|
20
|
+
require 'kitchen-salt/util'
|
|
21
|
+
require 'kitchen-salt/pillars'
|
|
22
|
+
require 'kitchen-salt/states'
|
|
21
23
|
require 'fileutils'
|
|
22
24
|
require 'yaml'
|
|
23
25
|
|
|
24
26
|
module Kitchen
|
|
25
|
-
|
|
26
27
|
module Provisioner
|
|
27
|
-
|
|
28
28
|
# Basic Salt Masterless Provisioner, based on work by
|
|
29
29
|
#
|
|
30
30
|
# @author Chris Lundquist (<chris.ludnquist@github.com>)
|
|
31
|
-
class SaltSolo < Base
|
|
32
|
-
|
|
33
|
-
default_config :salt_version, "latest"
|
|
34
|
-
|
|
35
|
-
# supported install methods: bootstrap|apt
|
|
36
|
-
default_config :salt_install, "bootstrap"
|
|
37
|
-
|
|
38
|
-
default_config :salt_bootstrap_url, "http://bootstrap.saltstack.org"
|
|
39
|
-
default_config :salt_bootstrap_options, ""
|
|
40
31
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
32
|
+
class SaltSolo < Base
|
|
33
|
+
include Kitchen::Salt::Util
|
|
34
|
+
include Kitchen::Salt::Pillars
|
|
35
|
+
include Kitchen::Salt::States
|
|
36
|
+
|
|
37
|
+
DEFAULT_CONFIG = {
|
|
38
|
+
dry_run: false,
|
|
39
|
+
salt_version: 'latest',
|
|
40
|
+
salt_install: 'bootstrap',
|
|
41
|
+
salt_bootstrap_url: 'https://bootstrap.saltstack.org',
|
|
42
|
+
salt_bootstrap_options: '',
|
|
43
|
+
salt_apt_repo: 'https://repo.saltstack.com/apt/ubuntu/16.04/amd64/latest',
|
|
44
|
+
salt_apt_repo_key: 'https://repo.saltstack.com/apt/ubuntu/16.04/amd64/latest/SALTSTACK-GPG-KEY.pub',
|
|
45
|
+
salt_ppa: 'ppa:saltstack/salt',
|
|
46
|
+
bootstrap_url: 'https://raw.githubusercontent.com/saltstack/kitchen-salt/master/assets/install.sh',
|
|
47
|
+
chef_bootstrap_url: 'https://www.getchef.com/chef/install.sh',
|
|
48
|
+
salt_config: '/etc/salt',
|
|
49
|
+
salt_minion_config: '/etc/salt/minion',
|
|
50
|
+
salt_minion_config_template: nil,
|
|
51
|
+
salt_minion_id: nil,
|
|
52
|
+
salt_env: 'base',
|
|
53
|
+
salt_file_root: '/srv/salt',
|
|
54
|
+
salt_pillar_root: '/srv/pillar',
|
|
55
|
+
salt_spm_root: '/srv/spm',
|
|
56
|
+
salt_state_top: '/srv/salt/top.sls',
|
|
57
|
+
state_collection: false,
|
|
58
|
+
state_top: {},
|
|
59
|
+
state_top_from_file: false,
|
|
60
|
+
salt_run_highstate: true,
|
|
61
|
+
salt_copy_filter: [],
|
|
62
|
+
is_file_root: false,
|
|
63
|
+
require_chef: true,
|
|
64
|
+
dependencies: [],
|
|
65
|
+
vendor_path: nil,
|
|
66
|
+
vendor_repo: {},
|
|
67
|
+
omnibus_cachier: false,
|
|
68
|
+
local_salt_root: nil
|
|
69
|
+
}
|
|
65
70
|
|
|
66
71
|
# salt-call version that supports the undocumented --retcode-passthrough command
|
|
67
|
-
RETCODE_VERSION = '0.17.5'
|
|
72
|
+
RETCODE_VERSION = '0.17.5'.freeze
|
|
73
|
+
|
|
74
|
+
DEFAULT_CONFIG.each do |k, v|
|
|
75
|
+
default_config k, v
|
|
76
|
+
end
|
|
68
77
|
|
|
69
78
|
def install_command
|
|
70
|
-
debug(diagnose
|
|
79
|
+
debug(diagnose)
|
|
80
|
+
salt_version = config[:salt_version]
|
|
71
81
|
|
|
72
82
|
# if salt_verison is set, bootstrap is being used & bootstrap_options is empty,
|
|
73
83
|
# set the bootstrap_options string to git install the requested version
|
|
74
|
-
if (
|
|
75
|
-
debug("Using bootstrap git to install #{
|
|
76
|
-
config[:salt_bootstrap_options] = "-P git v#{
|
|
84
|
+
if (salt_version != 'latest') && (config[:salt_install] == 'bootstrap') && config[:salt_bootstrap_options].empty?
|
|
85
|
+
debug("Using bootstrap git to install #{salt_version}")
|
|
86
|
+
config[:salt_bootstrap_options] = "-P git v#{salt_version}"
|
|
77
87
|
end
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
salt_version = config[:salt_version]
|
|
85
|
-
salt_apt_repo = config[:salt_apt_repo]
|
|
86
|
-
salt_apt_repo_key = config[:salt_apt_repo_key]
|
|
87
|
-
salt_ppa = config[:salt_ppa]
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<<-INSTALL
|
|
91
|
-
sh -c '
|
|
92
|
-
#{Util.shell_helpers}
|
|
93
|
-
|
|
94
|
-
# what version of salt is installed?
|
|
95
|
-
SALT_VERSION=`salt-call --version | cut -d " " -f 2`
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if [ -z "${SALT_VERSION}" -a "#{salt_install}" = "bootstrap" ]
|
|
99
|
-
then
|
|
100
|
-
do_download #{salt_url} /tmp/bootstrap-salt.sh
|
|
101
|
-
#{sudo('sh')} /tmp/bootstrap-salt.sh #{bootstrap_options}
|
|
102
|
-
elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "apt" ]
|
|
103
|
-
then
|
|
104
|
-
if [ -z "`which lsb_release`" ]; then
|
|
105
|
-
. /etc/lsb-release
|
|
106
|
-
else
|
|
107
|
-
DISTRIB_CODENAME=`lsb_release -s -c`
|
|
108
|
-
fi
|
|
109
|
-
|
|
110
|
-
echo "-----> Configuring apt repo for salt #{salt_version}"
|
|
111
|
-
echo "deb #{salt_apt_repo}/salt-#{salt_version} ${DISTRIB_CODENAME} main" | #{sudo('tee')} /etc/apt/sources.list.d/salt-#{salt_version}.list
|
|
112
|
-
|
|
113
|
-
do_download #{salt_apt_repo_key} /tmp/repo.key
|
|
114
|
-
#{sudo('apt-key')} add /tmp/repo.key
|
|
115
|
-
|
|
116
|
-
#{sudo('apt-get')} update
|
|
117
|
-
sleep 10
|
|
118
|
-
echo "-----> Installing salt-minion (#{salt_version})"
|
|
119
|
-
#{sudo('apt-get')} install -y python-support
|
|
120
|
-
#{sudo('apt-get')} install -y salt-minion
|
|
121
|
-
#{sudo('apt-get')} install -y salt-common
|
|
122
|
-
#{sudo('apt-get')} install -y salt-minion
|
|
123
|
-
elif [ -z "${SALT_VERSION}" -a "#{salt_install}" = "ppa" ]
|
|
124
|
-
then
|
|
125
|
-
#{sudo('apt-add-repository')} -y #{salt_ppa}
|
|
126
|
-
#{sudo('apt-get')} update
|
|
127
|
-
#{sudo('apt-get')} install -y salt-minion
|
|
128
|
-
fi
|
|
129
|
-
|
|
130
|
-
# check again, now that an install of some form should have happened
|
|
131
|
-
SALT_VERSION=`salt-call --version | cut -d " " -f 2`
|
|
132
|
-
|
|
133
|
-
if [ -z "${SALT_VERSION}" ]
|
|
134
|
-
then
|
|
135
|
-
echo "No salt-minion installed, install must have failed!!"
|
|
136
|
-
echo "salt_install = #{salt_install}"
|
|
137
|
-
echo "salt_url = #{salt_url}"
|
|
138
|
-
echo "bootstrap_options = #{bootstrap_options}"
|
|
139
|
-
echo "salt_version = #{salt_version}"
|
|
140
|
-
echo "salt_apt_repo = #{salt_apt_repo}"
|
|
141
|
-
echo "salt_apt_repo_key = #{salt_apt_repo_key}"
|
|
142
|
-
echo "salt_ppa = #{salt_ppa}"
|
|
143
|
-
exit 2
|
|
144
|
-
elif [ "${SALT_VERSION}" = "#{salt_version}" -o "#{salt_version}" = "latest" ]
|
|
145
|
-
then
|
|
146
|
-
echo "You asked for #{salt_version} and you have ${SALT_VERSION} installed, sweet!"
|
|
147
|
-
elif [ ! -z "${SALT_VERSION}" -a "#{salt_install}" = "bootstrap" ]
|
|
148
|
-
then
|
|
149
|
-
echo "You asked for bootstrap install and you have got ${SALT_VERSION}, hope thats ok!"
|
|
150
|
-
else
|
|
151
|
-
echo "You asked for #{salt_version} and you have got ${SALT_VERSION} installed, dunno how to fix that, sorry!"
|
|
152
|
-
exit 2
|
|
153
|
-
fi
|
|
154
|
-
|
|
155
|
-
#{install_chef}
|
|
89
|
+
if windows_os?
|
|
90
|
+
install_template = File.expand_path("./../install_win.erb", __FILE__)
|
|
91
|
+
else
|
|
92
|
+
install_template = File.expand_path("./../install.erb", __FILE__)
|
|
93
|
+
end
|
|
156
94
|
|
|
157
|
-
|
|
158
|
-
|
|
95
|
+
erb = ERB.new(File.read(install_template)).result(binding)
|
|
96
|
+
debug("Install Command:" + erb.to_s)
|
|
97
|
+
erb
|
|
159
98
|
end
|
|
160
99
|
|
|
161
100
|
def install_chef
|
|
162
101
|
return unless config[:require_chef]
|
|
163
102
|
chef_url = config[:chef_bootstrap_url]
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
103
|
+
if windows_os?
|
|
104
|
+
<<-POWERSHELL
|
|
105
|
+
if (-Not $(test-path c:\\opscode\\chef) {
|
|
106
|
+
if (-Not $(Test-Path c:\\temp)) {
|
|
107
|
+
New-Item -Path c:\\temp -itemtype directory
|
|
108
|
+
}
|
|
109
|
+
(New-Object net.webclient).DownloadFile("#{chef_url}", "c:\\temp\\chef_bootstrap.ps1")
|
|
110
|
+
write-host "-----> Installing Chef Omnibus (for busser/serverspec ruby support)"
|
|
111
|
+
#{sudo('powershell')} c:\\temp\\chef_bootstrap.ps1
|
|
112
|
+
}
|
|
113
|
+
POWERSHELL
|
|
114
|
+
else
|
|
115
|
+
omnibus_download_dir = config[:omnibus_cachier] ? '/tmp/vagrant-cache/omnibus_chef' : '/tmp'
|
|
116
|
+
bootstrap_url = config[:bootstrap_url]
|
|
117
|
+
bootstrap_download_dir = '/tmp'
|
|
118
|
+
<<-INSTALL
|
|
119
|
+
echo "-----> Trying to install ruby(-dev) using assets.sh from kitchen-salt"
|
|
120
|
+
mkdir -p #{bootstrap_download_dir}
|
|
121
|
+
if [ ! -x #{bootstrap_download_dir}/install.sh ]
|
|
122
|
+
then
|
|
123
|
+
do_download #{bootstrap_url} #{bootstrap_download_dir}/install.sh
|
|
124
|
+
fi
|
|
125
|
+
#{sudo('sh')} #{bootstrap_download_dir}/install.sh -d #{bootstrap_download_dir}
|
|
126
|
+
if [ $? -ne 0 ] || [ ! -d "/opt/chef" ]
|
|
127
|
+
then
|
|
128
|
+
echo "Failed install ruby(-dev) using assets.sh from kitchen-salt"
|
|
129
|
+
echo "-----> Fallback to Chef Bootstrap script (for busser/serverspec ruby support)"
|
|
130
|
+
mkdir -p "#{omnibus_download_dir}"
|
|
131
|
+
if [ ! -x #{omnibus_download_dir}/install.sh ]
|
|
132
|
+
#{sudo('sh')} #{omnibus_download_dir}/install.sh -d #{omnibus_download_dir}
|
|
133
|
+
fi;
|
|
134
|
+
INSTALL
|
|
135
|
+
end
|
|
177
136
|
end
|
|
178
137
|
|
|
179
138
|
def create_sandbox
|
|
@@ -182,75 +141,78 @@ module Kitchen
|
|
|
182
141
|
prepare_minion
|
|
183
142
|
prepare_pillars
|
|
184
143
|
prepare_grains
|
|
185
|
-
|
|
186
|
-
if config[:state_collection] || config[:is_file_root]
|
|
187
|
-
prepare_state_collection
|
|
188
|
-
else
|
|
189
|
-
prepare_formula config[:kitchen_root], config[:formula]
|
|
190
|
-
|
|
191
|
-
unless config[:vendor_path].nil?
|
|
192
|
-
if Pathname.new(config[:vendor_path]).exist?
|
|
193
|
-
deps = if Pathname.new(config[:vendor_path]).absolute?
|
|
194
|
-
Dir["#{config[:vendor_path]}/*"]
|
|
195
|
-
else
|
|
196
|
-
Dir["#{config[:kitchen_root]}/#{config[:vendor_path]}/*"]
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
deps.each do |d|
|
|
200
|
-
prepare_formula "#{config[:kitchen_root]}/#{config[:vendor_path]}", File.basename(d)
|
|
201
|
-
end
|
|
202
|
-
else
|
|
203
|
-
# :vendor_path was set, but not valid
|
|
204
|
-
raise UserError, "kitchen-salt: Invalid vendor_path set: #{config[:vendor_path]}"
|
|
205
|
-
end
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
config[:dependencies].each do |formula|
|
|
210
|
-
prepare_formula formula[:path], formula[:name]
|
|
211
|
-
end
|
|
144
|
+
prepare_states
|
|
212
145
|
prepare_state_top
|
|
146
|
+
# upload scripts, cached formulas, and setup system repositories
|
|
147
|
+
prepare_dependencies
|
|
213
148
|
end
|
|
214
149
|
|
|
215
150
|
def init_command
|
|
216
|
-
debug("Initialising Driver #{
|
|
217
|
-
|
|
151
|
+
debug("Initialising Driver #{name}")
|
|
152
|
+
if windows_os?
|
|
153
|
+
cmd = "mkdir -Force -Path ""#{config[:root_path]}"""
|
|
154
|
+
else
|
|
155
|
+
cmd = "mkdir -p '#{config[:root_path]}';"
|
|
156
|
+
end
|
|
157
|
+
cmd += <<-INSTALL
|
|
158
|
+
#{config[:init_environment]}
|
|
159
|
+
INSTALL
|
|
160
|
+
cmd
|
|
218
161
|
end
|
|
219
162
|
|
|
220
|
-
def
|
|
221
|
-
|
|
222
|
-
# sudo(File.join(config[:root_path], File.basename(config[:script])))
|
|
223
|
-
debug(diagnose())
|
|
224
|
-
if config[:salt_run_highstate]
|
|
225
|
-
cmd = sudo("salt-call --config-dir=#{File.join(config[:root_path], config[:salt_config])} --local state.highstate")
|
|
226
|
-
end
|
|
163
|
+
def salt_command
|
|
164
|
+
salt_version = config[:salt_version]
|
|
227
165
|
|
|
228
|
-
|
|
229
|
-
|
|
166
|
+
cmd = ""
|
|
167
|
+
if windows_os?
|
|
168
|
+
salt_call = "c:\\salt\\salt-call.bat"
|
|
169
|
+
salt_config_path = config[:salt_config].gsub('/', '\\')
|
|
170
|
+
cmd << "(get-content #{File.join(config[:root_path], salt_config_path, 'minion').gsub('/', '\\')}).replace(\"`$env`:TEMP\", $env:TEMP) | set-content #{File.join(config[:root_path], salt_config_path, 'minion').gsub('/', '\\')} ;"
|
|
171
|
+
else
|
|
172
|
+
# install/update dependencies
|
|
173
|
+
cmd << sudo("chmod +x #{config[:root_path]}/*.sh;")
|
|
174
|
+
cmd << sudo("#{config[:root_path]}/dependencies.sh;")
|
|
175
|
+
salt_config_path = config[:salt_config]
|
|
176
|
+
salt_call = "salt-call"
|
|
177
|
+
end
|
|
178
|
+
cmd << sudo("#{salt_call} --state-output=changes --config-dir=#{File.join(config[:root_path], salt_config_path)} --local state.highstate")
|
|
179
|
+
cmd << " --log-level=#{config[:log_level]}" if config[:log_level]
|
|
180
|
+
cmd << " --id=#{config[:salt_minion_id]}" if config[:salt_minion_id]
|
|
181
|
+
cmd << " test=#{config[:dry_run]}" if config[:dry_run]
|
|
182
|
+
if salt_version > RETCODE_VERSION || salt_version == 'latest'
|
|
183
|
+
# hope for the best and hope it works eventually
|
|
184
|
+
cmd << ' --retcode-passthrough'
|
|
230
185
|
end
|
|
186
|
+
if windows_os?
|
|
187
|
+
cmd << ' ; exit $LASTEXITCODE'
|
|
188
|
+
end
|
|
189
|
+
cmd
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def run_command
|
|
193
|
+
debug("running driver #{name}")
|
|
194
|
+
debug(diagnose)
|
|
231
195
|
|
|
232
196
|
# config[:salt_version] can be 'latest' or 'x.y.z', 'YYYY.M.x' etc
|
|
233
197
|
# error return codes are a mess in salt:
|
|
234
198
|
# https://github.com/saltstack/salt/pull/11337
|
|
235
199
|
# Unless we know we have a version that supports --retcode-passthrough
|
|
236
200
|
# attempt to scan the output for signs of failure
|
|
237
|
-
if config[:salt_version]
|
|
238
|
-
# hope for the best and hope it works eventually
|
|
239
|
-
cmd = cmd + " --retcode-passthrough"
|
|
240
|
-
else
|
|
201
|
+
if config[:salt_version] <= RETCODE_VERSION
|
|
241
202
|
# scan the output for signs of failure, there is a risk of false negatives
|
|
242
203
|
fail_grep = 'grep -e Result.*False -e Data.failed.to.compile -e No.matching.sls.found.for'
|
|
243
204
|
# capture any non-zero exit codes from the salt-call | tee pipe
|
|
244
|
-
cmd = 'set -o pipefail ; ' <<
|
|
205
|
+
cmd = 'set -o pipefail ; ' << salt_command
|
|
245
206
|
# Capture the salt-call output & exit code
|
|
246
|
-
cmd <<
|
|
207
|
+
cmd << ' 2>&1 | tee /tmp/salt-call-output ; SC=$? ; echo salt-call exit code: $SC ;'
|
|
247
208
|
# check the salt-call output for fail messages
|
|
248
209
|
cmd << " (sed '/#{fail_grep}/d' /tmp/salt-call-output | #{fail_grep} ; EC=$? ; echo salt-call output grep exit code ${EC} ;"
|
|
249
210
|
# use the non-zer exit code from salt-call, then invert the results of the grep for failures
|
|
250
|
-
cmd <<
|
|
211
|
+
cmd << ' [ ${SC} -ne 0 ] && exit ${SC} ; [ ${EC} -eq 0 ] && exit 1 ; [ ${EC} -eq 1 ] && exit 0)'
|
|
212
|
+
cmd
|
|
213
|
+
else
|
|
214
|
+
salt_command
|
|
251
215
|
end
|
|
252
|
-
|
|
253
|
-
cmd
|
|
254
216
|
end
|
|
255
217
|
|
|
256
218
|
protected
|
|
@@ -258,123 +220,34 @@ module Kitchen
|
|
|
258
220
|
def prepare_data
|
|
259
221
|
return unless config[:data_path]
|
|
260
222
|
|
|
261
|
-
info(
|
|
223
|
+
info('Preparing data')
|
|
262
224
|
debug("Using data from #{config[:data_path]}")
|
|
263
225
|
|
|
264
|
-
tmpdata_dir = File.join(sandbox_path,
|
|
226
|
+
tmpdata_dir = File.join(sandbox_path, 'data')
|
|
265
227
|
FileUtils.mkdir_p(tmpdata_dir)
|
|
266
|
-
#FileUtils.cp_r(Dir.glob("#{config[:data_path]}/*"), tmpdata_dir)
|
|
267
228
|
cp_r_with_filter(config[:data_path], tmpdata_dir, config[:salt_copy_filter])
|
|
268
229
|
end
|
|
269
230
|
|
|
270
231
|
def prepare_minion
|
|
271
|
-
info(
|
|
272
|
-
|
|
273
|
-
minion_config_content = <<-MINION_CONFIG.gsub(/^ {10}/, '')
|
|
274
|
-
state_top: top.sls
|
|
275
|
-
|
|
276
|
-
file_client: local
|
|
277
|
-
|
|
278
|
-
file_roots:
|
|
279
|
-
#{config[:salt_env]}:
|
|
280
|
-
- #{File.join(config[:root_path], config[:salt_file_root])}
|
|
281
|
-
|
|
282
|
-
pillar_roots:
|
|
283
|
-
#{config[:salt_env]}:
|
|
284
|
-
- #{File.join(config[:root_path], config[:salt_pillar_root])}
|
|
285
|
-
MINION_CONFIG
|
|
286
|
-
|
|
287
|
-
# create the temporary path for the salt-minion config file
|
|
288
|
-
debug("sandbox is #{sandbox_path}")
|
|
289
|
-
sandbox_minion_config_path = File.join(sandbox_path, config[:salt_minion_config])
|
|
290
|
-
|
|
291
|
-
# create the directory & drop the file in
|
|
292
|
-
FileUtils.mkdir_p(File.dirname(sandbox_minion_config_path))
|
|
293
|
-
File.open(sandbox_minion_config_path, "wb") do |file|
|
|
294
|
-
file.write(minion_config_content)
|
|
295
|
-
end
|
|
232
|
+
info('Preparing salt-minion')
|
|
296
233
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
def unsymbolize(obj)
|
|
300
|
-
return obj.inject({}){|memo,(k,v)| memo[k.to_s] = unsymbolize(v); memo} if obj.is_a? Hash
|
|
301
|
-
return obj.inject([]){|memo,v| memo << unsymbolize(v); memo} if obj.is_a? Array
|
|
302
|
-
return obj
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
def prepare_state_top
|
|
306
|
-
info("Preparing state_top")
|
|
307
|
-
|
|
308
|
-
sandbox_state_top_path = File.join(sandbox_path, config[:salt_state_top])
|
|
309
|
-
|
|
310
|
-
if (config[:state_top_from_file] == false)
|
|
311
|
-
# use the top.sls embedded in .kitchen.yml
|
|
312
|
-
|
|
313
|
-
# we get a hash with all the keys converted to symbols, salt doesn't like this
|
|
314
|
-
# to convert all the keys back to strings again
|
|
315
|
-
state_top_content = unsymbolize(config[:state_top]).to_yaml
|
|
316
|
-
# .to_yaml will produce ! '*' for a key, Salt doesn't like this either
|
|
317
|
-
state_top_content.gsub!(/(!\s'\*')/, "'*'")
|
|
234
|
+
if config[:salt_minion_config_template]
|
|
235
|
+
minion_template = File.expand_path(config[:salt_minion_config_template], Kitchen::Config.new.kitchen_root)
|
|
318
236
|
else
|
|
319
|
-
|
|
320
|
-
state_top_content = File.read("top.sls")
|
|
237
|
+
minion_template = File.expand_path("./../minion.erb", __FILE__)
|
|
321
238
|
end
|
|
322
239
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
240
|
+
if File.extname(minion_template) == ".erb"
|
|
241
|
+
minion_config_content = ERB.new(File.read(minion_template)).result(binding)
|
|
242
|
+
else
|
|
243
|
+
minion_config_content = File.read(minion_template)
|
|
327
244
|
end
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
def prepare_pillars
|
|
331
|
-
info("Preparing pillars into #{config[:salt_pillar_root]}")
|
|
332
|
-
debug("Pillars Hash: #{config[:pillars]}")
|
|
333
|
-
|
|
334
|
-
return if config[:pillars].nil? && config[:'pillars-from-files'].nil?
|
|
335
245
|
|
|
336
|
-
#
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
debug("unsymbolized pillars hash: #{pillars}")
|
|
340
|
-
|
|
341
|
-
# write out each pillar (we get key/contents pairs)
|
|
342
|
-
pillars.each do |key,contents|
|
|
343
|
-
|
|
344
|
-
# convert the hash to yaml
|
|
345
|
-
pillar = contents.to_yaml
|
|
346
|
-
|
|
347
|
-
# .to_yaml will produce ! '*' for a key, Salt doesn't like this either
|
|
348
|
-
pillar.gsub!(/(!\s'\*')/, "'*'")
|
|
349
|
-
|
|
350
|
-
# generate the filename
|
|
351
|
-
sandbox_pillar_path = File.join(sandbox_path, config[:salt_pillar_root], key)
|
|
352
|
-
|
|
353
|
-
# create the directory where the pillar file will go
|
|
354
|
-
FileUtils.mkdir_p(File.dirname(sandbox_pillar_path))
|
|
355
|
-
|
|
356
|
-
debug("Rendered pillar yaml for #{key}:\n #{pillar}")
|
|
357
|
-
# create the directory & drop the file in
|
|
358
|
-
File.open(sandbox_pillar_path, "wb") do |file|
|
|
359
|
-
file.write(pillar)
|
|
360
|
-
end
|
|
361
|
-
end
|
|
246
|
+
# create the temporary path for the salt-minion config file
|
|
247
|
+
debug("sandbox is #{sandbox_path}")
|
|
248
|
+
sandbox_minion_config_path = File.join(sandbox_path, config[:salt_minion_config])
|
|
362
249
|
|
|
363
|
-
|
|
364
|
-
# munge multiline strings
|
|
365
|
-
if !config[:'pillars-from-files'].nil?
|
|
366
|
-
external_pillars = unsymbolize(config[:'pillars-from-files'])
|
|
367
|
-
debug("external_pillars (unsymbolize): #{external_pillars}")
|
|
368
|
-
external_pillars.each do |key, srcfile|
|
|
369
|
-
debug("Copying external pillar: #{key}, #{srcfile}")
|
|
370
|
-
# generate the filename
|
|
371
|
-
sandbox_pillar_path = File.join(sandbox_path, config[:salt_pillar_root], key)
|
|
372
|
-
# create the directory where the pillar file will go
|
|
373
|
-
FileUtils.mkdir_p(File.dirname(sandbox_pillar_path))
|
|
374
|
-
# copy the file across
|
|
375
|
-
FileUtils.copy srcfile, sandbox_pillar_path
|
|
376
|
-
end
|
|
377
|
-
end
|
|
250
|
+
write_raw_file(sandbox_minion_config_path, minion_config_content)
|
|
378
251
|
end
|
|
379
252
|
|
|
380
253
|
def prepare_grains
|
|
@@ -383,101 +256,44 @@ module Kitchen
|
|
|
383
256
|
return if config[:grains].nil?
|
|
384
257
|
|
|
385
258
|
info("Preparing grains into #{config[:salt_config]}/grains")
|
|
386
|
-
# we get a hash with all the keys converted to symbols, salt doesn't like this
|
|
387
|
-
# to convert all the keys back to strings again we use unsymbolize
|
|
388
|
-
# then we convert the hash to yaml
|
|
389
|
-
grains = unsymbolize(config[:grains]).to_yaml
|
|
390
259
|
|
|
391
260
|
# generate the filename
|
|
392
261
|
sandbox_grains_path = File.join(sandbox_path, config[:salt_config], 'grains')
|
|
393
262
|
debug("sandbox_grains_path: #{sandbox_grains_path}")
|
|
394
263
|
|
|
395
|
-
|
|
396
|
-
FileUtils.mkdir_p(File.dirname(sandbox_grains_path))
|
|
397
|
-
|
|
398
|
-
debug("Rendered grains yaml")
|
|
399
|
-
# create the directory & drop the file in
|
|
400
|
-
File.open(sandbox_grains_path, "wb") do |file|
|
|
401
|
-
file.write(grains)
|
|
402
|
-
end
|
|
264
|
+
write_hash_file(sandbox_grains_path, config[:grains])
|
|
403
265
|
end
|
|
404
266
|
|
|
405
|
-
def
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
formula_dir = File.join(sandbox_path, config[:salt_file_root], formula)
|
|
410
|
-
FileUtils.mkdir_p(formula_dir)
|
|
411
|
-
cp_r_with_filter(File.join(path, formula), formula_dir, config[:salt_copy_filter])
|
|
412
|
-
|
|
413
|
-
# copy across the _modules etc directories for python implementation
|
|
414
|
-
['_modules', '_states', '_grains', '_renderers', '_returners'].each do |extrapath|
|
|
415
|
-
src = File.join(path, extrapath)
|
|
416
|
-
|
|
417
|
-
if (File.directory?(src))
|
|
418
|
-
debug("prepare_formula: #{src} exists, copying..")
|
|
419
|
-
extrapath_dir = File.join(sandbox_path, config[:salt_file_root], extrapath)
|
|
420
|
-
FileUtils.mkdir_p(extrapath_dir)
|
|
421
|
-
#FileUtils.cp_r(Dir.glob(File.join(src, "*")), extrapath_dir)
|
|
422
|
-
cp_r_with_filter(src, extrapath_dir, config[:salt_copy_filter])
|
|
423
|
-
else
|
|
424
|
-
debug("prepare_formula: #{src} doesn't exist, skipping.")
|
|
425
|
-
end
|
|
426
|
-
end
|
|
427
|
-
end
|
|
267
|
+
def prepare_dependencies
|
|
268
|
+
# upload scripts
|
|
269
|
+
sandbox_scripts_path = File.join(sandbox_path, config[:salt_config], 'scripts')
|
|
270
|
+
info("Preparing scripts into #{config[:salt_config]}/scripts")
|
|
428
271
|
|
|
429
|
-
|
|
430
|
-
info("Preparing state collection")
|
|
431
|
-
debug("Using config #{config}")
|
|
272
|
+
# PLACEHOLDER, git formulas might be fetched locally to temp and uploaded
|
|
432
273
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
274
|
+
# setup spm
|
|
275
|
+
spm_repos = config[:vendor_repo].select{|x| x[:type]=='spm'}.each{|x| x[:url]}.map {|x| x[:url] }
|
|
276
|
+
spm_repos.each do |url|
|
|
277
|
+
id=url.gsub(/[htp:\/.]/,'')
|
|
278
|
+
spmreposd = File.join(sandbox_path, 'etc', 'salt', 'spm.repos.d')
|
|
279
|
+
repo_spec = File.join(spmreposd, 'spm.repo')
|
|
280
|
+
FileUtils.mkdir_p(spmreposd)
|
|
281
|
+
repo_content = '
|
|
282
|
+
#{id}:
|
|
283
|
+
url: #{url}
|
|
442
284
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
collection_dir = File.join(sandbox_path, config[:salt_file_root], config[:collection_name])
|
|
447
|
-
FileUtils.mkdir_p(collection_dir)
|
|
448
|
-
cp_r_with_filter(config[:kitchen_root], collection_dir, config[:salt_copy_filter])
|
|
449
|
-
|
|
450
|
-
end
|
|
285
|
+
'
|
|
286
|
+
write_raw_file(repo_spec, repo_content)
|
|
287
|
+
end
|
|
451
288
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
debug("cp_r_with_filter:filter = #{filter}")
|
|
456
|
-
|
|
457
|
-
Array(source_path).each do |source_path|
|
|
458
|
-
Find.find(source_path) do |source|
|
|
459
|
-
target = source.sub(/^#{source_path}/, target_path)
|
|
460
|
-
debug("cp_r_with_filter:source = #{source}")
|
|
461
|
-
debug("cp_r_with_filter:target = #{target}")
|
|
462
|
-
filtered = filter.include?(File.basename(source))
|
|
463
|
-
if File.directory? source
|
|
464
|
-
if filtered
|
|
465
|
-
debug("Found #{source} in #{filter}, pruning it from the Find")
|
|
466
|
-
Find.prune
|
|
467
|
-
end
|
|
468
|
-
FileUtils.mkdir target unless File.exists? target
|
|
469
|
-
if File.symlink? source
|
|
470
|
-
FileUtils.cp_r "#{source}/.", target
|
|
471
|
-
end
|
|
472
|
-
else
|
|
473
|
-
if filtered
|
|
474
|
-
debug("Found #{source} in #{filter}, not copying file")
|
|
475
|
-
else
|
|
476
|
-
FileUtils.copy source, target
|
|
477
|
-
end
|
|
478
|
-
end
|
|
479
|
-
end
|
|
289
|
+
# upload scripts
|
|
290
|
+
%w(formula-fetch.sh repository-setup.sh).each do |script|
|
|
291
|
+
write_raw_file(File.join(sandbox_path, script), File.read(File.expand_path("../#{script}", __FILE__)))
|
|
480
292
|
end
|
|
293
|
+
dependencies_script = File.expand_path("./../dependencies.erb", __FILE__)
|
|
294
|
+
dependencies_content = ERB.new(File.read(dependencies_script)).result(binding)
|
|
295
|
+
write_raw_file(File.join(sandbox_path, 'dependencies.sh'), dependencies_content)
|
|
296
|
+
|
|
481
297
|
end
|
|
482
298
|
end
|
|
483
299
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kitchen-salt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.25
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Simon McCartney
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2017-06-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: test-kitchen
|
|
@@ -73,7 +73,16 @@ executables: []
|
|
|
73
73
|
extensions: []
|
|
74
74
|
extra_rdoc_files: []
|
|
75
75
|
files:
|
|
76
|
+
- lib/kitchen-salt/pillars.rb
|
|
77
|
+
- lib/kitchen-salt/states.rb
|
|
78
|
+
- lib/kitchen-salt/util.rb
|
|
76
79
|
- lib/kitchen-salt/version.rb
|
|
80
|
+
- lib/kitchen/provisioner/dependencies.erb
|
|
81
|
+
- lib/kitchen/provisioner/formula-fetch.sh
|
|
82
|
+
- lib/kitchen/provisioner/install.erb
|
|
83
|
+
- lib/kitchen/provisioner/install_win.erb
|
|
84
|
+
- lib/kitchen/provisioner/minion.erb
|
|
85
|
+
- lib/kitchen/provisioner/repository-setup.sh
|
|
77
86
|
- lib/kitchen/provisioner/salt_solo.rb
|
|
78
87
|
homepage: https://github.com//kitchen-salt
|
|
79
88
|
licenses:
|
|
@@ -95,7 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
95
104
|
version: '0'
|
|
96
105
|
requirements: []
|
|
97
106
|
rubyforge_project: "[none]"
|
|
98
|
-
rubygems_version: 2.
|
|
107
|
+
rubygems_version: 2.6.12
|
|
99
108
|
signing_key:
|
|
100
109
|
specification_version: 4
|
|
101
110
|
summary: salt provisioner for test-kitchen
|