beaker 1.0.0 → 1.0.1.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/beaker.gemspec +18 -18
- data/lib/beaker/cli.rb +16 -0
- data/lib/beaker/dsl/helpers.rb +53 -13
- data/lib/beaker/host.rb +9 -15
- data/lib/beaker/host/unix/file.rb +4 -0
- data/lib/beaker/hypervisor/vagrant.rb +42 -31
- data/lib/beaker/hypervisor/vcloud.rb +22 -12
- data/lib/beaker/network_manager.rb +15 -9
- data/lib/beaker/options/command_line_parser.rb +4 -0
- data/lib/beaker/options/parser.rb +1 -1
- data/lib/beaker/test_case.rb +11 -1
- data/spec/beaker/dsl/helpers_spec.rb +33 -0
- data/spec/beaker/hypervisor/vagrant_spec.rb +35 -12
- data/spec/beaker/options/command_line_parser_spec.rb +2 -2
- metadata +57 -43
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ODg5ZjNlOGYzYmRlZWViOTc4OWQzYjViYmEzZTdkMDNmZDYzODUxZA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
Mjg2YmJhNDE1OTdlMWE4OTg1YTVkOGVhNTYzMjg1ZGNlYzlmYzdhOQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MmIwNzlmODFjYjNhMjNkNDU4NTZkOTY1MDdjMTUxODM3YTEyZmMxZGFhNmNj
|
10
|
+
ZjgwYTNkYjU3MzQ5Yjk2NTY5OWYxYThmZDZlNjk5Mjg4NWIwYjcyY2E1OGI2
|
11
|
+
YWY3NTVjYzU5YzEzZTIyOWJmNjZmNTFjOGQ1NjJkODQ2OTU5YWY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZGFjYzQ0NWRiYzViYjNkNDRlY2JiM2RjM2U2ZDM1MjBmNTYzYzliYTJhMDFk
|
14
|
+
Y2U5ODlkMmUyYjRmZjc3MDU5NzAxMTc3YmFhZGEwZmFmYWY2MGQxMjRlZTIx
|
15
|
+
M2RhZDdlYThiMmU3MzI5MzVkODBhYWIzMTMxM2IzOWM1YTExYTk=
|
data/beaker.gemspec
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require 'rbconfig'
|
4
|
-
ruby_conf = defined?(RbConfig) ? RbConfig::CONFIG : Config::CONFIG
|
5
|
-
less_than_one_nine = ruby_conf['MAJOR'].to_i == 1 && ruby_conf['MINOR'].to_i < 9
|
6
2
|
|
7
3
|
Gem::Specification.new do |s|
|
8
4
|
s.name = "beaker"
|
9
|
-
s.version = '1.0.
|
5
|
+
s.version = '1.0.1.pre'
|
10
6
|
s.authors = ["Puppetlabs"]
|
11
7
|
s.email = ["delivery@puppetlabs.com"]
|
12
8
|
s.homepage = "https://github.com/puppetlabs/beaker"
|
@@ -23,24 +19,28 @@ Gem::Specification.new do |s|
|
|
23
19
|
s.add_development_dependency 'rspec', '~> 2.14.0'
|
24
20
|
s.add_development_dependency 'fakefs', '0.4'
|
25
21
|
s.add_development_dependency 'rake'
|
26
|
-
s.add_development_dependency 'simplecov' unless
|
22
|
+
s.add_development_dependency 'simplecov' unless RUBY_VERSION < '1.9'
|
27
23
|
|
28
24
|
# Documentation dependencies
|
29
25
|
s.add_development_dependency 'yard'
|
30
|
-
s.add_development_dependency 'markdown' unless
|
26
|
+
s.add_development_dependency 'markdown' unless RUBY_VERSION < '1.9'
|
31
27
|
s.add_development_dependency 'thin'
|
32
28
|
|
33
29
|
# Run time dependencies
|
34
|
-
s.add_runtime_dependency 'json'
|
35
|
-
s.add_runtime_dependency 'net-ssh'
|
36
|
-
s.add_runtime_dependency 'net-scp'
|
37
|
-
s.add_runtime_dependency '
|
38
|
-
|
30
|
+
s.add_runtime_dependency 'json', '~> 1.8'
|
31
|
+
s.add_runtime_dependency 'net-ssh', '~> 2.6'
|
32
|
+
s.add_runtime_dependency 'net-scp', '~> 1.1'
|
33
|
+
s.add_runtime_dependency 'inifile', '~> 2.0'
|
34
|
+
|
35
|
+
# Optional provisioner specific support
|
36
|
+
s.add_runtime_dependency 'rbvmomi', '~> 1.6'
|
37
|
+
s.add_runtime_dependency 'blimpy', '~> 0.6'
|
38
|
+
s.add_runtime_dependency 'fission', '~> 0.4'
|
39
|
+
|
40
|
+
# These are transitive dependencies that we include or pin to because...
|
41
|
+
# Ruby 1.8 compatibility
|
39
42
|
s.add_runtime_dependency 'nokogiri', '1.5.10'
|
40
|
-
s.add_runtime_dependency 'mime-types', '1.25'
|
41
|
-
|
42
|
-
s.add_runtime_dependency '
|
43
|
-
#unf is an 'optional' fog dependency, but it warns when it is missing
|
44
|
-
# see https://github.com/fog/fog/pull/2320/commits
|
45
|
-
s.add_runtime_dependency 'unf'
|
43
|
+
s.add_runtime_dependency 'mime-types', '~> 1.25' # 2.0 won't install on 1.8
|
44
|
+
# So fog doesn't always complain of unmet AWS dependencies
|
45
|
+
s.add_runtime_dependency 'unf', '~> 0.1'
|
46
46
|
end
|
data/lib/beaker/cli.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
module Beaker
|
2
2
|
class CLI
|
3
|
+
GEMSPEC = File.join(File.expand_path(File.dirname(__FILE__)), "../../beaker.gemspec")
|
4
|
+
VERSION_STRING =
|
5
|
+
" wWWWw
|
6
|
+
|o o|
|
7
|
+
| O | %s!
|
8
|
+
|(\")|
|
9
|
+
/ \\X/ \\
|
10
|
+
| V |
|
11
|
+
| | | "
|
12
|
+
|
3
13
|
def initialize
|
4
14
|
@options_parser = Beaker::Options::Parser.new
|
5
15
|
@options = @options_parser.parse_args
|
@@ -10,6 +20,12 @@ module Beaker
|
|
10
20
|
@logger.notify(@options_parser.usage)
|
11
21
|
exit
|
12
22
|
end
|
23
|
+
if @options[:version]
|
24
|
+
require 'rubygems' unless defined?(Gem)
|
25
|
+
spec = Gem::Specification::load(GEMSPEC)
|
26
|
+
@logger.notify(VERSION_STRING % spec.version)
|
27
|
+
exit
|
28
|
+
end
|
13
29
|
@logger.notify(@options.dump)
|
14
30
|
|
15
31
|
#add additional paths to the LOAD_PATH
|
data/lib/beaker/dsl/helpers.rb
CHANGED
@@ -449,7 +449,11 @@ module Beaker
|
|
449
449
|
if host.is_pe?
|
450
450
|
bounce_service( host, 'pe-httpd' )
|
451
451
|
else
|
452
|
-
|
452
|
+
if puppet_master_started
|
453
|
+
stop_puppet_from_source_on( host )
|
454
|
+
else
|
455
|
+
dump_puppet_log(host)
|
456
|
+
end
|
453
457
|
end
|
454
458
|
|
455
459
|
rescue Exception => teardown_exception
|
@@ -474,22 +478,43 @@ module Beaker
|
|
474
478
|
# @!visibility private
|
475
479
|
def restore_puppet_conf_from_backup( host, backup_file )
|
476
480
|
puppetpath = host['puppetpath']
|
481
|
+
puppet_conf = File.join(puppetpath, "puppet.conf")
|
482
|
+
|
483
|
+
if backup_file
|
484
|
+
host.exec( Command.new( "if [ -f '#{backup_file}' ]; then " +
|
485
|
+
"cat '#{backup_file}' > " +
|
486
|
+
"'#{puppet_conf}'; " +
|
487
|
+
"rm -f '#{backup_file}'; " +
|
488
|
+
"fi" ) )
|
489
|
+
else
|
490
|
+
host.exec( Command.new( "rm -f '#{puppet_conf}'" ))
|
491
|
+
end
|
477
492
|
|
478
|
-
host.exec( Command.new( "if [ -f '#{backup_file}' ]; then " +
|
479
|
-
"cat '#{backup_file}' > " +
|
480
|
-
"'#{puppetpath}/puppet.conf'; " +
|
481
|
-
"rm -f '#{backup_file}'; " +
|
482
|
-
"fi" ) )
|
483
493
|
end
|
484
494
|
|
495
|
+
# Back up the given file in the current_dir to the new_dir
|
496
|
+
#
|
485
497
|
# @!visibility private
|
498
|
+
#
|
499
|
+
# @param host [Beaker::Host] The target host
|
500
|
+
# @param current_dir [String] The directory containing the file to back up
|
501
|
+
# @param new_dir [String] The directory to copy the file to
|
502
|
+
# @param filename [String] The file to back up. Defaults to 'puppet.conf'
|
503
|
+
#
|
504
|
+
# @return [String, nil] The path to the file if the file exists, nil if it
|
505
|
+
# doesn't exist.
|
486
506
|
def backup_the_file host, current_dir, new_dir, filename = 'puppet.conf'
|
507
|
+
|
487
508
|
old_location = current_dir + '/' + filename
|
488
509
|
new_location = new_dir + '/' + filename + '.bak'
|
489
510
|
|
490
|
-
host.
|
491
|
-
|
492
|
-
|
511
|
+
if host.file_exist? old_location
|
512
|
+
host.exec( Command.new( "cp #{old_location} #{new_location}" ) )
|
513
|
+
return new_location
|
514
|
+
else
|
515
|
+
logger.warn "Could not backup file '#{old_location}': no such file"
|
516
|
+
nil
|
517
|
+
end
|
493
518
|
end
|
494
519
|
|
495
520
|
# @!visibility private
|
@@ -498,7 +523,6 @@ module Beaker
|
|
498
523
|
|
499
524
|
logger.debug 'Waiting for the puppet master to start'
|
500
525
|
unless port_open_within?( host, 8140, 10 )
|
501
|
-
dump_puppet_log(host)
|
502
526
|
raise Beaker::DSL::FailTest, 'Puppet master did not start in a timely fashion'
|
503
527
|
end
|
504
528
|
logger.debug 'The puppet master has started'
|
@@ -515,9 +539,6 @@ module Beaker
|
|
515
539
|
sleep 1
|
516
540
|
end
|
517
541
|
end
|
518
|
-
rescue RuntimeError => e
|
519
|
-
dump_puppet_log host
|
520
|
-
raise e
|
521
542
|
end
|
522
543
|
|
523
544
|
# @!visibility private
|
@@ -834,6 +855,25 @@ module Beaker
|
|
834
855
|
sign_certificate_for(default)
|
835
856
|
end
|
836
857
|
|
858
|
+
# Get a facter fact from a provided host
|
859
|
+
#
|
860
|
+
# @param [Host] host The host to query the fact for
|
861
|
+
# @param [String] name The name of the fact to query for
|
862
|
+
# @!macro common_opts
|
863
|
+
#
|
864
|
+
# @returns String The value of the fact 'name' on the provided host
|
865
|
+
# @raise [FailTest] Raises an exception if call to facter fails
|
866
|
+
def fact_on(host, name, opts = {})
|
867
|
+
result = on host, facter(name, opts)
|
868
|
+
result.stdout.chomp if result.stdout
|
869
|
+
end
|
870
|
+
|
871
|
+
# Get a facter fact from the default host
|
872
|
+
# @see #fact_on
|
873
|
+
def fact(name, opts = {})
|
874
|
+
fact_on(default, name, opts)
|
875
|
+
end
|
876
|
+
|
837
877
|
end
|
838
878
|
end
|
839
879
|
end
|
data/lib/beaker/host.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'socket'
|
2
|
+
require 'timeout'
|
2
3
|
|
3
4
|
%w(command ssh_connection).each do |lib|
|
4
5
|
begin
|
@@ -12,6 +13,8 @@ module Beaker
|
|
12
13
|
class Host
|
13
14
|
SELECT_TIMEOUT = 30
|
14
15
|
|
16
|
+
class CommandFailure < StandardError; end
|
17
|
+
|
15
18
|
# This class providers array syntax for using puppet --configprint on a host
|
16
19
|
class PuppetConfigReader
|
17
20
|
def initialize(host, command)
|
@@ -63,23 +66,14 @@ module Beaker
|
|
63
66
|
end
|
64
67
|
|
65
68
|
def port_open? port
|
66
|
-
s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
67
|
-
sa = Socket.sockaddr_in(port, reachable_name)
|
68
|
-
|
69
69
|
begin
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
begin
|
74
|
-
s.connect_nonblock(sa)
|
75
|
-
rescue Errno::EISCONN
|
76
|
-
return true
|
77
|
-
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
78
|
-
return false
|
79
|
-
end
|
70
|
+
Timeout.timeout SELECT_TIMEOUT do
|
71
|
+
TCPSocket.new(reachable_name, port).close
|
72
|
+
return true
|
80
73
|
end
|
74
|
+
rescue Errno::ECONNREFUSED, Timeout::Error
|
75
|
+
return false
|
81
76
|
end
|
82
|
-
return false
|
83
77
|
end
|
84
78
|
|
85
79
|
def up?
|
@@ -171,7 +165,7 @@ module Beaker
|
|
171
165
|
# is it necessary to break execution??
|
172
166
|
unless result.exit_code_in?(Array(options[:acceptable_exit_codes] || 0))
|
173
167
|
limit = 10
|
174
|
-
raise "Host '#{self}' exited with #{result.exit_code} running:\n #{cmdline}\nLast #{limit} lines of output were:\n#{result.formatted_output(limit)}"
|
168
|
+
raise CommandFailure, "Host '#{self}' exited with #{result.exit_code} running:\n #{cmdline}\nLast #{limit} lines of output were:\n#{result.formatted_output(limit)}"
|
175
169
|
end
|
176
170
|
end
|
177
171
|
# Danger, so we have to return this result?
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'open3'
|
2
2
|
|
3
|
-
module Beaker
|
3
|
+
module Beaker
|
4
4
|
class Vagrant < Beaker::Hypervisor
|
5
5
|
|
6
6
|
# Return a random mac address
|
@@ -13,7 +13,7 @@ module Beaker
|
|
13
13
|
def rand_chunk
|
14
14
|
(2 + rand(252)).to_s #don't want a 0, 1, or a 255
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
def randip
|
18
18
|
"10.255.#{rand_chunk}.#{rand_chunk}"
|
19
19
|
end
|
@@ -21,25 +21,24 @@ module Beaker
|
|
21
21
|
def make_vfile hosts
|
22
22
|
#HACK HACK HACK - add checks here to ensure that we have box + box_url
|
23
23
|
#generate the VagrantFile
|
24
|
-
|
24
|
+
v_file = "Vagrant.configure(\"2\") do |c|\n"
|
25
25
|
hosts.each do |host|
|
26
26
|
host['ip'] ||= randip #use the existing ip, otherwise default to a random ip
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
v_file << " c.vm.define '#{host.name}' do |v|\n"
|
28
|
+
v_file << " v.vm.hostname = '#{host.name}'\n"
|
29
|
+
v_file << " v.vm.box = '#{host['box']}'\n"
|
30
|
+
v_file << " v.vm.box_url = '#{host['box_url']}'\n" unless host['box_url'].nil?
|
31
|
+
v_file << " v.vm.base_mac = '#{randmac}'\n"
|
32
|
+
v_file << " v.vm.network :private_network, ip: \"#{host['ip'].to_s}\", :netmask => \"255.255.0.0\"\n"
|
33
|
+
v_file << " end\n"
|
34
34
|
@logger.debug "created Vagrantfile for VagrantHost #{host.name}"
|
35
35
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
f.write(vagrant_file)
|
36
|
+
v_file << " c.vm.provider :virtualbox do |vb|\n"
|
37
|
+
v_file << " vb.customize [\"modifyvm\", :id, \"--memory\", \"1024\"]\n"
|
38
|
+
v_file << " end\n"
|
39
|
+
v_file << "end\n"
|
40
|
+
File.open(@vagrant_file, 'w') do |f|
|
41
|
+
f.write(v_file)
|
43
42
|
end
|
44
43
|
end
|
45
44
|
|
@@ -66,13 +65,16 @@ module Beaker
|
|
66
65
|
def set_ssh_config host, user
|
67
66
|
f = Tempfile.new("#{host.name}")
|
68
67
|
ssh_config = Dir.chdir(@vagrant_path) do
|
69
|
-
stdin, stdout = Open3.popen3('vagrant', 'ssh-config', host.name)
|
68
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3('vagrant', 'ssh-config', host.name)
|
69
|
+
if not wait_thr.value.success?
|
70
|
+
raise "Failed to 'vagrant ssh-config' for #{host.name}"
|
71
|
+
end
|
70
72
|
stdout.read
|
71
73
|
end
|
72
74
|
#replace hostname with ip
|
73
|
-
ssh_config = ssh_config.gsub(/#{host.name}/, host['ip'])
|
74
|
-
#set the user
|
75
|
-
ssh_config = ssh_config.gsub(/User vagrant/, "User #{user}")
|
75
|
+
ssh_config = ssh_config.gsub(/#{host.name}/, host['ip']) unless not host['ip']
|
76
|
+
#set the user
|
77
|
+
ssh_config = ssh_config.gsub(/User vagrant/, "User #{user}")
|
76
78
|
f.write(ssh_config)
|
77
79
|
f.rewind
|
78
80
|
host['ssh'] = {:config => f.path()}
|
@@ -86,32 +88,37 @@ module Beaker
|
|
86
88
|
@logger = options[:logger]
|
87
89
|
@temp_files = []
|
88
90
|
@vagrant_hosts = vagrant_hosts
|
89
|
-
@vagrant_path = File.expand_path(File.join(File.basename(__FILE__), '..', 'vagrant_files', options[:hosts_file]))
|
91
|
+
@vagrant_path = File.expand_path(File.join(File.basename(__FILE__), '..', 'vagrant_files', File.basename(options[:hosts_file])))
|
92
|
+
FileUtils.mkdir_p(@vagrant_path)
|
93
|
+
@vagrant_file = File.expand_path(File.join(@vagrant_path, "Vagrantfile"))
|
90
94
|
|
91
95
|
end
|
92
96
|
|
93
97
|
def provision
|
94
|
-
|
98
|
+
if @options[:provision]
|
99
|
+
#setting up new vagrant hosts
|
100
|
+
#make sure that any old boxes are dead dead dead
|
101
|
+
vagrant_cmd("destroy --force") if File.file?(@vagrant_file)
|
95
102
|
|
96
|
-
|
97
|
-
vagrant_cmd("halt")
|
98
|
-
vagrant_cmd("up")
|
103
|
+
make_vfile @vagrant_hosts
|
99
104
|
|
105
|
+
vagrant_cmd("up")
|
106
|
+
end
|
100
107
|
@logger.debug "configure vagrant boxes (set ssh-config, switch to root user, hack etc/hosts)"
|
101
108
|
@vagrant_hosts.each do |host|
|
102
109
|
default_user = host['user']
|
103
|
-
|
110
|
+
|
104
111
|
set_ssh_config host, 'vagrant'
|
105
|
-
|
112
|
+
|
106
113
|
copy_ssh_to_root host
|
107
114
|
#shut down connection, will reconnect on next exec
|
108
|
-
host.close
|
115
|
+
host.close
|
109
116
|
|
110
117
|
set_ssh_config host, default_user
|
111
|
-
|
112
118
|
end
|
113
119
|
|
114
120
|
hack_etc_hosts @vagrant_hosts
|
121
|
+
|
115
122
|
end
|
116
123
|
|
117
124
|
def cleanup
|
@@ -121,11 +128,15 @@ module Beaker
|
|
121
128
|
end
|
122
129
|
@logger.notify "Destroying vagrant boxes"
|
123
130
|
vagrant_cmd("destroy --force")
|
131
|
+
FileUtils.rm_rf(@vagrant_path)
|
124
132
|
end
|
125
133
|
|
126
134
|
def vagrant_cmd(args)
|
127
135
|
Dir.chdir(@vagrant_path) do
|
128
|
-
system("vagrant #{args}")
|
136
|
+
run = system("vagrant #{args}")
|
137
|
+
if not run
|
138
|
+
raise "Failed to execute vagrant_cmd ( #{args} )"
|
139
|
+
end
|
129
140
|
end
|
130
141
|
end
|
131
142
|
|
@@ -157,17 +157,19 @@ module Beaker
|
|
157
157
|
def cleanup
|
158
158
|
@logger.notify "Destroying vCloud boxes"
|
159
159
|
connect_to_vsphere
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
160
|
+
|
161
|
+
vm_names = @vcloud_hosts.map {|h| h['vmhostname'] }.compact
|
162
|
+
if @vcloud_hosts.length != vm_names.length
|
163
|
+
@logger.warn "Some hosts did not have vmhostname set correctly! This likely means VM provisioning was not successful"
|
164
|
+
end
|
165
|
+
vms = @vsphere_helper.find_vms vm_names
|
166
|
+
begin
|
166
167
|
vm_names.each do |name|
|
167
168
|
unless vm = vms[name]
|
168
|
-
|
169
|
+
@logger.warn "Unable to cleanup #{name}, couldn't find VM #{name} in vSphere!"
|
170
|
+
next
|
169
171
|
end
|
170
|
-
|
172
|
+
|
171
173
|
if vm.runtime.powerState == 'poweredOn'
|
172
174
|
@logger.notify "Shutting down #{vm.name}"
|
173
175
|
duration = run_and_report_duration do
|
@@ -175,16 +177,24 @@ module Beaker
|
|
175
177
|
end
|
176
178
|
@logger.notify "Spent %.2f seconds halting #{vm.name}" % duration
|
177
179
|
end
|
178
|
-
|
180
|
+
|
179
181
|
duration = run_and_report_duration do
|
180
182
|
vm.Destroy_Task
|
181
183
|
end
|
182
184
|
@logger.notify "Spent %.2f seconds destroying #{vm.name}" % duration
|
185
|
+
|
186
|
+
end
|
187
|
+
rescue RbVmomi::Fault => ex
|
188
|
+
if ex.fault.is_a?(RbVmomi::VIM::ManagedObjectNotFound)
|
189
|
+
#it's already gone, don't bother trying to delete it
|
190
|
+
name = vms.key(ex.fault.obj)
|
191
|
+
vms.delete(name)
|
192
|
+
vm_names.delete(name)
|
193
|
+
@logger.warn "Unable to destroy #{name}, it was not found in vSphere"
|
194
|
+
retry
|
183
195
|
end
|
184
|
-
rescue => e
|
185
|
-
@vsphere_helper.close
|
186
|
-
report_and_raise(@logger, e, "Vcloud.cleanup")
|
187
196
|
end
|
197
|
+
@vsphere_helper.close
|
188
198
|
end
|
189
199
|
|
190
200
|
end
|
@@ -10,6 +10,17 @@ module Beaker
|
|
10
10
|
class NetworkManager
|
11
11
|
HYPERVISOR_TYPES = ['solaris', 'blimpy', 'vsphere', 'fusion', 'aix', 'vcloud', 'vagrant']
|
12
12
|
|
13
|
+
def provision? options, host
|
14
|
+
#provision this box
|
15
|
+
# - only if we are running with --provision
|
16
|
+
# - only if we have a hypervisor
|
17
|
+
# - only if either the specific hosts has no specification or has 'provision' in its config
|
18
|
+
# - always if it is a vagrant box (vagrant boxes are always provisioned as they always need ssh key hacking)
|
19
|
+
command_line_says = options[:provision]
|
20
|
+
host_says = host['hypervisor'] && (host.has_key?('provision') ? host['provision'] : true)
|
21
|
+
(command_line_says && host_says) or (host['hypervisor'] =~/vagrant/)
|
22
|
+
end
|
23
|
+
|
13
24
|
def initialize(options, logger)
|
14
25
|
@logger = logger
|
15
26
|
@options = options
|
@@ -21,16 +32,11 @@ module Beaker
|
|
21
32
|
def provision
|
22
33
|
#sort hosts into those to be provisioned and those to use non-provisioned
|
23
34
|
@options['HOSTS'].each_key do |name|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
#provision this box
|
28
|
-
# - only if we are running with --provision
|
29
|
-
# - only if we have a hypervisor
|
30
|
-
# - only if either the specific hosts has no specification or has 'provision' in its config
|
31
|
-
if @options[:provision] && hypervisor && (host_info.has_key?('provision') ? host_info['provision'] : true) #obey config file provision, defaults to provisioning vms
|
35
|
+
host = @options['HOSTS'][name]
|
36
|
+
hypervisor = host['hypervisor']
|
37
|
+
if provision?(@options, host)
|
32
38
|
raise "Invalid hypervisor: #{hypervisor} (#{name})" unless HYPERVISOR_TYPES.include? hypervisor
|
33
|
-
@logger.debug "Hypervisor for #{name} is #{
|
39
|
+
@logger.debug "Hypervisor for #{name} is #{hypervisor}"
|
34
40
|
@virtual_machines[hypervisor] = [] unless @virtual_machines[hypervisor]
|
35
41
|
@virtual_machines[hypervisor] << name
|
36
42
|
else #this is a non-provisioned machine, deal with it without hypervisors
|
@@ -157,6 +157,10 @@ module Beaker
|
|
157
157
|
@cmd_options[:add_el_extras] = true
|
158
158
|
end
|
159
159
|
|
160
|
+
opts.on('--version', 'Report currently running version of beaker' ) do
|
161
|
+
@cmd_options[:version] = true
|
162
|
+
end
|
163
|
+
|
160
164
|
opts.on '-c', '--config FILE',
|
161
165
|
'DEPRECATED use --hosts' do |file|
|
162
166
|
@cmd_options[:hosts_file] = file
|
@@ -151,7 +151,7 @@ module Beaker
|
|
151
151
|
# overwrite defaults with command line and file options
|
152
152
|
@options = @options.merge(cmd_line_and_file_options)
|
153
153
|
|
154
|
-
if not @options[:help]
|
154
|
+
if not @options[:help] and not @options[:version]
|
155
155
|
#read the hosts file that contains the node configuration and hypervisor info
|
156
156
|
hosts_options = Beaker::Options::HostsFileParser.parse_hosts_file(@options[:hosts_file])
|
157
157
|
# merge in host file vars
|
data/lib/beaker/test_case.rb
CHANGED
@@ -10,7 +10,18 @@ require 'tempfile'
|
|
10
10
|
require 'benchmark'
|
11
11
|
require 'stringio'
|
12
12
|
require 'rbconfig'
|
13
|
+
#include test/unit, but do not allow it to autorun on exit
|
13
14
|
require 'test/unit'
|
15
|
+
if defined?(Test::Unit::AutoRunner.need_auto_run?)
|
16
|
+
# For test-unit gem >= 2.4.9
|
17
|
+
Test::Unit::AutoRunner.need_auto_run = false
|
18
|
+
elsif defined?(Test::Unit.run?)
|
19
|
+
# For test-unit gem < 2.4.9
|
20
|
+
Test::Unit.run = true
|
21
|
+
elsif defined?(Test::Unit::Runner)
|
22
|
+
# For test/unit bundled in Ruby >= 1.9.3
|
23
|
+
Test::Unit::Runner.module_eval("@@stop_auto_run = true")
|
24
|
+
end
|
14
25
|
|
15
26
|
module Beaker
|
16
27
|
# This class represents a single test case. A test case is necessarily
|
@@ -29,7 +40,6 @@ module Beaker
|
|
29
40
|
rb_config_class = defined?(RbConfig) ? RbConfig : Config
|
30
41
|
if rb_config_class::CONFIG['MAJOR'].to_i == 1 &&
|
31
42
|
rb_config_class::CONFIG['MINOR'].to_i == 8 then
|
32
|
-
Test::Unit.run = true
|
33
43
|
# The Exception raised by Ruby's STDLIB's test framework (Ruby 1.8)
|
34
44
|
TEST_EXCEPTION_CLASS = Test::Unit::AssertionFailedError
|
35
45
|
else
|
@@ -524,6 +524,7 @@ describe ClassMixedWithDSLHelpers do
|
|
524
524
|
def stub_host_and_subject_to_allow_the_default_testdir_argument_to_be_created
|
525
525
|
subject.instance_variable_set(:@path, test_case_path)
|
526
526
|
host.stub(:tmpdir).and_return(tmpdir_path)
|
527
|
+
host.stub(:file_exist?).and_return(true)
|
527
528
|
end
|
528
529
|
|
529
530
|
before do
|
@@ -535,6 +536,13 @@ describe ClassMixedWithDSLHelpers do
|
|
535
536
|
expect { subject.with_puppet_running_on(host, '--foo --bar') }.to raise_error(ArgumentError, /conf_opts must be a Hash. You provided a String: '--foo --bar'/)
|
536
537
|
end
|
537
538
|
|
539
|
+
it 'raises the early_exception if backup_the_file fails' do
|
540
|
+
subject.should_receive(:backup_the_file).and_raise(RuntimeError.new('puppet conf backup failed'))
|
541
|
+
expect {
|
542
|
+
subject.with_puppet_running_on(host, {})
|
543
|
+
}.to raise_error(RuntimeError, /puppet conf backup failed/)
|
544
|
+
end
|
545
|
+
|
538
546
|
describe "with valid arguments" do
|
539
547
|
before do
|
540
548
|
Tempfile.should_receive(:open).with('beaker')
|
@@ -623,6 +631,12 @@ describe ClassMixedWithDSLHelpers do
|
|
623
631
|
subject.with_puppet_running_on(host, {})
|
624
632
|
expect(host).to execute_commands_matching(/cat '#{backup_location}' > '#{original_location}'/).once
|
625
633
|
end
|
634
|
+
|
635
|
+
it "doesn't restore a non-existent file" do
|
636
|
+
subject.stub(:backup_the_file)
|
637
|
+
subject.with_puppet_running_on(host, {})
|
638
|
+
expect(host).to execute_commands_matching(/rm -f '#{original_location}'/)
|
639
|
+
end
|
626
640
|
end
|
627
641
|
|
628
642
|
describe 'handling failures' do
|
@@ -662,4 +676,23 @@ describe ClassMixedWithDSLHelpers do
|
|
662
676
|
|
663
677
|
end
|
664
678
|
end
|
679
|
+
|
680
|
+
describe '#fact_on' do
|
681
|
+
it 'retreives a fact on host(s)' do
|
682
|
+
subject.should_receive(:facter).with('osfamily',{}).once
|
683
|
+
subject.should_receive(:on).and_return(result)
|
684
|
+
|
685
|
+
subject.fact_on('host','osfamily')
|
686
|
+
end
|
687
|
+
end
|
688
|
+
|
689
|
+
describe '#fact' do
|
690
|
+
it 'delegates to #fact_on with the default host' do
|
691
|
+
subject.stub(:hosts).and_return(hosts)
|
692
|
+
subject.should_receive(:fact_on).with(master,"osfamily",{}).once
|
693
|
+
|
694
|
+
subject.fact('osfamily')
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
665
698
|
end
|
@@ -66,8 +66,12 @@ module Beaker
|
|
66
66
|
PasswordAuthentication no
|
67
67
|
IdentityFile /home/root/.vagrant.d/insecure_private_key
|
68
68
|
IdentitiesOnly yes")
|
69
|
+
wait_thr = OpenStruct.new
|
70
|
+
state = mock( 'state' )
|
71
|
+
state.stub( :success? ).and_return( true )
|
72
|
+
wait_thr.value = state
|
69
73
|
|
70
|
-
Open3.stub( :popen3 ).with( 'vagrant', 'ssh-config', host.name ).and_return( [ "", out ])
|
74
|
+
Open3.stub( :popen3 ).with( 'vagrant', 'ssh-config', host.name ).and_return( [ "", out, "", wait_thr ])
|
71
75
|
|
72
76
|
file = double( 'file' )
|
73
77
|
file.stub( :path ).and_return( '/path/sshconfig' )
|
@@ -82,22 +86,41 @@ module Beaker
|
|
82
86
|
|
83
87
|
end
|
84
88
|
|
85
|
-
|
89
|
+
describe "provisioning and cleanup" do
|
90
|
+
|
91
|
+
before :each do
|
92
|
+
FakeFS.activate!
|
93
|
+
vagrant.should_receive( :vagrant_cmd ).with( "up" ).once
|
94
|
+
@hosts.each do |host|
|
95
|
+
host_prev_name = host['user']
|
96
|
+
vagrant.should_receive( :set_ssh_config ).with( host, 'vagrant' ).once
|
97
|
+
vagrant.should_receive( :copy_ssh_to_root ).with( host ).once
|
98
|
+
vagrant.should_receive( :set_ssh_config ).with( host, host_prev_name ).once
|
99
|
+
end
|
100
|
+
vagrant.should_receive( :hack_etc_hosts ).with( @hosts ).once
|
101
|
+
end
|
86
102
|
|
87
|
-
|
103
|
+
it "can provision a set of hosts" do
|
104
|
+
vagrant.should_receive( :make_vfile ).with( @hosts ).once
|
105
|
+
vagrant.should_receive( :vagrant_cmd ).with( "destroy --force" ).never
|
106
|
+
vagrant.provision
|
107
|
+
end
|
88
108
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
vagrant.should_receive( :set_ssh_config ).with( host, 'vagrant' ).once
|
94
|
-
vagrant.should_receive( :copy_ssh_to_root ).with( host ).once
|
95
|
-
vagrant.should_receive( :set_ssh_config ).with( host, host_prev_name ).once
|
109
|
+
it "destroys an existing set of hosts before provisioning" do
|
110
|
+
vagrant.make_vfile(@hosts)
|
111
|
+
vagrant.should_receive(:vagrant_cmd).with("destroy --force").once
|
112
|
+
vagrant.provision
|
96
113
|
end
|
97
|
-
vagrant.should_receive( :hack_etc_hosts ).with( @hosts ).once
|
98
114
|
|
115
|
+
it "can cleanup" do
|
116
|
+
vagrant.should_receive( :vagrant_cmd ).with( "destroy --force" ).once
|
117
|
+
FileUtils.should_receive( :rm_rf ).once
|
118
|
+
|
119
|
+
vagrant.provision
|
120
|
+
vagrant.cleanup
|
121
|
+
|
122
|
+
end
|
99
123
|
|
100
|
-
vagrant.provision
|
101
124
|
end
|
102
125
|
|
103
126
|
end
|
@@ -6,14 +6,14 @@ module Beaker
|
|
6
6
|
|
7
7
|
let(:parser) {Beaker::Options::CommandLineParser.new}
|
8
8
|
let(:test_opts) {["-h", "vcloud.cfg", "--debug", "--tests", "test.rb", "--help"]}
|
9
|
-
let(:full_opts) {["--hosts", "host.cfg", "--options", "opts_file", "--type", "pe", "--helper", "path_to_helper", "--load-path", "load_path", "--tests", "test1.rb,test2.rb,test3.rb", "--pre-suite", "pre_suite.rb", "--post-suite", "post_suite.rb", "--no-provision", "--preserve-hosts", "--root-keys", "--keyfile", "../.ssh/id_rsa", "--install", "gitrepopath", "-m", "module", "-q", "--no-xml", "--dry-run", "--no-ntp", "--repo-proxy", "--add-el-extras", "--config", "anotherfile.cfg", "--fail-mode", "fast", "--no-color"]}
|
9
|
+
let(:full_opts) {["--hosts", "host.cfg", "--options", "opts_file", "--type", "pe", "--helper", "path_to_helper", "--load-path", "load_path", "--tests", "test1.rb,test2.rb,test3.rb", "--pre-suite", "pre_suite.rb", "--post-suite", "post_suite.rb", "--no-provision", "--preserve-hosts", "--root-keys", "--keyfile", "../.ssh/id_rsa", "--install", "gitrepopath", "-m", "module", "-q", "--no-xml", "--dry-run", "--no-ntp", "--repo-proxy", "--add-el-extras", "--config", "anotherfile.cfg", "--fail-mode", "fast", "--no-color", "--version"]}
|
10
10
|
|
11
11
|
it "can correctly read command line input" do
|
12
12
|
expect(parser.parse!(test_opts)).to be === {:hosts_file=>"vcloud.cfg", :debug=>true, :tests=>"test.rb", :help=>true}
|
13
13
|
end
|
14
14
|
|
15
15
|
it "supports all our command line options" do
|
16
|
-
expect(parser.parse!(full_opts)).to be === {:hosts_file=>"anotherfile.cfg", :options_file=>"opts_file", :type=>"pe", :helper=>"path_to_helper", :load_path=>"load_path", :tests=>"test1.rb,test2.rb,test3.rb", :pre_suite=>"pre_suite.rb", :post_suite=>"post_suite.rb", :provision=>false, :preserve_hosts=>true, :root_keys=>true, :keyfile=>"../.ssh/id_rsa", :install=>"gitrepopath", :modules=>"module", :quiet=>true, :xml=>false, :dry_run=>true, :timesync=>false, :repo_proxy=>true, :add_el_extras=>true, :fail_mode=>"fast", :color=>false}
|
16
|
+
expect(parser.parse!(full_opts)).to be === {:hosts_file=>"anotherfile.cfg", :options_file=>"opts_file", :type=>"pe", :helper=>"path_to_helper", :load_path=>"load_path", :tests=>"test1.rb,test2.rb,test3.rb", :pre_suite=>"pre_suite.rb", :post_suite=>"post_suite.rb", :provision=>false, :preserve_hosts=>true, :root_keys=>true, :keyfile=>"../.ssh/id_rsa", :install=>"gitrepopath", :modules=>"module", :quiet=>true, :xml=>false, :dry_run=>true, :timesync=>false, :repo_proxy=>true, :add_el_extras=>true, :fail_mode=>"fast", :color=>false, :version=>true}
|
17
17
|
end
|
18
18
|
|
19
19
|
it "can produce a usage description" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: beaker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppetlabs
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -112,128 +112,142 @@ dependencies:
|
|
112
112
|
name: json
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - ~>
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '1.8'
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- -
|
122
|
+
- - ~>
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '1.8'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: net-ssh
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- -
|
129
|
+
- - ~>
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '
|
131
|
+
version: '2.6'
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- -
|
136
|
+
- - ~>
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '
|
138
|
+
version: '2.6'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: net-scp
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- -
|
143
|
+
- - ~>
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: '
|
145
|
+
version: '1.1'
|
146
146
|
type: :runtime
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
|
-
- -
|
150
|
+
- - ~>
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: '
|
152
|
+
version: '1.1'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: inifile
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ~>
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '2.0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ~>
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '2.0'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
168
|
name: rbvmomi
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
156
170
|
requirements:
|
157
|
-
- -
|
171
|
+
- - ~>
|
158
172
|
- !ruby/object:Gem::Version
|
159
|
-
version: '
|
173
|
+
version: '1.6'
|
160
174
|
type: :runtime
|
161
175
|
prerelease: false
|
162
176
|
version_requirements: !ruby/object:Gem::Requirement
|
163
177
|
requirements:
|
164
|
-
- -
|
178
|
+
- - ~>
|
165
179
|
- !ruby/object:Gem::Version
|
166
|
-
version: '
|
180
|
+
version: '1.6'
|
167
181
|
- !ruby/object:Gem::Dependency
|
168
182
|
name: blimpy
|
169
183
|
requirement: !ruby/object:Gem::Requirement
|
170
184
|
requirements:
|
171
|
-
- -
|
185
|
+
- - ~>
|
172
186
|
- !ruby/object:Gem::Version
|
173
|
-
version: '0'
|
187
|
+
version: '0.6'
|
174
188
|
type: :runtime
|
175
189
|
prerelease: false
|
176
190
|
version_requirements: !ruby/object:Gem::Requirement
|
177
191
|
requirements:
|
178
|
-
- -
|
192
|
+
- - ~>
|
179
193
|
- !ruby/object:Gem::Version
|
180
|
-
version: '0'
|
194
|
+
version: '0.6'
|
181
195
|
- !ruby/object:Gem::Dependency
|
182
|
-
name:
|
196
|
+
name: fission
|
183
197
|
requirement: !ruby/object:Gem::Requirement
|
184
198
|
requirements:
|
185
|
-
- -
|
199
|
+
- - ~>
|
186
200
|
- !ruby/object:Gem::Version
|
187
|
-
version:
|
201
|
+
version: '0.4'
|
188
202
|
type: :runtime
|
189
203
|
prerelease: false
|
190
204
|
version_requirements: !ruby/object:Gem::Requirement
|
191
205
|
requirements:
|
192
|
-
- -
|
206
|
+
- - ~>
|
193
207
|
- !ruby/object:Gem::Version
|
194
|
-
version:
|
208
|
+
version: '0.4'
|
195
209
|
- !ruby/object:Gem::Dependency
|
196
|
-
name:
|
210
|
+
name: nokogiri
|
197
211
|
requirement: !ruby/object:Gem::Requirement
|
198
212
|
requirements:
|
199
|
-
- -
|
213
|
+
- - '='
|
200
214
|
- !ruby/object:Gem::Version
|
201
|
-
version:
|
215
|
+
version: 1.5.10
|
202
216
|
type: :runtime
|
203
217
|
prerelease: false
|
204
218
|
version_requirements: !ruby/object:Gem::Requirement
|
205
219
|
requirements:
|
206
|
-
- -
|
220
|
+
- - '='
|
207
221
|
- !ruby/object:Gem::Version
|
208
|
-
version:
|
222
|
+
version: 1.5.10
|
209
223
|
- !ruby/object:Gem::Dependency
|
210
|
-
name:
|
224
|
+
name: mime-types
|
211
225
|
requirement: !ruby/object:Gem::Requirement
|
212
226
|
requirements:
|
213
|
-
- -
|
227
|
+
- - ~>
|
214
228
|
- !ruby/object:Gem::Version
|
215
|
-
version: '
|
229
|
+
version: '1.25'
|
216
230
|
type: :runtime
|
217
231
|
prerelease: false
|
218
232
|
version_requirements: !ruby/object:Gem::Requirement
|
219
233
|
requirements:
|
220
|
-
- -
|
234
|
+
- - ~>
|
221
235
|
- !ruby/object:Gem::Version
|
222
|
-
version: '
|
236
|
+
version: '1.25'
|
223
237
|
- !ruby/object:Gem::Dependency
|
224
238
|
name: unf
|
225
239
|
requirement: !ruby/object:Gem::Requirement
|
226
240
|
requirements:
|
227
|
-
- -
|
241
|
+
- - ~>
|
228
242
|
- !ruby/object:Gem::Version
|
229
|
-
version: '0'
|
243
|
+
version: '0.1'
|
230
244
|
type: :runtime
|
231
245
|
prerelease: false
|
232
246
|
version_requirements: !ruby/object:Gem::Requirement
|
233
247
|
requirements:
|
234
|
-
- -
|
248
|
+
- - ~>
|
235
249
|
- !ruby/object:Gem::Version
|
236
|
-
version: '0'
|
250
|
+
version: '0.1'
|
237
251
|
description: Puppetlabs accceptance testing harness
|
238
252
|
email:
|
239
253
|
- delivery@puppetlabs.com
|
@@ -387,9 +401,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
387
401
|
version: '0'
|
388
402
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
389
403
|
requirements:
|
390
|
-
- - ! '
|
404
|
+
- - ! '>'
|
391
405
|
- !ruby/object:Gem::Version
|
392
|
-
version:
|
406
|
+
version: 1.3.1
|
393
407
|
requirements: []
|
394
408
|
rubyforge_project:
|
395
409
|
rubygems_version: 2.0.6
|