vagabond 0.1.4 → 0.2.0
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.
- data/CHANGELOG.md +12 -0
- data/README.md +48 -48
- data/USAGE.md +197 -0
- data/bin/vagabond +24 -2
- data/lib/vagabond/actions/create.rb +14 -5
- data/lib/vagabond/actions/destroy.rb +10 -8
- data/lib/vagabond/actions/freeze.rb +2 -1
- data/lib/vagabond/actions/provision.rb +6 -3
- data/lib/vagabond/actions/rebuild.rb +6 -5
- data/lib/vagabond/actions/ssh.rb +3 -2
- data/lib/vagabond/actions/start.rb +2 -1
- data/lib/vagabond/actions/status.rb +19 -3
- data/lib/vagabond/actions/thaw.rb +2 -1
- data/lib/vagabond/actions/up.rb +16 -3
- data/lib/vagabond/constants.rb +36 -0
- data/lib/vagabond/cookbooks/lxc/libraries/lxc.rb +5 -4
- data/lib/vagabond/cookbooks/lxc/metadata.rb +2 -2
- data/lib/vagabond/cookbooks/lxc/providers/container.rb +0 -1
- data/lib/vagabond/cookbooks/vagabond/attributes/default.rb +5 -0
- data/lib/vagabond/cookbooks/vagabond/recipes/default.rb +32 -9
- data/lib/vagabond/helpers/cheffile_loader.rb +20 -0
- data/lib/vagabond/helpers.rb +66 -1
- data/lib/vagabond/internal_configuration.rb +43 -19
- data/lib/vagabond/kitchen.rb +311 -0
- data/lib/vagabond/knife.rb +22 -17
- data/lib/vagabond/server.rb +42 -32
- data/lib/vagabond/vagabond.rb +147 -55
- data/lib/vagabond/vagabondfile.rb +31 -6
- data/lib/vagabond/version.rb +1 -1
- data/vagabond.gemspec +3 -0
- metadata +54 -4
- data/lib/vagabond/commands.rb +0 -87
- data/lib/vagabond/config.rb +0 -7
@@ -224,7 +224,7 @@ class Lxc
|
|
224
224
|
|
225
225
|
# Stop the container
|
226
226
|
def stop
|
227
|
-
run_command("#{sudo}lxc-stop -n #{name}")
|
227
|
+
run_command("#{sudo}lxc-stop -n #{name}", :allow_failure_retry => 3)
|
228
228
|
run_command("#{sudo}lxc-wait -n #{name} -s STOPPED", :allow_failure_retry => 2)
|
229
229
|
end
|
230
230
|
|
@@ -276,13 +276,14 @@ class Lxc
|
|
276
276
|
shlout.run_command
|
277
277
|
shlout.error!
|
278
278
|
rescue Mixlib::ShellOut::ShellCommandFailed, CommandFailed, Mixlib::ShellOut::CommandTimeout
|
279
|
-
if(
|
280
|
-
true
|
281
|
-
elsif(retries > 0)
|
279
|
+
if(retries > 0)
|
282
280
|
Chef::Log.warn "LXC run command failed: #{cmd}"
|
283
281
|
Chef::Log.warn "Retrying command. #{args[:allow_failure_retry].to_i - retries} of #{args[:allow_failure_retry].to_i} retries remain"
|
282
|
+
sleep(0.3)
|
284
283
|
retries -= 1
|
285
284
|
retry
|
285
|
+
elsif(args[:allow_failure])
|
286
|
+
true
|
286
287
|
else
|
287
288
|
raise
|
288
289
|
end
|
@@ -5,7 +5,7 @@ description "Chef driven Linux Containers"
|
|
5
5
|
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
|
6
6
|
version "0.1.1"
|
7
7
|
|
8
|
-
supports 'ubuntu'
|
8
|
+
supports 'ubuntu'
|
9
9
|
|
10
|
-
|
10
|
+
suggests 'omnibus_updater'
|
11
11
|
suggests 'bridger'
|
@@ -1,4 +1,8 @@
|
|
1
|
+
default[:vagabond][:bases][:ubuntu_1004][:template] = 'ubuntu'
|
2
|
+
default[:vagabond][:bases][:ubuntu_1004][:template_options] = {'--release' => 'lucid'}
|
3
|
+
default[:vagabond][:bases][:ubuntu_1204][:template] = 'ubuntu'
|
1
4
|
default[:vagabond][:bases][:ubuntu_1204][:template_options] = {'--release' => 'precise'}
|
5
|
+
default[:vagabond][:bases][:ubuntu_1210][:template] = 'ubuntu'
|
2
6
|
default[:vagabond][:bases][:ubuntu_1210][:template_options] = {'--release' => 'quantal'}
|
3
7
|
default[:vagabond][:bases][:centos_58][:template] = 'centos'
|
4
8
|
default[:vagabond][:bases][:centos_58][:template_options] = {'--release' => '5', '--releaseminor' => '8'}
|
@@ -10,3 +14,4 @@ default[:vagabond][:bases][:debian_6][:template] = 'debian'
|
|
10
14
|
default[:vagabond][:bases][:debian_6][:create_environment] = {'SUITE' => 'squeeze'}
|
11
15
|
default[:vagabond][:bases][:debian_7][:template] = 'debian'
|
12
16
|
default[:vagabond][:bases][:debian_7][:create_environment] = {'SUITE' => 'wheezy'}
|
17
|
+
default[:vagabond][:customs] = {}
|
@@ -1,8 +1,23 @@
|
|
1
|
-
include_recipe 'lxc
|
1
|
+
include_recipe 'lxc'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
ruby_block 'LXC template: lxc-centos' do
|
4
|
+
block do
|
5
|
+
dir = %w(/usr/share /usr/lib).map do |prefix|
|
6
|
+
if(File.directory?(d = File.join(prefix, 'lxc/templates')))
|
7
|
+
d
|
8
|
+
end
|
9
|
+
end.compact.first
|
10
|
+
raise 'Failed to locate LXC template directory' unless dir
|
11
|
+
cfl = Chef::Resource::CookbookFile.new(
|
12
|
+
::File.join(dir, 'lxc-centos'),
|
13
|
+
run_context
|
14
|
+
)
|
15
|
+
cfl.source 'lxc-centos'
|
16
|
+
cfl.mode 0755
|
17
|
+
cfl.cookbook cookbook_name.to_s
|
18
|
+
cfl.action :nothing
|
19
|
+
cfl.run_action(:create)
|
20
|
+
end
|
6
21
|
end
|
7
22
|
|
8
23
|
node[:vagabond][:bases].each do |name, options|
|
@@ -14,9 +29,9 @@ node[:vagabond][:bases].each do |name, options|
|
|
14
29
|
'upgrade -y -q',
|
15
30
|
'install curl -y -q'
|
16
31
|
]
|
17
|
-
if(%
|
32
|
+
if(!options[:template].scan(%r{debian|ubuntu}).empty?)
|
18
33
|
pkg_man = 'apt-get'
|
19
|
-
elsif(%
|
34
|
+
elsif(!options[:template].scan(%r{fedora|centos}).empty?)
|
20
35
|
pkg_man = 'yum'
|
21
36
|
end
|
22
37
|
if(pkg_man)
|
@@ -40,14 +55,22 @@ node[:vagabond][:bases].each do |name, options|
|
|
40
55
|
'curl -L https://www.opscode.com/chef/install.sh | bash'
|
41
56
|
]
|
42
57
|
end
|
58
|
+
end
|
59
|
+
|
60
|
+
node[:vagabond][:customs].each do |name, options|
|
43
61
|
|
62
|
+
lxc_container name do
|
63
|
+
action :clone
|
64
|
+
base_container options[:base]
|
65
|
+
end
|
66
|
+
|
44
67
|
if(options[:memory])
|
45
68
|
lxc_config name do
|
46
69
|
cgroup(
|
47
|
-
'memory.limit_in_bytes' => options[:memory][:
|
70
|
+
'memory.limit_in_bytes' => options[:memory][:ram],
|
48
71
|
'memory.memsw.limit_in_bytes' => (
|
49
|
-
Vagabond.get_bytes(options[:memory][:
|
50
|
-
Vagabond.get_bytes(options[:memory][:
|
72
|
+
Vagabond.get_bytes(options[:memory][:ram]) +
|
73
|
+
Vagabond.get_bytes(options[:memory][:swap])
|
51
74
|
)
|
52
75
|
)
|
53
76
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Vagabond
|
2
|
+
class CheffileLoader
|
3
|
+
|
4
|
+
attr_reader :cookbooks
|
5
|
+
|
6
|
+
def initialize(path=nil)
|
7
|
+
@cookbooks = []
|
8
|
+
load(path) if path
|
9
|
+
end
|
10
|
+
|
11
|
+
def cookbook(name, *args)
|
12
|
+
cookbooks[name] = args
|
13
|
+
end
|
14
|
+
|
15
|
+
def load(path)
|
16
|
+
instance_eval(File.read(path))
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/lib/vagabond/helpers.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'vagabond/constants'
|
2
|
+
|
1
3
|
module Vagabond
|
2
4
|
module Helpers
|
3
5
|
private
|
@@ -11,7 +13,70 @@ module Vagabond
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def debug(s)
|
14
|
-
ui.info "#{ui.color('DEBUG:', :red, :bold)} #{s}" if
|
16
|
+
ui.info "#{ui.color('DEBUG:', :red, :bold)} #{s}" if options[:debug]
|
17
|
+
end
|
18
|
+
|
19
|
+
def generated_name(n=nil)
|
20
|
+
n = name unless n
|
21
|
+
if(@_gn.nil? || @_gn[n].nil?)
|
22
|
+
@_gn ||= Mash.new
|
23
|
+
s = Digest::MD5.new
|
24
|
+
s << @vagabondfile.path
|
25
|
+
@_gn[n] = "#{n}-#{s.hexdigest}"
|
26
|
+
end
|
27
|
+
@_gn[n]
|
28
|
+
end
|
29
|
+
|
30
|
+
def setup_ui(ui=nil)
|
31
|
+
unless(ui)
|
32
|
+
Chef::Config[:color] = options[:color].nil? ? true : options[:color]
|
33
|
+
@ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
|
34
|
+
else
|
35
|
+
@ui = ui
|
36
|
+
end
|
37
|
+
options[:debug] = STDOUT if options[:debug]
|
38
|
+
self.class.ui = @ui
|
39
|
+
end
|
40
|
+
|
41
|
+
def execute
|
42
|
+
if(public_methods.include?(@action.to_sym))
|
43
|
+
send(@action)
|
44
|
+
else
|
45
|
+
ui.error "Invalid action received: #{@action}"
|
46
|
+
exit EXIT_CODES[:invalid_action]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def generate_hash
|
51
|
+
Digest::MD5.hexdigest(@vagabondfile.path)
|
52
|
+
end
|
53
|
+
|
54
|
+
def direct_container_command(command, args={})
|
55
|
+
_lxc = args[:lxc] || lxc
|
56
|
+
com = "#{sudo}ssh root@#{lxc.container_ip} -i /opt/hw-lxc-config/id_rsa -oStrictHostKeyChecking=no '#{command}'"
|
57
|
+
debug(com)
|
58
|
+
begin
|
59
|
+
cmd = Mixlib::ShellOut.new(com,
|
60
|
+
:live_stream => args[:live_stream] || options[:debug],
|
61
|
+
:timeout => args[:timeout] || 1200
|
62
|
+
)
|
63
|
+
cmd.run_command
|
64
|
+
cmd.error!
|
65
|
+
true
|
66
|
+
rescue
|
67
|
+
raise if args[:raise_on_failure]
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class << self
|
73
|
+
def included(klass)
|
74
|
+
klass.class_eval do
|
75
|
+
class << self
|
76
|
+
attr_accessor :ui
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
15
80
|
end
|
16
81
|
|
17
82
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'digest/sha2'
|
2
|
+
require 'json'
|
2
3
|
require 'vagabond/helpers'
|
4
|
+
require 'vagabond/constants'
|
3
5
|
|
4
6
|
module Vagabond
|
5
7
|
class InternalConfiguration
|
@@ -8,14 +10,26 @@ module Vagabond
|
|
8
10
|
|
9
11
|
attr_reader :config
|
10
12
|
attr_reader :ui
|
13
|
+
attr_reader :options
|
14
|
+
attr_accessor :force_bases
|
11
15
|
|
12
|
-
def initialize(
|
13
|
-
@
|
14
|
-
@config = Mash.new(:mappings => Mash.new)
|
16
|
+
def initialize(vagabondfile, ui, options, args={})
|
17
|
+
@vagabondfile = vagabondfile
|
15
18
|
@checksums = Mash.new
|
16
19
|
@ui = ui
|
20
|
+
@options = options
|
17
21
|
create_store
|
18
22
|
load_existing
|
23
|
+
@config = Mash.new(
|
24
|
+
:mappings => Mash.new,
|
25
|
+
:template_mappings => Mash.new,
|
26
|
+
:test_mappings => Mash.new
|
27
|
+
).merge(config)
|
28
|
+
@force_bases = args[:force_bases] || []
|
29
|
+
ensure_state
|
30
|
+
end
|
31
|
+
|
32
|
+
def ensure_state
|
19
33
|
store_checksums
|
20
34
|
write_dna_json
|
21
35
|
write_solo_rb
|
@@ -41,13 +55,15 @@ module Vagabond
|
|
41
55
|
File.read(path)
|
42
56
|
)
|
43
57
|
)
|
58
|
+
else
|
59
|
+
@config = Mash.new
|
44
60
|
end
|
45
61
|
end
|
46
62
|
|
47
63
|
def store_path
|
48
64
|
FileUtils.mkdir_p(
|
49
65
|
File.join(
|
50
|
-
File.dirname(@
|
66
|
+
File.dirname(@vagabondfile.path), '.vagabond'
|
51
67
|
)
|
52
68
|
)
|
53
69
|
end
|
@@ -61,26 +77,33 @@ module Vagabond
|
|
61
77
|
end
|
62
78
|
|
63
79
|
def write_dna_json
|
64
|
-
conf = Mash.new
|
65
|
-
@
|
66
|
-
conf[t] = Mash.new(:enabled => true)
|
80
|
+
conf = Mash.new(:bases => Mash.new, :customs => Mash.new)
|
81
|
+
(Array(@vagabondfile[:nodes]).map(&:last).map{|i| i[:template]}.compact + Array(force_bases)).uniq.each do |t|
|
82
|
+
conf[:bases][t] = Mash.new(:enabled => true) if BASE_TEMPLATES.include?(t.to_s)
|
67
83
|
end
|
68
|
-
|
69
|
-
|
70
|
-
conf[
|
71
|
-
|
84
|
+
Array(@vagabondfile[:templates]).each do |t_name, opts|
|
85
|
+
if(BASE_TEMPLATES.include?(opts[:base].to_s))
|
86
|
+
conf[:bases][opts[:base]] = Mash.new(:enabled => true)
|
87
|
+
if(opts.has_key?(:memory) && !opts[:memory].is_a?(Hash))
|
88
|
+
opts[:memory][:ram] = opts[:memory].to_s
|
89
|
+
end
|
90
|
+
conf[:customs][generated_name(t_name)] = opts
|
91
|
+
config[:template_mappings][t_name] = generated_name(t_name)
|
92
|
+
else
|
93
|
+
ui.fatal "Invalid base template encountered: #{t}"
|
94
|
+
ui.info ui.color(" -> Valid base templates: #{BASE_TEMPLATES.sort.join(', ')}", :red)
|
95
|
+
exit EXIT_CODES[:invalid_base_template]
|
72
96
|
end
|
73
97
|
end
|
74
98
|
File.open(dna_path, 'w') do |file|
|
75
99
|
file.write(
|
76
100
|
JSON.dump(
|
77
|
-
:vagabond =>
|
78
|
-
:bases => conf
|
79
|
-
},
|
101
|
+
:vagabond => conf,
|
80
102
|
:run_list => %w(recipe[vagabond])
|
81
103
|
)
|
82
104
|
)
|
83
105
|
end
|
106
|
+
save
|
84
107
|
end
|
85
108
|
|
86
109
|
def write_solo_rb
|
@@ -106,9 +129,9 @@ module Vagabond
|
|
106
129
|
end
|
107
130
|
|
108
131
|
def solo_needed?
|
109
|
-
if(
|
132
|
+
if(options[:force_solo])
|
110
133
|
true
|
111
|
-
elsif(
|
134
|
+
elsif(options[:disable_solo])
|
112
135
|
false
|
113
136
|
else
|
114
137
|
[dna_path, solo_path].detect do |path|
|
@@ -133,13 +156,14 @@ module Vagabond
|
|
133
156
|
end
|
134
157
|
|
135
158
|
def run_solo
|
136
|
-
ui.info ui.color('Ensuring expected system state (creating required
|
159
|
+
ui.info ui.color('Ensuring expected system state (creating required base containers)', :yellow)
|
137
160
|
ui.info ui.color(' - This can take a while...', :yellow)
|
138
|
-
com = "#{
|
161
|
+
com = "#{options[:sudo]}chef-solo -j #{File.join(store_path, 'dna.json')} -c #{File.join(store_path, 'solo.rb')}"
|
139
162
|
debug(com)
|
140
|
-
cmd = Mixlib::ShellOut.new(com, :timeout =>
|
163
|
+
cmd = Mixlib::ShellOut.new(com, :timeout => 12000, :live_stream => options[:debug])
|
141
164
|
cmd.run_command
|
142
165
|
cmd.error!
|
166
|
+
ui.info ui.color(' -> COMPLETE!', :yellow)
|
143
167
|
end
|
144
168
|
|
145
169
|
def save
|
@@ -0,0 +1,311 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'chef'
|
3
|
+
require 'kitchen/busser'
|
4
|
+
|
5
|
+
%w(helpers vagabondfile vagabond server helpers/cheffile_loader).each do |dep|
|
6
|
+
require "vagabond/#{dep}"
|
7
|
+
end
|
8
|
+
|
9
|
+
module Vagabond
|
10
|
+
class Kitchen < Thor
|
11
|
+
|
12
|
+
include Thor::Actions
|
13
|
+
include Helpers
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def basename
|
17
|
+
'vagabond kitchen'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
self.class_exec(&Vagabond::CLI_OPTIONS)
|
22
|
+
|
23
|
+
attr_reader :kitchen
|
24
|
+
attr_reader :platform_map
|
25
|
+
attr_reader :vagabondfile
|
26
|
+
attr_reader :ui
|
27
|
+
attr_reader :name
|
28
|
+
attr_reader :action
|
29
|
+
|
30
|
+
def initialize(*args)
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'teardown COOKBOOK', 'Destroy containers related to COOKBOOK test'
|
35
|
+
method_option(:platform,
|
36
|
+
:type => :string,
|
37
|
+
:desc => 'Specify platform to destroy'
|
38
|
+
)
|
39
|
+
method_option(:suite,
|
40
|
+
:type => :string,
|
41
|
+
:desc => 'Specify suite to destroy'
|
42
|
+
)
|
43
|
+
def teardown(cookbook)
|
44
|
+
ui.info "#{ui.color('Vagabond:', :bold)} - Kitchen teardown for cookbook #{ui.color(name, :red)}"
|
45
|
+
plats = [platform || options[:platform] || platform_map.keys].flatten
|
46
|
+
plats.each do |plat|
|
47
|
+
validate_platform!(plat)
|
48
|
+
ui.info ui.color(" -> Tearing down platform: #{plat}", :red)
|
49
|
+
vagabond_instance(:destroy, plat).send(:execute)
|
50
|
+
ui.info ui.color(" -> Teardown of platform: #{plat} - COMPLETE!", :red)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'test COOKBOOK', 'Run test kitchen on COOKBOOK'
|
55
|
+
method_option(:platform,
|
56
|
+
:type => :string,
|
57
|
+
:desc => 'Specify platform to test'
|
58
|
+
)
|
59
|
+
method_option(:cluster,
|
60
|
+
:type => :string,
|
61
|
+
:desc => 'Specify cluster to test'
|
62
|
+
)
|
63
|
+
method_option(:teardown,
|
64
|
+
:type => :boolean,
|
65
|
+
:default => true,
|
66
|
+
:desc => 'Teardown nodes automatically after testing'
|
67
|
+
)
|
68
|
+
method_option(:parallel,
|
69
|
+
:type => :boolean,
|
70
|
+
:default => false,
|
71
|
+
:desc => 'Build test nodes in parallel'
|
72
|
+
)
|
73
|
+
method_option(:suites,
|
74
|
+
:type => :string,
|
75
|
+
:desc => 'Specify suites to test [suite1,suite2,...]'
|
76
|
+
)
|
77
|
+
def test(cookbook)
|
78
|
+
setup(cookbook, :test)
|
79
|
+
ui.info "#{ui.color('Vagabond:', :bold)} - Kitchen testing for cookbook #{ui.color(name, :cyan)}"
|
80
|
+
results = Mash.new
|
81
|
+
platforms = [options[:platform] || platform_map.keys].flatten
|
82
|
+
if(options[:cluster])
|
83
|
+
ui.info ui.color(" -> Cluster Testing #{options[:cluster]}!", :yellow)
|
84
|
+
if(kitchen[:clusters].nil? || kitchen[:clusters][options[:cluster]].nil?)
|
85
|
+
ui.fatal "Requested cluster is not defined: #{options[:cluster]}"
|
86
|
+
exit EXIT_CODES[:cluster_invalid]
|
87
|
+
end
|
88
|
+
serv = Server.new
|
89
|
+
serv.options = options
|
90
|
+
serv.auto_upload # upload everything : make optional?
|
91
|
+
suites = kitchen[:clusters][options[:cluster]]
|
92
|
+
platforms.each do |platform|
|
93
|
+
%w(local_server_provision test destroy).each do |action|
|
94
|
+
suites.each do |suite_name|
|
95
|
+
res = self.send("#{action}_node", platform, suite_name)
|
96
|
+
if(action == 'test')
|
97
|
+
results[platform] ||=[]
|
98
|
+
results[platform] << {
|
99
|
+
:suite_name => suite_name,
|
100
|
+
:result => res
|
101
|
+
}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
else
|
107
|
+
suites = options[:suites] ? options[:suites].split(',') : ['default']
|
108
|
+
platforms.each do |platform|
|
109
|
+
suites.each do |suite_name|
|
110
|
+
provision_node(platform, suite_name)
|
111
|
+
results[platform] ||= []
|
112
|
+
results[platform] << {
|
113
|
+
:suite_name => suite_name,
|
114
|
+
:result => test_node(platform, suite_name)
|
115
|
+
}
|
116
|
+
destroy_node(platform, suite_name)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
ui.info ui.color('Kitchen Test Results:', :bold)
|
121
|
+
results.each do |platform, infos|
|
122
|
+
ui.info " Platform: #{ui.color(platform, :blue, :bold)}"
|
123
|
+
infos.each do |res|
|
124
|
+
ui.info " Suite: #{res[:suite_name]} -> #{res[:result] ? ui.color('SUCCESS!', :green) : ui.color('FAILED!', :red)}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
protected
|
130
|
+
|
131
|
+
def local_server_provision_node(platform, suite_name)
|
132
|
+
run_list = generate_runlist(platform, suite_name)
|
133
|
+
v_inst = vagabond_instance(:up, platform, :suite_name => suite_name, :run_list => run_list)
|
134
|
+
raise "ERROR! No local chef!" unless v_inst.options[:knife_opts]
|
135
|
+
v_inst.send(:execute)
|
136
|
+
end
|
137
|
+
|
138
|
+
# TODO: Handle failed provision!
|
139
|
+
def provision_node(platform, suite_name)
|
140
|
+
run_list = generate_runlist(platform, suite_name)
|
141
|
+
ui.info ui.color(" -> Provisioning suite #{suite_name} on platform: #{platform}", :cyan)
|
142
|
+
v_inst = vagabond_instance(:create, platform, :suite_name => suite_name)
|
143
|
+
v_inst.send(:execute)
|
144
|
+
solo_path = configure_for(v_inst.name, platform, suite_name, run_list, :dna, :cookbooks)
|
145
|
+
v_inst.send(:provision_solo, solo_path)
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_node(platform, suite_name)
|
149
|
+
v_inst = vagabond_instance(:create, platform, :suite_name => suite_name)
|
150
|
+
busser = bus_node(v_inst, suite_name)
|
151
|
+
ui.info "#{ui.color('Kitchen:', :bold)} Running tests..."
|
152
|
+
cmd = busser.run_cmd
|
153
|
+
res = cmd.to_s.empty? ? true : v_inst.send(:direct_container_command, cmd, :live_stream => STDOUT)
|
154
|
+
ui.info "\n -> #{ui.color('Testing', :bold, :cyan)} #{name} suite #{suite_name} on platform #{platform}: #{res ? ui.color('SUCCESS!', :green, :bold) : ui.color('FAILED', :red)}"
|
155
|
+
res
|
156
|
+
end
|
157
|
+
|
158
|
+
def destroy_node(platform, suite_name)
|
159
|
+
if(options[:teardown])
|
160
|
+
v_inst = vagabond_instance(:destroy, platform, :suite_name => suite_name)
|
161
|
+
v_inst.send(:execute)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def setup(name, action)
|
166
|
+
@options = options.dup
|
167
|
+
@vagabondfile = Vagabondfile.new(options[:vagabond_file])
|
168
|
+
setup_ui
|
169
|
+
@name = name
|
170
|
+
@action = action
|
171
|
+
load_kitchen_yml
|
172
|
+
end
|
173
|
+
|
174
|
+
def configure_for(l_name, platform, suite_name, runlist, *args)
|
175
|
+
dir = File.join(File.dirname(vagabondfile.path), ".vagabond/node_configs/#{l_name}")
|
176
|
+
FileUtils.mkdir_p(dir)
|
177
|
+
_args = [args.include?(:integration) ? :integration : nil].compact
|
178
|
+
write_dna(l_name, suite_name, dir, platform, runlist, *_args) if args.include?(:dna)
|
179
|
+
load_cookbooks(l_name, suite_name, dir, platform, runlist, *_args) if args.include?(:cookbooks)
|
180
|
+
write_solo_config(dir) if args.include?(:cookbooks) && !args.include?(:integration)
|
181
|
+
dir
|
182
|
+
end
|
183
|
+
|
184
|
+
def write_solo_config(dir)
|
185
|
+
File.open(File.join(dir, 'solo.rb'), 'w') do |file|
|
186
|
+
file.write("cookbook_path '#{File.join(dir, 'cookbooks')}'\n")
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def write_dna(l_name, suite_name, dir, platform, runlist, *args)
|
191
|
+
key = args.include?(:integration) ? :integration_suites : :suites
|
192
|
+
dna = Mash.new
|
193
|
+
dna.merge!(platform_map[platform][:attributes] || {})
|
194
|
+
s_args = kitchen[:suites].detect{|s|s[:name] == suite_name}
|
195
|
+
if(s_args)
|
196
|
+
dna.merge!(s_args)
|
197
|
+
end
|
198
|
+
dna[:run_list] = runlist
|
199
|
+
File.open(File.join(dir, 'dna.json'), 'w') do |file|
|
200
|
+
file.write(JSON.dump(dna))
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def cookbook_path
|
205
|
+
Chef::CookbookLoader.new(
|
206
|
+
File.join(File.dirname(vagabondfile.path), 'cookbooks')
|
207
|
+
).load_cookbooks[name].root_dir
|
208
|
+
end
|
209
|
+
|
210
|
+
def load_cookbooks(l_name, suite_name, dir, platform, runlist, *_args)
|
211
|
+
contents = ['site "http://community.opscode.com/api/v1"']
|
212
|
+
contents << "cookbook '#{name}', :path => '#{cookbook_path}'"
|
213
|
+
contents << "cookbook 'minitest-handler'"
|
214
|
+
# TODO - Customs from kitchen. Customs from root. Customs from cookbook
|
215
|
+
File.open(File.join(dir, 'Cheffile'), 'w') do |file|
|
216
|
+
file.write(contents.join("\n"))
|
217
|
+
end
|
218
|
+
com = "librarian-chef update"
|
219
|
+
debug(com)
|
220
|
+
c = Mixlib::ShellOut.new(com, :live_stream => options[:debug], :cwd => dir)
|
221
|
+
c.run_command
|
222
|
+
c.error!
|
223
|
+
end
|
224
|
+
|
225
|
+
def bus_node(v_inst, suite_name)
|
226
|
+
unless(::Kitchen::Busser::DEFAULT_TEST_ROOT == c_path = File.join(cookbook_path, 'test/integration'))
|
227
|
+
::Kitchen::Busser.send(:remove_const, :DEFAULT_TEST_ROOT)
|
228
|
+
::Kitchen::Busser.const_set(:DEFAULT_TEST_ROOT, File.join(cookbook_path, 'test/integration'))
|
229
|
+
end
|
230
|
+
busser = ::Kitchen::Busser.new(suite_name)
|
231
|
+
ui.info "#{ui.color('Kitchen:', :bold)} Setting up..."
|
232
|
+
%w(setup_cmd sync_cmd).each do |cmd|
|
233
|
+
com = busser.send(cmd)
|
234
|
+
next if com.to_s.empty?
|
235
|
+
v_inst.send(:direct_container_command, com)
|
236
|
+
end
|
237
|
+
busser
|
238
|
+
end
|
239
|
+
|
240
|
+
def vagabond_instance(action, platform, args={})
|
241
|
+
options[:disable_name_validate] = true
|
242
|
+
v = Vagabond.new
|
243
|
+
v.options = options
|
244
|
+
v.send(:setup, action, [name, platform, args[:suite_name]].compact.join('-'),
|
245
|
+
:ui => ui,
|
246
|
+
:template => platform_map[platform][:template],
|
247
|
+
:disable_name_validate => true,
|
248
|
+
:ui => ui
|
249
|
+
)
|
250
|
+
v.internal_config.force_bases = platform_map[platform][:template]
|
251
|
+
v.internal_config.ensure_state
|
252
|
+
v.mappings_key = :test_mappings
|
253
|
+
v.config = Mash.new(
|
254
|
+
:template => platform_map[platform][:template],
|
255
|
+
:run_list => args[:run_list]
|
256
|
+
)
|
257
|
+
v.lxc = Lxc.new(
|
258
|
+
v.internal_config[v.mappings_key][v.name]
|
259
|
+
) if v.internal_config[v.mappings_key][v.name]
|
260
|
+
v
|
261
|
+
end
|
262
|
+
|
263
|
+
def load_kitchen_yml
|
264
|
+
y_path = File.join(
|
265
|
+
File.dirname(vagabondfile.path), 'cookbooks', name, '.kitchen.yml'
|
266
|
+
)
|
267
|
+
if(File.exists?(y_path))
|
268
|
+
@kitchen = Mash.new(YAML.load(File.read(y_path)))
|
269
|
+
else
|
270
|
+
ui.fatal "Cookbook #{name} does not have a .kitchen.yml file defined!"
|
271
|
+
ui.info ui.color(" -> Path: #{y_path}", :red)
|
272
|
+
exit EXIT_CODES[:kitchen_missing_yml]
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def platform_map
|
277
|
+
@platform_map ||= Mash.new(Hash[*(
|
278
|
+
kitchen[:platforms].map do |plat|
|
279
|
+
[
|
280
|
+
plat[:name], Mash.new(
|
281
|
+
:template => plat[:driver_config][:box].scan(
|
282
|
+
%r{([^-]+-[^-]+)$}
|
283
|
+
).flatten.first.to_s.gsub('.', '').gsub('-', '_'),
|
284
|
+
:run_list => plat[:run_list],
|
285
|
+
:attributes => plat[:attributes]
|
286
|
+
)
|
287
|
+
]
|
288
|
+
end.flatten
|
289
|
+
)])
|
290
|
+
end
|
291
|
+
|
292
|
+
def generate_runlist(platform, suite)
|
293
|
+
r = platform_map[platform][:run_list]
|
294
|
+
kitchen_suite = kitchen[:suites].detect do |k_s|
|
295
|
+
k_s[:name] == suite
|
296
|
+
end
|
297
|
+
if(kitchen_suite && kitchen_suite[:run_list])
|
298
|
+
r |= kitchen_suite[:run_list]
|
299
|
+
end
|
300
|
+
r.uniq
|
301
|
+
end
|
302
|
+
|
303
|
+
def validate_platform!(plat)
|
304
|
+
unless(platform_map[plat])
|
305
|
+
ui.fatal "Requested platform does not exist: #{ui.color(plat, :red)}"
|
306
|
+
ui.info " -> Available platforms: #{platform_map.keys.sort.join(', ')}"
|
307
|
+
exit EXIT_CODES[:kitchen_invalid_platform]
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|