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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZWQyZDI5MDhjOTdlYjFmMWM0NmMyNzVjYWYyZTMxMjZkNjViMTk1Mg==
4
+ ODg5ZjNlOGYzYmRlZWViOTc4OWQzYjViYmEzZTdkMDNmZDYzODUxZA==
5
5
  data.tar.gz: !binary |-
6
- ODYxMGI2Y2E3NDc3NTViMzBhYjRhMGNlNDkxNDljMmYyMDcwNmZjYw==
6
+ Mjg2YmJhNDE1OTdlMWE4OTg1YTVkOGVhNTYzMjg1ZGNlYzlmYzdhOQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- YmViMDgxMTgwZjQ3NDk5M2NhZDkwYzAwMDA0MDNlMGE4NDA1NDI4YjM4ZTkw
10
- NTFhMDBhMTczMzZmMTgzMmE2YmIxNzlhMDY3MzAyMGVkY2YyMGVmMmU0Njdh
11
- ZThlYWE5NzY2MDZiZTg1ZTk1OTZhZmFiZDcwZTk3ZDdlOGJiYjA=
9
+ MmIwNzlmODFjYjNhMjNkNDU4NTZkOTY1MDdjMTUxODM3YTEyZmMxZGFhNmNj
10
+ ZjgwYTNkYjU3MzQ5Yjk2NTY5OWYxYThmZDZlNjk5Mjg4NWIwYjcyY2E1OGI2
11
+ YWY3NTVjYzU5YzEzZTIyOWJmNjZmNTFjOGQ1NjJkODQ2OTU5YWY=
12
12
  data.tar.gz: !binary |-
13
- ZDFiODE1ZjRlNjkzNDE5ODA4NmUxYmU3MmExZTQ4NDA4MTNjYzg3NjY1MmI3
14
- YWNhYmJmZDcyNjM3ZGRiNDJlOGFmYzNmNTI1YjY5NWIwNDAyMzJhNjMwYjVk
15
- MzUyMmI2YWQ3ODFjNWMyOTRkNjJmOTUwNjBmMDgwMzY5NmI5Mjg=
13
+ ZGFjYzQ0NWRiYzViYjNkNDRlY2JiM2RjM2U2ZDM1MjBmNTYzYzliYTJhMDFk
14
+ Y2U5ODlkMmUyYjRmZjc3MDU5NzAxMTc3YmFhZGEwZmFmYWY2MGQxMjRlZTIx
15
+ M2RhZDdlYThiMmU3MzI5MzVkODBhYWIzMTMxM2IzOWM1YTExYTk=
@@ -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.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 less_than_one_nine
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 less_than_one_nine
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 'rbvmomi'
38
- s.add_runtime_dependency 'blimpy'
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' if RUBY_VERSION < "1.9"
41
- s.add_runtime_dependency 'fission' if RUBY_PLATFORM =~ /darwin/i
42
- s.add_runtime_dependency 'inifile'
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
@@ -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
@@ -449,7 +449,11 @@ module Beaker
449
449
  if host.is_pe?
450
450
  bounce_service( host, 'pe-httpd' )
451
451
  else
452
- stop_puppet_from_source_on( host ) if puppet_master_started
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.exec( Command.new( "cp #{old_location} #{new_location}" ) )
491
-
492
- return new_location
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
@@ -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
- s.connect_nonblock(sa)
71
- rescue Errno::EINPROGRESS
72
- if IO.select(nil, [s], nil, SELECT_TIMEOUT)
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?
@@ -13,4 +13,8 @@ module Unix::File
13
13
  paths.split(':')
14
14
  end
15
15
 
16
+ def file_exist?(path)
17
+ result = exec(Beaker::Command.new("test -e #{path}"), :acceptable_exit_codes => [0, 1])
18
+ result.exit_code == 0
19
+ end
16
20
  end
@@ -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
- vagrant_file = "Vagrant.configure(\"2\") do |c|\n"
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
- vagrant_file << " c.vm.define '#{host.name}' do |v|\n"
28
- vagrant_file << " v.vm.hostname = '#{host.name}'\n"
29
- vagrant_file << " v.vm.box = '#{host['box']}'\n"
30
- vagrant_file << " v.vm.box_url = '#{host['box_url']}'\n" unless host['box_url'].nil?
31
- vagrant_file << " v.vm.base_mac = '#{randmac}'\n"
32
- vagrant_file << " v.vm.network :private_network, ip: \"#{host['ip'].to_s}\", :netmask => \"255.255.0.0\"\n"
33
- vagrant_file << " end\n"
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
- vagrant_file << " c.vm.provider :virtualbox do |vb|\n"
37
- vagrant_file << " vb.customize [\"modifyvm\", :id, \"--memory\", \"1024\"]\n"
38
- vagrant_file << " end\n"
39
- vagrant_file << "end\n"
40
- FileUtils.mkdir_p(@vagrant_path)
41
- File.open(File.expand_path(File.join(@vagrant_path, "Vagrantfile")), 'w') do |f|
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
- make_vfile @vagrant_hosts
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
- #stop anything currently running, that way vagrant up will re-do networking on existing boxes
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
- begin
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
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
- raise "Couldn't find VM #{name} in vSphere!"
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
- host_info = @options['HOSTS'][name]
25
- #check to see if this host has a hypervisor
26
- hypervisor = host_info['hypervisor']
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 #{host_info['hypervisor'] || 'default' }, and I'm going to use #{hypervisor}"
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
@@ -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
- it "can provision a set of hosts" do
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
- vagrant.should_receive( :make_vfile ).with( @hosts ).once
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
- vagrant.should_receive( :vagrant_cmd ).with( "halt" ).once
90
- vagrant.should_receive( :vagrant_cmd ).with( "up" ).once
91
- @hosts.each do |host|
92
- host_prev_name = host['user']
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.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-12 00:00:00.000000000 Z
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: nokogiri
196
+ name: fission
183
197
  requirement: !ruby/object:Gem::Requirement
184
198
  requirements:
185
- - - '='
199
+ - - ~>
186
200
  - !ruby/object:Gem::Version
187
- version: 1.5.10
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: 1.5.10
208
+ version: '0.4'
195
209
  - !ruby/object:Gem::Dependency
196
- name: fission
210
+ name: nokogiri
197
211
  requirement: !ruby/object:Gem::Requirement
198
212
  requirements:
199
- - - ! '>='
213
+ - - '='
200
214
  - !ruby/object:Gem::Version
201
- version: '0'
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: '0'
222
+ version: 1.5.10
209
223
  - !ruby/object:Gem::Dependency
210
- name: inifile
224
+ name: mime-types
211
225
  requirement: !ruby/object:Gem::Requirement
212
226
  requirements:
213
- - - ! '>='
227
+ - - ~>
214
228
  - !ruby/object:Gem::Version
215
- version: '0'
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: '0'
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: '0'
406
+ version: 1.3.1
393
407
  requirements: []
394
408
  rubyforge_project:
395
409
  rubygems_version: 2.0.6