beaker 2.2.0 → 2.3.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.
@@ -40,7 +40,7 @@ module Beaker
40
40
  if not @compute_client
41
41
  raise "Unable to create OpenStack Compute instance (api key: #{@options[:openstack_api_key]}, username: #{@options[:openstack_username]}, auth_url: #{@options[:openstack_auth_url]}, tenant: #{@options[:openstack_tenant]})"
42
42
  end
43
- @network_client || Fog::Network.new(
43
+ @network_client ||= Fog::Network.new(
44
44
  :provider => :openstack,
45
45
  :openstack_api_key => @options[:openstack_api_key],
46
46
  :openstack_username => @options[:openstack_username],
@@ -132,6 +132,8 @@ module Beaker
132
132
  :project => @options[:project].to_s })
133
133
  @vms << vm
134
134
 
135
+ #enable root if user is not root
136
+ enable_root_on_hosts()
135
137
  end
136
138
  end
137
139
 
@@ -150,5 +152,28 @@ module Beaker
150
152
  end
151
153
  end
152
154
 
155
+ # Enables root for instances with custom username like ubuntu-amis
156
+ #
157
+ # @return [void]
158
+ # @api private
159
+ def enable_root_on_hosts
160
+ @hosts.each do |host|
161
+ enable_root(host)
162
+ end
163
+ end
164
+
165
+ # Enables root access for a host when username is not root
166
+ #
167
+ # @return [void]
168
+ # @api private
169
+ def enable_root(host)
170
+ if host['user'] != 'root'
171
+ copy_ssh_to_root(host, @options)
172
+ enable_root_login(host, @options)
173
+ host['user'] = 'root'
174
+ host.close
175
+ end
176
+ end
177
+
153
178
  end
154
179
  end
@@ -31,6 +31,7 @@ module Beaker
31
31
  v_file << " v.vm.box_url = '#{host['box_url']}'\n" unless host['box_url'].nil?
32
32
  v_file << " v.vm.box_version = '#{host['box_version']}'\n" unless host['box_version'].nil?
33
33
  v_file << " v.vm.box_check_update = '#{host['box_check_update'] ||= 'true'}'\n"
34
+ v_file << " v.vm.synced_folder '.', '/vagrant', disabled: true\n" if host['synced_folder'] == 'disabled'
34
35
  v_file << " v.vm.network :private_network, ip: \"#{host['ip'].to_s}\", :netmask => \"#{host['netmask'] ||= "255.255.0.0"}\", :mac => \"#{randmac}\"\n"
35
36
 
36
37
  if /windows/i.match(host['platform'])
@@ -39,6 +40,11 @@ module Beaker
39
40
  v_file << " v.vm.guest = :windows"
40
41
  end
41
42
 
43
+ if /osx/i.match(host['platform'])
44
+ v_file << " v.vm.network 'private_network', ip: '10.0.1.10'\n"
45
+ v_file << " v.vm.synced_folder '.', '/vagrant', :nfs => true\n"
46
+ end
47
+
42
48
  v_file << self.class.provider_vfile_section(host, options)
43
49
 
44
50
  v_file << " end\n"
@@ -163,12 +169,12 @@ module Beaker
163
169
  @logger.info(line)
164
170
  end
165
171
  if not wait_thr.value.success?
166
- raise "Failed to exec 'vagrant #{args}'"
172
+ raise "Failed to exec 'vagrant #{args}'. Error was #{stderr.read}"
167
173
  end
168
174
  exit_status = wait_thr.value
169
175
  }
170
176
  if exit_status != 0
171
- raise "Failed to execute vagrant_cmd ( #{args} )"
177
+ raise "Failed to execute vagrant_cmd ( #{args} ). Error was #{stderr.read}"
172
178
  end
173
179
  end
174
180
  end
@@ -18,6 +18,8 @@ class Beaker::VagrantVirtualbox < Beaker::Vagrant
18
18
  provider_section << " vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 1, '--device', 0, '--type', 'hdd', '--medium','#{host['disk_path']}']\n"
19
19
  provider_section << " vb.customize [\"modifyvm\", :id, \"--natdnshostresolver1\", \"#{host['natdns']}\"]\n" unless host['natdns'].nil?
20
20
  provider_section << " vb.customize [\"modifyvm\", :id, \"--natdnsproxy1\", \"#{host['natdns']}\"]\n" unless host['natdns'].nil?
21
+ provider_section << " vb.gui = true\n" unless host['vb_gui'].nil?
22
+ provider_section << " [\"modifyvm\", :id, \"--cpuidset\", \"1\",\"000206a7\",\"02100800\",\"1fbae3bf\",\"bfebfbff\"\]" if /osx/i.match(host['platform'])
21
23
  end
22
24
  provider_section << " end\n"
23
25
 
@@ -119,6 +119,7 @@ module Beaker
119
119
  :log_level => 'info',
120
120
  :trace_limit => 10,
121
121
  :"master-start-curl-retries" => 120,
122
+ :masterless => false,
122
123
  :options_file => nil,
123
124
  :type => 'pe',
124
125
  :provision => true,
@@ -46,6 +46,27 @@ module Beaker
46
46
  a_host.first
47
47
  end
48
48
 
49
+ # Find at most a single host with the role provided. Raise an error if
50
+ # more than one host is found to have the provided role.
51
+ # @param [Array<Host>] hosts The hosts to examine
52
+ # @param [String] role The host returned will have this role in its role list
53
+ # @return [Host] The single host with the desired role in its roles list
54
+ # or nil if no host is found
55
+ # @raise [ArgumentError] Raised if more than one host has the given role defined
56
+ def find_at_most_one_host_with_role(hosts, role)
57
+ role_hosts = hosts_with_role(hosts, role)
58
+ host_with_role = nil
59
+ case role_hosts.length
60
+ when 0
61
+ when 1
62
+ host_with_role = role_hosts[0]
63
+ else
64
+ host_string = ( role_hosts.map { |host| host.name } ).join( ', ')
65
+ raise ArgumentError, "There should be only one host with #{role} defined, but I found #{role_hosts.length} (#{host_string})"
66
+ end
67
+ host_with_role
68
+ end
69
+
49
70
  #Execute a block selecting the hosts that match with the provided criteria
50
71
  #@param [Array<Host>, Host] hosts The host or hosts to run the provided block against
51
72
  #@param [String, Symbol] filter Optional filter to apply to provided hosts - limits by name or role
@@ -1,5 +1,5 @@
1
1
  module Beaker
2
2
  module Version
3
- STRING = '2.2.0'
3
+ STRING = '2.3.0'
4
4
  end
5
5
  end
@@ -50,6 +50,44 @@ module Beaker
50
50
  end
51
51
  end
52
52
 
53
+ describe "Masterless Setup" do
54
+ let( :ver ) { @ver || '3.0' }
55
+ let( :options ) { options = Beaker::Options::Presets.new.presets
56
+ options[:masterless] = true
57
+ options }
58
+ let( :hosts ) { make_hosts({}, 1) }
59
+ let( :host ) { hosts[0] }
60
+ let( :answers ) { Beaker::Answers.create(ver, hosts, options) }
61
+ let( :host_answers ) { answers.answers[host.name] }
62
+
63
+
64
+ it 'adds the correct answers' do
65
+ expect( host_answers[:q_puppetagent_server] ).to be === host_answers[:q_puppetagent_certname]
66
+ expect( host_answers[:q_continue_or_reenter_master_hostname]).to be === 'c'
67
+ end
68
+
69
+ it 'skips the correct answers' do
70
+ expect( host_answers[:q_puppetmaster_install]).to be === 'n'
71
+ expect( host_answers[:q_puppet_enterpriseconsole_install] ).to be === 'n'
72
+ expect( host_answers[:q_puppetdb_install] ).to be === 'n'
73
+ end
74
+
75
+ it '3.0: never calls #only_host_with_role in #generate_answers' do
76
+ expect( answers.generate_answers ).to_not receive( :only_host_with_role )
77
+ end
78
+
79
+ it '3.2: never calls #only_host_with_role in #generate_answers' do
80
+ @ver = '3.2'
81
+ expect( answers.generate_answers ).to_not receive( :only_host_with_role )
82
+ end
83
+
84
+ it '3.4: never calls #only_host_with_role in #generate_answers' do
85
+ @ver = '3.4'
86
+ expect( answers.generate_answers ).to_not receive( :only_host_with_role )
87
+ end
88
+
89
+ end
90
+
53
91
  describe Version34 do
54
92
  let( :options ) { Beaker::Options::Presets.new.presets }
55
93
  let( :basic_hosts ) { make_hosts( {'pe_ver' => @ver } ) }
@@ -1294,4 +1294,56 @@ describe ClassMixedWithDSLHelpers do
1294
1294
  end
1295
1295
  end
1296
1296
  end
1297
+
1298
+ describe "#write_hiera_config_on" do
1299
+ let(:hierarchy) { [ 'nodes/%{::fqdn}', 'common' ] }
1300
+ it 'on FOSS host' do
1301
+ host = make_host('testhost', { :platform => 'ubuntu' } )
1302
+ expect(subject).to receive(:create_remote_file).with(host, host[:hieraconf], /#{host[:hieradatadir]}/)
1303
+ subject.write_hiera_config_on(host, hierarchy)
1304
+ end
1305
+
1306
+ it 'on PE host' do
1307
+ host = make_host('testhost', { :platform => 'ubuntu', :type => 'pe' } )
1308
+ expect(subject).to receive(:create_remote_file).with(host, host[:hieraconf], /#{host[:hieradatadir]}/)
1309
+ subject.write_hiera_config_on(host, hierarchy)
1310
+ end
1311
+
1312
+ end
1313
+
1314
+ describe "#write_hiera_config" do
1315
+ let(:hierarchy) { [ 'nodes/%{::fqdn}', 'common' ] }
1316
+ it 'delegates to #write_hiera_config_on with the default host' do
1317
+ allow( subject ).to receive( :hosts ).and_return( hosts )
1318
+ expect( subject ).to receive( :write_hiera_config_on ).with( master, hierarchy).once
1319
+ subject.write_hiera_config( hierarchy )
1320
+ end
1321
+
1322
+ end
1323
+
1324
+ describe "#copy_hiera_data_to" do
1325
+ let(:path) { 'spec/fixtures/hieradata' }
1326
+ it 'on FOSS host' do
1327
+ host = make_host('testhost', { :platform => 'ubuntu' } )
1328
+ expect(subject).to receive(:scp_to).with(host, File.expand_path(path), host[:hieradatadir])
1329
+ subject.copy_hiera_data_to(host, path)
1330
+ end
1331
+
1332
+ it 'on PE host' do
1333
+ host = make_host('testhost', { :platform => 'ubuntu', :type => 'pe' } )
1334
+ expect(subject).to receive(:scp_to).with(host, File.expand_path(path), host[:hieradatadir])
1335
+ subject.copy_hiera_data_to(host, path)
1336
+ end
1337
+ end
1338
+
1339
+ describe "#copy_hiera_data" do
1340
+ let(:path) { 'spec/fixtures/hieradata' }
1341
+ it 'delegates to #copy_hiera_data_to with the default host' do
1342
+ allow( subject ).to receive( :hosts ).and_return( hosts )
1343
+ expect( subject ).to receive( :copy_hiera_data_to ).with( master, path).once
1344
+ subject.copy_hiera_data( path )
1345
+ end
1346
+
1347
+ end
1348
+
1297
1349
  end
@@ -154,6 +154,13 @@ describe ClassMixedWithDSLInstallUtils do
154
154
  allow( subject ).to receive( :hosts ).and_return( hosts )
155
155
  expect( subject.sorted_hosts ).to be === hosts
156
156
  end
157
+
158
+ it 'does not allow nil entries' do
159
+ allow( subject ).to receive( :options ).and_return( { :masterless => true } )
160
+ masterless_host = [basic_hosts[0]]
161
+ allow( subject ).to receive( :hosts ).and_return( masterless_host )
162
+ expect( subject.sorted_hosts ).to be === masterless_host
163
+ end
157
164
  end
158
165
 
159
166
  describe 'installer_cmd' do
@@ -372,7 +379,7 @@ describe ClassMixedWithDSLInstallUtils do
372
379
  expect( subject ).to receive( :on ).with( hosts[2], /puppet config set certname/ ).once
373
380
  expect( subject ).to receive( :on ).with( hosts[3], /puppet config set certname/ ).once
374
381
  expect( subject ).to receive( :on ).with( hosts[2], /puppet agent -t/, :acceptable_exit_codes => [1] ).once
375
- expect( subject ).to receive( :on ).with( hosts[3], /puppet agent -t/, :acceptable_exit_codes => [1] ).once
382
+ expect( subject ).to receive( :on ).with( hosts[3], /puppet agent -t/, :acceptable_exit_codes => [0, 1] ).once
376
383
  #sign certificate per-host
377
384
  expect( subject ).to receive( :sign_certificate_for ).with( hosts[0] ).once
378
385
  expect( subject ).to receive( :sign_certificate_for ).with( hosts[1] ).once
@@ -397,6 +404,30 @@ describe ClassMixedWithDSLInstallUtils do
397
404
  expect( subject ).to receive( :on ).with( hosts, /puppet agent/, :acceptable_exit_codes => [0,2] ).once
398
405
  subject.do_install( hosts, opts )
399
406
  end
407
+
408
+ it 'can perform a masterless installation' do
409
+ hosts = make_hosts({
410
+ :pe_ver => '3.0',
411
+ :roles => ['agent']
412
+ }, 1)
413
+
414
+ allow( subject ).to receive( :hosts ).and_return( hosts )
415
+ allow( subject ).to receive( :options ).and_return({ :masterless => true })
416
+ allow( subject ).to receive( :on ).and_return( Beaker::Result.new( {}, '' ) )
417
+ allow( subject ).to receive( :fetch_puppet ).and_return( true )
418
+ allow( subject ).to receive( :create_remote_file ).and_return( true )
419
+ allow( subject ).to receive( :stop_agent_on ).and_return( true )
420
+ allow( subject ).to receive( :version_is_less ).with(anything, '3.2.0').exactly(hosts.length + 1).times.and_return( false )
421
+
422
+ expect( subject ).to receive( :on ).with( hosts[0], /puppet-enterprise-installer/ ).once
423
+ expect( subject ).to receive( :create_remote_file ).with( hosts[0], /answers/, /q/ ).once
424
+ expect( subject ).to_not receive( :sign_certificate_for )
425
+ expect( subject ).to receive( :stop_agent_on ).with( hosts[0] ).once
426
+ expect( subject ).to_not receive( :sleep_until_puppetdb_started )
427
+ expect( subject ).to_not receive( :wait_for_host_in_dashboard )
428
+ expect( subject ).to_not receive( :on ).with( hosts[0], /puppet agent -t/, :acceptable_exit_codes => [0,2] )
429
+ subject.do_install( hosts, opts)
430
+ end
400
431
  end
401
432
 
402
433
  describe 'do_higgs_install' do
@@ -812,6 +843,32 @@ describe ClassMixedWithDSLInstallUtils do
812
843
 
813
844
  end
814
845
 
846
+ describe '#install_packages_from_local_dev_repo' do
847
+ let( :package_name ) { 'puppet-agent' }
848
+ let( :platform ) { @platform || 'other' }
849
+ let( :host ) do
850
+ FakeHost.create('fakvm', platform, opts)
851
+ end
852
+
853
+ it 'sets the find command correctly for el-based systems' do
854
+ @platform = 'el-1-3'
855
+ expect( subject ).to receive( :on ).with( host, /\*\.rpm.+rpm\s-ivh/ )
856
+ subject.install_packages_from_local_dev_repo( host, package_name )
857
+ end
858
+
859
+ it 'sets the find command correctly for debian-based systems' do
860
+ @platform = 'debian-1-3'
861
+ expect( subject ).to receive( :on ).with( host, /\*\.deb.+dpkg\s-i/ )
862
+ subject.install_packages_from_local_dev_repo( host, package_name )
863
+ end
864
+
865
+ it 'fails correctly for systems not accounted for' do
866
+ @platform = 'eos-1-3'
867
+ expect{ subject.install_packages_from_local_dev_repo( host, package_name ) }.to raise_error RuntimeError
868
+ end
869
+
870
+ end
871
+
815
872
  describe '#install_puppetagent_dev_repo' do
816
873
 
817
874
  it 'raises an exception when host platform is unsupported' do
@@ -9,6 +9,7 @@ end
9
9
  describe ClassMixedWithDSLRoles do
10
10
 
11
11
  let( :hosts ) { @hosts || Hash.new }
12
+ let( :options ) { @options || Hash.new }
12
13
  let( :agent1 ) { make_host( 'agent1', { :roles => [ 'agent' ] } ) }
13
14
  let( :agent2 ) { make_host( 'agent2', { :roles => [ 'agent' ] } ) }
14
15
  let( :a_and_dash ) { make_host( 'a_and_dash', { :roles => [ 'agent', 'dashboard' ] } ) }
@@ -42,6 +43,12 @@ describe ClassMixedWithDSLRoles do
42
43
  expect( subject ).to receive( :hosts ).exactly( 1 ).times.and_return( hosts )
43
44
  expect { subject.master }.to raise_error Beaker::DSL::FailTest
44
45
  end
46
+ it 'returns nil if no master and masterless is set' do
47
+ @options = { :masterless => true }
48
+ expect( subject ).to receive( :hosts ).and_return( hosts )
49
+ expect( subject ).to receive( :options ).and_return( options )
50
+ expect( subject.master ).to be_nil
51
+ end
45
52
  end
46
53
  describe '#dashboard' do
47
54
  it 'returns the dashboard if there is one' do
@@ -59,6 +66,12 @@ describe ClassMixedWithDSLRoles do
59
66
  expect( subject ).to receive( :hosts ).and_return( hosts )
60
67
  expect { subject.dashboard }.to raise_error Beaker::DSL::FailTest
61
68
  end
69
+ it 'returns nil if no dashboard and masterless is set' do
70
+ @options = { :masterless => true }
71
+ expect( subject ).to receive( :hosts ).and_return( hosts )
72
+ expect( subject ).to receive( :options ).and_return( options )
73
+ expect( subject.dashboard ).to be_nil
74
+ end
62
75
  end
63
76
  describe '#database' do
64
77
  it 'returns the database if there is one' do
@@ -76,6 +89,12 @@ describe ClassMixedWithDSLRoles do
76
89
  expect( subject ).to receive( :hosts ).and_return( hosts )
77
90
  expect { subject.database }.to raise_error Beaker::DSL::FailTest
78
91
  end
92
+ it 'returns nil if no database and masterless is set' do
93
+ @options = { :masterless => true }
94
+ expect( subject ).to receive( :hosts ).and_return( hosts )
95
+ expect( subject ).to receive( :options ).and_return( options )
96
+ expect( subject.database ).to be_nil
97
+ end
79
98
  end
80
99
  describe '#default' do
81
100
  it 'returns the default host when one is specified' do
@@ -86,12 +105,18 @@ describe ClassMixedWithDSLRoles do
86
105
  it 'raises an error if there is more than one default' do
87
106
  @hosts = [ db, monolith, default, default ]
88
107
  expect( subject ).to receive( :hosts ).and_return( hosts )
89
- expect { subject.database }.to raise_error Beaker::DSL::FailTest
108
+ expect { subject.default }.to raise_error Beaker::DSL::FailTest
90
109
  end
91
110
  it 'and raises an error if there is no default' do
92
111
  @hosts = [ agent1, agent2, custom ]
93
112
  expect( subject ).to receive( :hosts ).and_return( hosts )
94
- expect { subject.database }.to raise_error Beaker::DSL::FailTest
113
+ expect { subject.default }.to raise_error Beaker::DSL::FailTest
114
+ end
115
+ it 'returns nil if no default and masterless is set' do
116
+ @options = { :masterless => true }
117
+ expect( subject ).to receive( :hosts ).and_return( hosts )
118
+ expect( subject ).to receive( :options ).and_return( options )
119
+ expect( subject.default ).to be_nil
95
120
  end
96
121
  end
97
122
  describe '#add_role_def' do
@@ -56,6 +56,24 @@ module Beaker
56
56
  expect(host.is_using_passenger?).to be_truthy
57
57
  expect(host.graceful_restarts?).to be_truthy
58
58
  end
59
+
60
+ it 'can be an AIO host' do
61
+ options['type'] = 'aio'
62
+ expect(host.is_pe?).to be_falsy
63
+ expect(host.use_service_scripts?).to be_falsy
64
+ expect(host.is_using_passenger?).to be_falsy
65
+ end
66
+
67
+ it 'sets the paths correctly for an AIO agent host' do
68
+ options['type'] = 'aio'
69
+ expect(host['puppetvardir']).to be === Unix::Host::aio_defaults[:puppetvardir]
70
+ end
71
+
72
+ it 'sets the paths correctly for an AIO non-agent host' do
73
+ options['type'] = 'aio'
74
+ options['roles'] = ['master']
75
+ expect(host['puppetvardir']).to be === Unix::Host::foss_defaults[:puppetvardir]
76
+ end
59
77
  end
60
78
 
61
79
  describe "uses_passenger!" do
@@ -79,6 +79,17 @@ EOF
79
79
  expect( vagrantfile ).to match(/(ssh.forward_agent = true)/)
80
80
  end
81
81
 
82
+ it "can make a Vagrantfile with synced_folder disabled" do
83
+ path = vagrant.instance_variable_get( :@vagrant_path )
84
+ allow( vagrant ).to receive( :randmac ).and_return( "0123456789" )
85
+
86
+ hosts = make_hosts({:synced_folder => 'disabled'},1)
87
+ vagrant.make_vfile( hosts, options )
88
+
89
+ vagrantfile = File.read( File.expand_path( File.join( path, "Vagrantfile")))
90
+ expect( vagrantfile ).to match(/v.vm.synced_folder .* disabled: true/)
91
+ end
92
+
82
93
  it "generates a valid windows config" do
83
94
  path = vagrant.instance_variable_get( :@vagrant_path )
84
95
  allow( vagrant ).to receive( :randmac ).and_return( "0123456789" )
@@ -56,6 +56,26 @@ module Beaker
56
56
  end
57
57
  end
58
58
 
59
+ context "find_at_most_one_host_with_role" do
60
+ it "can find the single master in a set of hosts" do
61
+
62
+ expect( host_handler.find_at_most_one_host_with_role( hosts, 'master' ) ).to be === hosts[1]
63
+
64
+ end
65
+
66
+ it "throws an error when more than one host with matching role is found" do
67
+
68
+ expect{ host_handler.find_at_most_one_host_with_role( hosts, 'agent' ) }.to raise_error(ArgumentError)
69
+
70
+ end
71
+
72
+ it "returns nil when no host is found matching the role" do
73
+
74
+ expect( host_handler.find_at_most_one_host_with_role( hosts, 'surprise' ) ).to be_nil
75
+
76
+ end
77
+ end
78
+
59
79
  context "run_block_on" do
60
80
  it "can execute a block against hosts identified by a string" do
61
81
  myhosts = host_handler.run_block_on( hosts, role0 ) do |hosts|