beaker 2.18.3 → 2.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +8 -8
  2. data/HISTORY.md +439 -2
  3. data/acceptance/lib/beaker/acceptance/install_utils.rb +58 -0
  4. data/acceptance/pre_suite/puppet_git/install.rb +6 -65
  5. data/acceptance/tests/foss_utils/clone_git_repo_on.rb +49 -0
  6. data/beaker.gemspec +2 -0
  7. data/lib/beaker/dsl/helpers/web_helpers.rb +2 -1
  8. data/lib/beaker/dsl/install_utils/aio_defaults.rb +0 -2
  9. data/lib/beaker/dsl/install_utils/foss_utils.rb +97 -60
  10. data/lib/beaker/dsl/install_utils/pe_utils.rb +30 -53
  11. data/lib/beaker/dsl/install_utils/puppet_utils.rb +43 -0
  12. data/lib/beaker/dsl/install_utils/windows_utils.rb +144 -0
  13. data/lib/beaker/dsl/roles.rb +20 -3
  14. data/lib/beaker/dsl/structure.rb +14 -3
  15. data/lib/beaker/host.rb +24 -3
  16. data/lib/beaker/host/unix/pkg.rb +9 -0
  17. data/lib/beaker/host/windows/exec.rb +3 -0
  18. data/lib/beaker/host_prebuilt_steps.rb +5 -9
  19. data/lib/beaker/hypervisor/aws_sdk.rb +22 -18
  20. data/lib/beaker/hypervisor/docker.rb +7 -0
  21. data/lib/beaker/hypervisor/vmpooler.rb +4 -0
  22. data/lib/beaker/logger.rb +12 -1
  23. data/lib/beaker/options/command_line_parser.rb +9 -0
  24. data/lib/beaker/options/options_hash.rb +3 -296
  25. data/lib/beaker/options/parser.rb +12 -0
  26. data/lib/beaker/options/presets.rb +0 -1
  27. data/lib/beaker/ssh_connection.rb +48 -23
  28. data/lib/beaker/test_case.rb +1 -1
  29. data/lib/beaker/version.rb +1 -1
  30. data/spec/beaker/dsl/helpers/web_helpers_spec.rb +10 -1
  31. data/spec/beaker/dsl/install_utils/foss_utils_spec.rb +194 -49
  32. data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +112 -22
  33. data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +57 -0
  34. data/spec/beaker/dsl/install_utils/windows_utils_spec.rb +132 -0
  35. data/spec/beaker/dsl/roles_spec.rb +36 -5
  36. data/spec/beaker/dsl/structure_spec.rb +9 -2
  37. data/spec/beaker/host/unix/pkg_spec.rb +26 -6
  38. data/spec/beaker/host_prebuilt_steps_spec.rb +3 -2
  39. data/spec/beaker/host_spec.rb +18 -0
  40. data/spec/beaker/hypervisor/aixer_spec.rb +1 -1
  41. data/spec/beaker/hypervisor/aws_sdk_spec.rb +595 -58
  42. data/spec/beaker/hypervisor/docker_spec.rb +2 -1
  43. data/spec/beaker/hypervisor/solaris_spec.rb +1 -0
  44. data/spec/beaker/hypervisor/vagrant_spec.rb +2 -1
  45. data/spec/beaker/logger_spec.rb +39 -0
  46. data/spec/beaker/options/command_line_parser_spec.rb +2 -2
  47. data/spec/beaker/options/options_hash_spec.rb +1 -102
  48. data/spec/beaker/options/parser_spec.rb +19 -0
  49. data/spec/beaker/options/pe_version_scaper_spec.rb +11 -1
  50. data/spec/beaker/options/presets_spec.rb +8 -0
  51. data/spec/beaker/ssh_connection_spec.rb +39 -21
  52. data/spec/helpers.rb +9 -3
  53. data/spec/mocks.rb +2 -0
  54. metadata +34 -11
  55. data/lib/beaker/answers.rb +0 -143
  56. data/lib/beaker/answers/version20.rb +0 -120
  57. data/lib/beaker/answers/version28.rb +0 -121
  58. data/lib/beaker/answers/version30.rb +0 -227
  59. data/lib/beaker/answers/version32.rb +0 -44
  60. data/lib/beaker/answers/version34.rb +0 -51
  61. data/lib/beaker/answers/version38.rb +0 -29
  62. data/lib/beaker/answers/version40.rb +0 -44
  63. data/spec/beaker/answers_spec.rb +0 -547
@@ -128,14 +128,21 @@ describe ClassMixedWithDSLStructure do
128
128
  subject.confine( :to, {} )
129
129
  end
130
130
 
131
- it 'uses a provided host subset when no criteria is provided' do
132
-
131
+ it ':to - uses a provided host subset when no criteria is provided' do
133
132
  subset = ['host1', 'host2']
134
133
  hosts = subset.dup << 'host3'
135
134
  expect( subject ).to receive( :hosts= ).with( subset )
136
135
  subject.confine :to, {}, subset
137
136
  end
138
137
 
138
+ it ':except - excludes provided host subset when no criteria is provided' do
139
+ subset = ['host1', 'host2']
140
+ hosts = subset.dup << 'host3'
141
+ allow( subject ).to receive( :hosts ).and_return(hosts)
142
+ expect( subject ).to receive( :hosts= ).with( hosts - subset )
143
+ subject.confine :except, {}, subset
144
+ end
145
+
139
146
  it 'raises when given mode is not :to or :except' do
140
147
  allow( subject ).to receive( :hosts )
141
148
  allow( subject ).to receive( :hosts= )
@@ -122,9 +122,29 @@ module Beaker
122
122
  expect{ instance.check_for_package(pkg) }.to raise_error
123
123
 
124
124
  end
125
-
126
- end
127
-
128
- end
129
- end
130
-
125
+
126
+ end
127
+
128
+ context "install_package" do
129
+
130
+ it "uses yum on fedora-20" do
131
+ @opts = {'platform' => 'fedora-20-is-me'}
132
+ pkg = 'fedora_package'
133
+ expect( Beaker::Command ).to receive(:new).with("yum -y install #{pkg}", [], {:prepend_cmds=>nil, :cmdexe=>false}).and_return('')
134
+ expect( instance ).to receive(:exec).with('', {}).and_return(generate_result("hello", {:exit_code => 0}))
135
+ expect( instance.install_package(pkg) ).to be == "hello"
136
+ end
137
+
138
+ it "uses dnf on fedora-22" do
139
+ @opts = {'platform' => 'fedora-22-is-me'}
140
+ pkg = 'fedora_package'
141
+ expect( Beaker::Command ).to receive(:new).with("dnf -y install #{pkg}", [], {:prepend_cmds=>nil, :cmdexe=>false}).and_return('')
142
+ expect( instance ).to receive(:exec).with('', {}).and_return(generate_result("hello", {:exit_code => 0}))
143
+ expect( instance.install_package(pkg) ).to be == "hello"
144
+ end
145
+
146
+ end
147
+
148
+ end
149
+ end
150
+
@@ -97,7 +97,7 @@ describe Beaker do
97
97
 
98
98
  expect( Beaker::Command ).to receive( :new ).with("ntpdate -t 20 #{ntpserver}").exactly( 5 ).times
99
99
 
100
- expect{ subject.timesync( hosts, options ) }.to raise_error
100
+ expect{ subject.timesync( hosts, options ) }.to raise_error(/NTP date was not successful after/)
101
101
  end
102
102
 
103
103
  it "can sync time on windows hosts" do
@@ -170,7 +170,7 @@ describe Beaker do
170
170
  it "raises an error on non el-5/6 host" do
171
171
  host = make_host( 'testhost', { :platform => Beaker::Platform.new('el-4-platform') } )
172
172
 
173
- expect{ subject.epel_info_for( host, options )}.to raise_error
173
+ expect{ subject.epel_info_for( host, options )}.to raise_error(RuntimeError, /epel_info_for does not support el version/)
174
174
 
175
175
  end
176
176
 
@@ -572,6 +572,7 @@ describe Beaker do
572
572
  expect( Beaker::Command ).to receive( :new ).with( "mkdir -p #{Pathname.new(host[:ssh_env_file]).dirname}" ).once
573
573
  expect( Beaker::Command ).to receive( :new ).with( "chmod 0600 #{Pathname.new(host[:ssh_env_file]).dirname}" ).once
574
574
  expect( Beaker::Command ).to receive( :new ).with( "touch #{host[:ssh_env_file]}" ).once
575
+ expect_any_instance_of( Class ).to receive( :extend ).and_return( double( 'class' ).as_null_object )
575
576
  expect( Beaker::Command ).to receive( :new ).with( "cat #{host[:ssh_env_file]}" ).once
576
577
  expect( host ).to receive( :add_env_var ).with( 'PATH', '$PATH' ).once
577
578
  opts.each_pair do |key, value|
@@ -243,6 +243,9 @@ module Beaker
243
243
  host.instance_variable_set :@logger, logger
244
244
  conn = double(:connection)
245
245
  allow( conn ).to receive(:execute).and_return(result)
246
+ allow( conn ).to receive(:ip).and_return(host['ip'])
247
+ allow( conn ).to receive(:vmhostname).and_return(host['vmhostname'])
248
+ allow( conn ).to receive(:hostname).and_return(host.name)
246
249
  host.instance_variable_set :@connection, conn
247
250
  end
248
251
 
@@ -399,6 +402,9 @@ module Beaker
399
402
 
400
403
  expect( logger ).to receive(:trace)
401
404
  expect( conn ).to receive(:scp_to).with( *conn_args ).and_return(Beaker::Result.new(host, 'output!'))
405
+ allow( conn ).to receive(:ip).and_return(host['ip'])
406
+ allow( conn ).to receive(:vmhostname).and_return(host['vmhostname'])
407
+ allow( conn ).to receive(:hostname).and_return(host.name)
402
408
 
403
409
  host.do_scp_to *args
404
410
  end
@@ -466,6 +472,9 @@ module Beaker
466
472
  expect( conn ).to_not receive(:scp_to).with( *conn_args )
467
473
  end
468
474
  end
475
+ allow( conn ).to receive(:ip).and_return(host['ip'])
476
+ allow( conn ).to receive(:vmhostname).and_return(host['vmhostname'])
477
+ allow( conn ).to receive(:hostname).and_return(host.name)
469
478
 
470
479
  host.do_scp_to *args
471
480
  end
@@ -527,6 +536,9 @@ module Beaker
527
536
  expect( conn ).to_not receive(:scp_to).with( *conn_args )
528
537
  end
529
538
  end
539
+ allow( conn ).to receive(:ip).and_return(host['ip'])
540
+ allow( conn ).to receive(:vmhostname).and_return(host['vmhostname'])
541
+ allow( conn ).to receive(:hostname).and_return(host.name)
530
542
 
531
543
  host.do_scp_to *args
532
544
  end
@@ -555,6 +567,9 @@ module Beaker
555
567
  expect( conn ).to receive(:scp_to).with( *conn_args ).and_return(Beaker::Result.new(host, 'output!'))
556
568
  end
557
569
 
570
+ allow( conn ).to receive(:ip).and_return(host['ip'])
571
+ allow( conn ).to receive(:vmhostname).and_return(host['vmhostname'])
572
+ allow( conn ).to receive(:hostname).and_return(host.name)
558
573
  host.do_scp_to *args
559
574
  end
560
575
  end
@@ -572,6 +587,9 @@ module Beaker
572
587
  expect( logger ).to receive(:debug)
573
588
  expect( conn ).to receive(:scp_from).with( *conn_args ).and_return(Beaker::Result.new(host, 'output!'))
574
589
 
590
+ allow( conn ).to receive(:ip).and_return(host['ip'])
591
+ allow( conn ).to receive(:vmhostname).and_return(host['vmhostname'])
592
+ allow( conn ).to receive(:hostname).and_return(host.name)
575
593
  host.do_scp_from *args
576
594
  end
577
595
  end
@@ -16,7 +16,7 @@ module Beaker
16
16
  expect( Command ).to receive( :new ).with( "cd pe-aix && rake restore:#{host.name}" ).once
17
17
 
18
18
  end
19
-
19
+ allow_any_instance_of(Host).to receive(:close)
20
20
  aixer.provision
21
21
 
22
22
  end
@@ -44,90 +44,314 @@ module Beaker
44
44
  @hosts[4][:user] = 'notroot'
45
45
  end
46
46
 
47
- context 'enabling root' do
48
- it 'enables root once on the ubuntu host through the main code path' do
49
- expect( aws ).to receive(:copy_ssh_to_root).with( @hosts[3], options ).once()
50
- expect( aws ).to receive(:enable_root_login).with( @hosts[3], options).once()
51
- aws.enable_root_on_hosts();
47
+ describe '#provision' do
48
+ before :each do
49
+ expect(aws).to receive(:launch_all_nodes)
50
+ expect(aws).to receive(:add_tags)
51
+ expect(aws).to receive(:populate_dns)
52
+ expect(aws).to receive(:enable_root_on_hosts)
53
+ expect(aws).to receive(:set_hostnames)
54
+ expect(aws).to receive(:configure_hosts)
52
55
  end
53
56
 
54
- it 'enables root once on the f5 host through its code path' do
55
- expect( aws ).to receive(:enable_root_f5).with( @hosts[4] ).once()
56
- aws.enable_root_on_hosts()
57
+ it 'should step through provisioning' do
58
+ aws.provision
59
+ end
60
+
61
+ it 'should return nil' do
62
+ expect(aws.provision).to be_nil
57
63
  end
64
+ end
58
65
 
59
- describe '#enable_root_f5' do
66
+ describe '#kill_instances' do
67
+ let( :ec2_instance ) { double('ec2_instance', :nil? => false, :exists? => true, :id => "ec2", :terminate => nil) }
68
+ let( :vpc_instance ) { double('vpc_instance', :nil? => false, :exists? => true, :id => "vpc", :terminate => nil) }
69
+ let( :nil_instance ) { double('vpc_instance', :nil? => true, :exists? => true, :id => "nil", :terminate => nil) }
70
+ let( :unreal_instance ) { double('vpc_instance', :nil? => false, :exists? => false, :id => "unreal", :terminate => nil) }
71
+ subject(:kill_instances) { aws.kill_instances(instance_set) }
72
+
73
+ it 'should return nil' do
74
+ instance_set = [ec2_instance, vpc_instance, nil_instance, unreal_instance]
75
+ expect(aws.kill_instances(instance_set)).to be_nil
76
+ end
77
+
78
+ it 'cleanly handles an empty instance list' do
79
+ instance_set = []
80
+ expect(aws.kill_instances(instance_set)).to be_nil
81
+ end
82
+
83
+ context 'in general use' do
84
+ let( :instance_set ) { [ec2_instance, vpc_instance] }
60
85
 
61
- it 'creates a password on the host' do
62
- f5_host = @hosts[4]
63
- result_mock = Beaker::Result.new(f5_host, '')
64
- result_mock.exit_code = 0
65
- allow( f5_host ).to receive( :exec ).and_return(result_mock)
66
- allow( aws ).to receive( :backoff_sleep )
67
- sha_mock = Object.new
68
- allow( Digest::SHA256 ).to receive( :new ).and_return(sha_mock)
69
- expect( sha_mock ).to receive( :hexdigest ).once()
70
- aws.enable_root_f5(f5_host)
86
+ it 'terminates each running instance' do
87
+ instance_set.each do |instance|
88
+ expect(instance).to receive(:terminate).once
89
+ end
90
+ expect(kill_instances).to be_nil
91
+ end
92
+
93
+ it 'verifies instances are not nil' do
94
+ instance_set.each do |instance|
95
+ expect(instance).to receive(:nil?)
96
+ allow(instance).to receive(:terminate).once
97
+ end
98
+ expect(kill_instances).to be_nil
71
99
  end
100
+
101
+ it 'verifies instances exist in AWS' do
102
+ instance_set.each do |instance|
103
+ expect(instance).to receive(:exists?)
104
+ allow(instance).to receive(:terminate).once
105
+ end
106
+ expect(kill_instances).to be_nil
107
+ end
108
+ end
109
+
110
+ context 'for a single running instance' do
111
+ let( :instance_set ) { [ec2_instance] }
72
112
 
73
- it 'tries 10x before failing correctly' do
74
- f5_host = @hosts[4]
75
- result_mock = Beaker::Result.new(f5_host, '')
76
- result_mock.exit_code = 2
77
- allow( f5_host ).to receive( :exec ).and_return(result_mock)
78
- expect( aws ).to receive( :backoff_sleep ).exactly(9).times
79
- expect{ aws.enable_root_f5(f5_host) }.to raise_error( RuntimeError, /unable/ )
113
+ it 'terminates the running instance' do
114
+ instance_set.each do |instance|
115
+ expect(instance).to receive(:terminate).once
116
+ end
117
+ expect(kill_instances).to be_nil
118
+ end
119
+
120
+ it 'verifies instance is not nil' do
121
+ instance_set.each do |instance|
122
+ expect(instance).to receive(:nil?)
123
+ allow(instance).to receive(:terminate).once
124
+ end
125
+ expect(kill_instances).to be_nil
126
+ end
127
+
128
+ it 'verifies instance exists in AWS' do
129
+ instance_set.each do |instance|
130
+ expect(instance).to receive(:exists?)
131
+ allow(instance).to receive(:terminate).once
132
+ end
133
+ expect(kill_instances).to be_nil
80
134
  end
135
+ end
81
136
 
137
+ context 'when an instance does not exist' do
138
+ let( :instance_set ) { [unreal_instance] }
139
+
140
+ it 'does not call terminate' do
141
+ instance_set.each do |instance|
142
+ expect(instance).to receive(:terminate).exactly(0).times
143
+ end
144
+ expect(kill_instances).to be_nil
145
+ end
146
+
147
+ it 'verifies instance does not exist' do
148
+ instance_set.each do |instance|
149
+ expect(instance).to receive(:exists?).once
150
+ allow(instance).to receive(:terminate).exactly(0).times
151
+ end
152
+ expect(kill_instances).to be_nil
153
+ end
82
154
  end
83
- end
84
155
 
85
- context '#backoff_sleep' do
86
- it "should call sleep 1024 times at attempt 10" do
87
- expect_any_instance_of( Object ).to receive(:sleep).with(1024)
88
- aws.backoff_sleep(10)
156
+ context 'when an instance is nil' do
157
+ let( :instance_set ) { [nil_instance] }
158
+
159
+ it 'does not call terminate' do
160
+ instance_set.each do |instance|
161
+ expect(instance).to receive(:terminate).exactly(0).times
162
+ end
163
+ expect(kill_instances).to be_nil
164
+ end
165
+
166
+ it 'verifies instance is nil' do
167
+ instance_set.each do |instance|
168
+ expect(instance).to receive(:nil?).once
169
+ allow(instance).to receive(:terminate).exactly(0).times
170
+ end
171
+ expect(kill_instances).to be_nil
172
+ end
89
173
  end
174
+
90
175
  end
91
176
 
92
- context '#public_key' do
93
- it "retrieves contents from local ~/.ssh/ssh_rsa.pub file" do
94
- # Stub calls to file read/exists
95
- allow(File).to receive(:exists?).with(/id_rsa.pub/) { true }
96
- allow(File).to receive(:read).with(/id_rsa.pub/) { "foobar" }
177
+ describe '#cleanup' do
178
+ subject(:cleanup) { aws.cleanup }
179
+ let( :ec2_instance ) { double('ec2_instance', :nil? => false, :exists? => true, :terminate => nil, :id => 'id') }
97
180
 
98
- # Should return contents of allow( previously ).to receivebed id_rsa.pub
99
- expect(aws.public_key).to eq("foobar")
181
+ context 'with a list of hosts' do
182
+ before :each do
183
+ @hosts.each {|host| host['instance'] = ec2_instance}
184
+ end
185
+
186
+ it { is_expected.to be_nil }
187
+
188
+ it 'kills instances' do
189
+ expect(aws).to receive(:kill_instances).once
190
+ expect(cleanup).to be_nil
191
+ end
100
192
  end
101
193
 
102
- it "should return an error if the files do not exist" do
103
- expect { aws.public_key }.to raise_error(RuntimeError, /Expected either/)
194
+ context 'with an empty host list' do
195
+ before :each do
196
+ @hosts = []
197
+ end
198
+
199
+ it { is_expected.to be_nil }
200
+
201
+ it 'kills instances' do
202
+ expect(aws).to receive(:kill_instances).once
203
+ expect(cleanup).to be_nil
204
+ end
104
205
  end
105
206
  end
106
207
 
107
- context '#key_name' do
108
- it 'returns a key name from the local hostname' do
109
- # Mock out the hostname and local user calls
110
- expect( Socket ).to receive(:gethostname) { "foobar" }
111
- expect( aws ).to receive(:local_user) { "bob" }
208
+ describe '#log_instances', :wip do
209
+ end
112
210
 
113
- # Should match the expected composite key name
114
- expect(aws.key_name).to eq("Beaker-bob-foobar")
211
+ describe '#instance_by_id' do
212
+ subject { aws.instance_by_id('my_id') }
213
+ it { is_expected.to be_instance_of(AWS::EC2::Instance) }
214
+ end
215
+
216
+ describe '#instances' do
217
+ subject { aws.instances }
218
+ it { is_expected.to be_instance_of(AWS::EC2::InstanceCollection) }
219
+ end
220
+
221
+ describe '#vpc_by_id' do
222
+ subject { aws.vpc_by_id('my_id') }
223
+ it { is_expected.to be_instance_of(AWS::EC2::VPC) }
224
+ end
225
+
226
+ describe '#vpcs' do
227
+ subject { aws.vpcs }
228
+ it { is_expected.to be_instance_of(AWS::EC2::VPCCollection) }
229
+ end
230
+
231
+ describe '#security_group_by_id' do
232
+ subject { aws.security_group_by_id('my_id') }
233
+ it { is_expected.to be_instance_of(AWS::EC2::SecurityGroup) }
234
+ end
235
+
236
+ describe '#security_groups' do
237
+ subject { aws.security_groups }
238
+ it { is_expected.to be_instance_of(AWS::EC2::SecurityGroupCollection) }
239
+ end
240
+
241
+ describe '#kill_zombies', :wip do
242
+ end
243
+
244
+ describe '#kill_zombie_volumes', :wip do
245
+ end
246
+
247
+ describe '#create_instance', :wip do
248
+ end
249
+
250
+ describe '#launch_nodes_on_some_subnet', :wip do
251
+ end
252
+
253
+ describe '#launch_all_nodes', :wip do
254
+ end
255
+
256
+ describe '#wait_for_status' do
257
+ let( :aws_instance ) { double('aws_instance', :id => "ec2", :terminate => nil) }
258
+ let( :instance_set ) { [{:instance => aws_instance}] }
259
+ subject(:wait_for_status) { aws.wait_for_status(:running, instance_set) }
260
+
261
+ it 'handles a single instance' do
262
+ allow(aws_instance).to receive(:status).and_return(:waiting, :waiting, :running)
263
+ expect(aws).to receive(:backoff_sleep).exactly(3).times
264
+ expect(wait_for_status).to eq(instance_set)
265
+ end
266
+
267
+ context 'with multiple instances' do
268
+ let( :instance_set ) { [{:instance => aws_instance}, {:instance => aws_instance}] }
269
+
270
+ it 'returns the instance set passed to it' do
271
+ allow(aws_instance).to receive(:status).and_return(:waiting, :waiting, :running, :waiting, :waiting, :running)
272
+ allow(aws).to receive(:backoff_sleep).exactly(6).times
273
+ expect(wait_for_status).to eq(instance_set)
274
+ end
275
+
276
+ it 'calls backoff_sleep once per instance.status call' do
277
+ allow(aws_instance).to receive(:status).and_return(:waiting, :waiting, :running, :waiting, :waiting, :running)
278
+ expect(aws).to receive(:backoff_sleep).exactly(6).times
279
+ expect(wait_for_status).to eq(instance_set)
280
+ end
281
+ end
282
+
283
+ context 'after 10 tries' do
284
+ it 'raises RuntimeError' do
285
+ expect(aws_instance).to receive(:status).and_return(:waiting).exactly(10).times
286
+ expect(aws).to receive(:backoff_sleep).exactly(9).times
287
+ expect { wait_for_status }.to raise_error('Instance never reached state running')
288
+ end
289
+ end
290
+
291
+ context 'with an invalid instance' do
292
+ it 'raises AWS::EC2::Errors::InvalidInstanceID::NotFound' do
293
+ expect(aws_instance).to receive(:status).and_raise(AWS::EC2::Errors::InvalidInstanceID::NotFound).exactly(10).times
294
+ allow(aws).to receive(:backoff_sleep).at_most(10).times
295
+ expect(wait_for_status).to eq(instance_set)
296
+ end
115
297
  end
116
298
  end
117
299
 
118
- context '#group_id' do
119
- it 'should return a predicatable group_id from a port list' do
120
- expect(aws.group_id([22, 1024])).to eq("Beaker-2799478787")
300
+ describe '#add_tags' do
301
+ let( :aws_instance ) { double('aws_instance', :add_tag => nil) }
302
+ subject(:add_tags) { aws.add_tags }
303
+
304
+ it 'returns nil' do
305
+ @hosts.each {|host| host['instance'] = aws_instance}
306
+ expect(add_tags).to be_nil
121
307
  end
122
308
 
123
- it 'should return a predicatable group_id from an empty list' do
124
- expect { aws.group_id([]) }.to raise_error(ArgumentError, "Ports list cannot be nil or empty")
309
+ it 'handles a single host' do
310
+ @hosts[0]['instance'] = aws_instance
311
+ @hosts = [@hosts[0]]
312
+ expect(add_tags).to be_nil
313
+ end
314
+
315
+ context 'with multiple hosts' do
316
+ before :each do
317
+ @hosts.each {|host| host['instance'] = aws_instance}
318
+ end
319
+
320
+ it 'adds tag for jenkins_build_url' do
321
+ aws.instance_eval('@options[:jenkins_build_url] = "my_build_url"')
322
+ expect(aws_instance).to receive(:add_tag).with('jenkins_build_url', hash_including(:value => 'my_build_url')).at_least(:once)
323
+ expect(add_tags).to be_nil
324
+ end
325
+
326
+ it 'adds tag for Name' do
327
+ expect(aws_instance).to receive(:add_tag).with('Name', hash_including(:value => /vm/)).at_least(@hosts.size).times
328
+ expect(add_tags).to be_nil
329
+ end
330
+
331
+ it 'adds tag for department' do
332
+ aws.instance_eval('@options[:department] = "my_department"')
333
+ expect(aws_instance).to receive(:add_tag).with('department', hash_including(:value => 'my_department')).at_least(:once)
334
+ expect(add_tags).to be_nil
335
+ end
336
+
337
+ it 'adds tag for project' do
338
+ aws.instance_eval('@options[:project] = "my_project"')
339
+ expect(aws_instance).to receive(:add_tag).with('project', hash_including(:value => 'my_project')).at_least(:once)
340
+ expect(add_tags).to be_nil
341
+ end
342
+
343
+ it 'adds tag for created_by' do
344
+ aws.instance_eval('@options[:created_by] = "my_created_by"')
345
+ expect(aws_instance).to receive(:add_tag).with('created_by', hash_including(:value => 'my_created_by')).at_least(:once)
346
+ expect(add_tags).to be_nil
347
+ end
125
348
  end
126
349
  end
127
350
 
128
351
  describe '#populate_dns' do
129
352
  let( :vpc_instance ) { {ip_address: nil, private_ip_address: "vpc_private_ip", dns_name: "vpc_dns_name"} }
130
353
  let( :ec2_instance ) { {ip_address: "ec2_public_ip", private_ip_address: "ec2_private_ip", dns_name: "ec2_dns_name"} }
354
+ subject(:populate_dns) { aws.populate_dns }
131
355
 
132
356
  context 'on a public EC2 instance' do
133
357
  before :each do
@@ -135,7 +359,7 @@ module Beaker
135
359
  end
136
360
 
137
361
  it 'sets host ip to instance.ip_address' do
138
- aws.populate_dns();
362
+ populate_dns
139
363
  hosts = aws.instance_variable_get( :@hosts )
140
364
  hosts.each do |host|
141
365
  expect(host['ip']).to eql(ec2_instance[:ip_address])
@@ -143,7 +367,7 @@ module Beaker
143
367
  end
144
368
 
145
369
  it 'sets host private_ip to instance.private_ip_address' do
146
- aws.populate_dns();
370
+ populate_dns
147
371
  hosts = aws.instance_variable_get( :@hosts )
148
372
  hosts.each do |host|
149
373
  expect(host['private_ip']).to eql(ec2_instance[:private_ip_address])
@@ -151,7 +375,7 @@ module Beaker
151
375
  end
152
376
 
153
377
  it 'sets host dns_name to instance.dns_name' do
154
- aws.populate_dns();
378
+ populate_dns
155
379
  hosts = aws.instance_variable_get( :@hosts )
156
380
  hosts.each do |host|
157
381
  expect(host['dns_name']).to eql(ec2_instance[:dns_name])
@@ -165,7 +389,7 @@ module Beaker
165
389
  end
166
390
 
167
391
  it 'sets host ip to instance.private_ip_address' do
168
- aws.populate_dns();
392
+ populate_dns
169
393
  hosts = aws.instance_variable_get( :@hosts )
170
394
  hosts.each do |host|
171
395
  expect(host['ip']).to eql(vpc_instance[:private_ip_address])
@@ -173,7 +397,7 @@ module Beaker
173
397
  end
174
398
 
175
399
  it 'sets host private_ip to instance.private_ip_address' do
176
- aws.populate_dns();
400
+ populate_dns
177
401
  hosts = aws.instance_variable_get( :@hosts )
178
402
  hosts.each do |host|
179
403
  expect(host['private_ip']).to eql(vpc_instance[:private_ip_address])
@@ -181,7 +405,7 @@ module Beaker
181
405
  end
182
406
 
183
407
  it 'sets host dns_name to instance.dns_name' do
184
- aws.populate_dns();
408
+ populate_dns
185
409
  hosts = aws.instance_variable_get( :@hosts )
186
410
  hosts.each do |host|
187
411
  expect(host['dns_name']).to eql(vpc_instance[:dns_name])
@@ -189,5 +413,318 @@ module Beaker
189
413
  end
190
414
  end
191
415
  end
416
+
417
+ describe '#etc_hosts_entry' do
418
+ let( :host ) { @hosts[0] }
419
+ let( :interface ) { :ip }
420
+ subject(:etc_hosts_entry) { aws.etc_hosts_entry(host, interface) }
421
+
422
+ it 'returns a predictable host entry' do
423
+ expect(aws).to receive(:get_domain_name).and_return('lan')
424
+ expect(etc_hosts_entry).to eq("ip.address.for.vm1\tvm1 vm1.lan vm1.box.tld\n")
425
+ end
426
+
427
+ context 'when :private_ip is requested' do
428
+ let( :interface ) { :private_ip }
429
+ it 'returns host entry for the private_ip' do
430
+ host = @hosts[0]
431
+ expect(aws).to receive(:get_domain_name).and_return('lan')
432
+ expect(etc_hosts_entry).to eq("private.ip.for.vm1\tvm1 vm1.lan vm1.box.tld\n")
433
+ end
434
+ end
435
+ end
436
+
437
+ describe '#configure_hosts' do
438
+ subject(:configure_hosts) { aws.configure_hosts }
439
+
440
+ it { is_expected.to be_nil }
441
+
442
+ context 'calls #set_etc_hosts' do
443
+ it 'for each host' do
444
+ expect(aws).to receive(:set_etc_hosts).exactly(@hosts.size).times
445
+ expect(configure_hosts).to be_nil
446
+ end
447
+
448
+ it 'with predictable host entries' do
449
+ @hosts = [@hosts[0], @hosts[1]]
450
+ entries = "127.0.0.1\tlocalhost localhost.localdomain\n"\
451
+ "private.ip.for.vm1\tvm1 vm1.lan vm1.box.tld\n"\
452
+ "ip.address.for.vm2\tvm2 vm2.lan vm2.box.tld\n"
453
+ allow(aws).to receive(:get_domain_name).and_return('lan')
454
+ expect(aws).to receive(:set_etc_hosts).with(@hosts[0], entries)
455
+ expect(aws).to receive(:set_etc_hosts).with(@hosts[1], anything)
456
+ expect(configure_hosts).to be_nil
457
+ end
458
+ end
459
+ end
460
+
461
+ describe '#enable_root_on_hosts' do
462
+ context 'enabling root shall be called once for the ubuntu machine' do
463
+ it "should enable root once" do
464
+ expect( aws ).to receive(:copy_ssh_to_root).with( @hosts[3], options ).once()
465
+ expect( aws ).to receive(:enable_root_login).with( @hosts[3], options).once()
466
+ aws.enable_root_on_hosts();
467
+ end
468
+ end
469
+
470
+ it 'enables root once on the f5 host through its code path' do
471
+ expect( aws ).to receive(:enable_root_f5).with( @hosts[4] ).once()
472
+ aws.enable_root_on_hosts()
473
+ end
474
+ end
475
+
476
+ describe '#enable_root_f5' do
477
+ let( :f5_host ) { @hosts[4] }
478
+ subject(:enable_root_f5) { aws.enable_root_f5(f5_host) }
479
+
480
+ it 'creates a password on the host' do
481
+ result_mock = Beaker::Result.new(f5_host, '')
482
+ result_mock.exit_code = 0
483
+ allow( f5_host ).to receive( :exec ).and_return(result_mock)
484
+ allow( aws ).to receive( :backoff_sleep )
485
+ sha_mock = Object.new
486
+ allow( Digest::SHA256 ).to receive( :new ).and_return(sha_mock)
487
+ expect( sha_mock ).to receive( :hexdigest ).once()
488
+ enable_root_f5
489
+ end
490
+
491
+ it 'tries 10x before failing correctly' do
492
+ result_mock = Beaker::Result.new(f5_host, '')
493
+ result_mock.exit_code = 2
494
+ allow( f5_host ).to receive( :exec ).and_return(result_mock)
495
+ expect( aws ).to receive( :backoff_sleep ).exactly(9).times
496
+ expect{ enable_root_f5 }.to raise_error( RuntimeError, /unable/ )
497
+ end
498
+ end
499
+
500
+ describe '#set_hostnames' do
501
+ subject(:set_hostnames) { aws.set_hostnames }
502
+ it 'returns @hosts' do
503
+ expect(set_hostnames).to eq(@hosts)
504
+ end
505
+
506
+ context 'for each host' do
507
+ it 'calls exec' do
508
+ @hosts.each {|host| expect(host).to receive(:exec).once}
509
+ expect(set_hostnames).to eq(@hosts)
510
+ end
511
+
512
+ it 'passes a Command instance to exec' do
513
+ @hosts.each do |host|
514
+ expect(host).to receive(:exec).with( instance_of(Beaker::Command) ).once
515
+ end
516
+ expect(set_hostnames).to eq(@hosts)
517
+ end
518
+ end
519
+ end
520
+
521
+ describe '#backoff_sleep' do
522
+ it "should call sleep 1024 times at attempt 10" do
523
+ expect_any_instance_of( Object ).to receive(:sleep).with(1024)
524
+ aws.backoff_sleep(10)
525
+ end
526
+ end
527
+
528
+ describe '#public_key' do
529
+ subject(:public_key) { aws.public_key }
530
+
531
+ it "retrieves contents from local ~/.ssh/ssh_rsa.pub file" do
532
+ # Stub calls to file read/exists
533
+ allow(File).to receive(:exists?).with(/id_rsa.pub/) { true }
534
+ allow(File).to receive(:read).with(/id_rsa.pub/) { "foobar" }
535
+
536
+ # Should return contents of allow( previously ).to receivebed id_rsa.pub
537
+ expect(public_key).to eq("foobar")
538
+ end
539
+
540
+ it "should return an error if the files do not exist" do
541
+ expect { public_key }.to raise_error(RuntimeError, /Expected either/)
542
+ end
543
+ end
544
+
545
+ describe '#key_name' do
546
+ it 'returns a key name from the local hostname' do
547
+ # Mock out the hostname and local user calls
548
+ expect( Socket ).to receive(:gethostname) { "foobar" }
549
+ expect( aws ).to receive(:local_user) { "bob" }
550
+
551
+ # Should match the expected composite key name
552
+ expect(aws.key_name).to eq("Beaker-bob-foobar")
553
+ end
554
+ end
555
+
556
+ describe '#local_user' do
557
+ it 'returns ENV["USER"]' do
558
+ stub_const('ENV', ENV.to_hash.merge('USER' => 'SuperUser'))
559
+ expect(aws.local_user).to eq("SuperUser")
560
+ end
561
+ end
562
+
563
+ describe '#ensure_key_pair' do
564
+ let( :region ) { double('region') }
565
+ subject(:ensure_key_pair) { aws.ensure_key_pair(region) }
566
+
567
+ context 'when a beaker keypair already exists' do
568
+ it 'returns the keypair if available' do
569
+ stub_const('ENV', ENV.to_hash.merge('USER' => 'rspec'))
570
+ key_pair = double(:exists? => true, :secret => 'supersekritkey')
571
+ key_pairs = { "Beaker-rspec-SUT" => key_pair }
572
+
573
+ expect( region ).to receive(:key_pairs).and_return(key_pairs).once
574
+ expect( Socket ).to receive(:gethostname).and_return("SUT")
575
+ expect(ensure_key_pair).to eq(key_pair)
576
+ end
577
+ end
578
+
579
+ context 'when a pre-existing keypair cannot be found' do
580
+ let( :key_name ) { "Beaker-rspec-SUT" }
581
+ let( :key_pair ) { double(:exists? => false) }
582
+ let( :key_pairs ) { { key_name => key_pair } }
583
+ let( :pubkey ) { "Beaker-rspec-SUT_secret-key" }
584
+
585
+ before :each do
586
+ stub_const('ENV', ENV.to_hash.merge('USER' => 'rspec'))
587
+ expect( region ).to receive(:key_pairs).and_return(key_pairs).once
588
+ expect( Socket ).to receive(:gethostname).and_return("SUT")
589
+ end
590
+
591
+ it 'imports a new key based on user pubkey' do
592
+ allow(aws).to receive(:public_key).and_return(pubkey)
593
+ expect( key_pairs ).to receive(:import).with(key_name, pubkey)
594
+ expect(ensure_key_pair)
595
+ end
596
+
597
+ it 'returns imported keypair' do
598
+ allow(aws).to receive(:public_key)
599
+ expect( key_pairs ).to receive(:import).and_return(key_pair).once
600
+ expect(ensure_key_pair).to eq(key_pair)
601
+ end
602
+ end
603
+ end
604
+
605
+ describe '#group_id' do
606
+ it 'should return a predicatable group_id from a port list' do
607
+ expect(aws.group_id([22, 1024])).to eq("Beaker-2799478787")
608
+ end
609
+
610
+ it 'should return a predicatable group_id from an empty list' do
611
+ expect { aws.group_id([]) }.to raise_error(ArgumentError, "Ports list cannot be nil or empty")
612
+ end
613
+ end
614
+
615
+ describe '#ensure_group' do
616
+ let( :vpc ) { double('vpc') }
617
+ let( :ports ) { [22, 80, 8080] }
618
+ subject(:ensure_group) { aws.ensure_group(vpc, ports) }
619
+
620
+ context 'for an existing group' do
621
+ before :each do
622
+ @group = double(:nil? => false)
623
+ end
624
+
625
+ it 'returns group from vpc lookup' do
626
+ expect(vpc).to receive_message_chain('security_groups.filter.first').and_return(@group)
627
+ expect(ensure_group).to eq(@group)
628
+ end
629
+
630
+ context 'during group lookup' do
631
+ it 'performs group_id lookup for ports' do
632
+ expect(aws).to receive(:group_id).with(ports)
633
+ expect(vpc).to receive_message_chain('security_groups.filter.first').and_return(@group)
634
+ expect(ensure_group).to eq(@group)
635
+ end
636
+
637
+ it 'filters on group_id' do
638
+ expect(vpc).to receive(:security_groups).and_return(vpc)
639
+ expect(vpc).to receive(:filter).with('group-name', 'Beaker-1521896090').and_return(vpc)
640
+ expect(vpc).to receive(:first).and_return(@group)
641
+ expect(ensure_group).to eq(@group)
642
+ end
643
+ end
644
+ end
645
+
646
+ context 'when group does not exist' do
647
+ it 'creates group if group.nil?' do
648
+ group = double(:nil? => true)
649
+ expect(aws).to receive(:create_group).with(vpc, ports).and_return(group)
650
+ expect(vpc).to receive_message_chain('security_groups.filter.first').and_return(group)
651
+ expect(ensure_group).to eq(group)
652
+ end
653
+ end
654
+ end
655
+
656
+ describe '#create_group' do
657
+ let( :rv ) { double('rv') }
658
+ let( :ports ) { [22, 80, 8080] }
659
+ subject(:create_group) { aws.create_group(rv, ports) }
660
+
661
+ before :each do
662
+ @group = double(:nil? => false)
663
+ end
664
+
665
+ it 'returns a newly created group' do
666
+ allow(rv).to receive_message_chain('security_groups.create').and_return(@group)
667
+ allow(@group).to receive(:authorize_ingress).at_least(:once)
668
+ expect(create_group).to eq(@group)
669
+ end
670
+
671
+ it 'requests group_id for ports given' do
672
+ expect(aws).to receive(:group_id).with(ports)
673
+ allow(rv).to receive_message_chain('security_groups.create').and_return(@group)
674
+ allow(@group).to receive(:authorize_ingress).at_least(:once)
675
+ expect(create_group).to eq(@group)
676
+ end
677
+
678
+ it 'creates group with expected arguments' do
679
+ group_name = "Beaker-1521896090"
680
+ group_desc = "Custom Beaker security group for #{ports.to_a}"
681
+ expect(rv).to receive_message_chain('security_groups.create')
682
+ .with(group_name, :description => group_desc)
683
+ .and_return(@group)
684
+ allow(@group).to receive(:authorize_ingress).at_least(:once)
685
+ expect(create_group).to eq(@group)
686
+ end
687
+
688
+ it 'authorizes requested ports for group' do
689
+ expect(rv).to receive_message_chain('security_groups.create').and_return(@group)
690
+ ports.each do |port|
691
+ expect(@group).to receive(:authorize_ingress).with(:tcp, port).once
692
+ end
693
+ expect(create_group).to eq(@group)
694
+ end
695
+ end
696
+
697
+ describe '#load_fog_credentials' do
698
+ # Receive#and_call_original below allows us to test the core load_fog_credentials method
699
+ let(:creds) { {:access_key => 'awskey', :secret_key => 'awspass'} }
700
+ let(:dot_fog) { '.fog' }
701
+ subject(:load_fog_credentials) { aws.load_fog_credentials(dot_fog) }
702
+
703
+ it 'returns loaded fog credentials' do
704
+ fog_hash = {:default => {:aws_access_key_id => 'awskey', :aws_secret_access_key => 'awspass'}}
705
+ expect(aws).to receive(:load_fog_credentials).and_call_original
706
+ expect(YAML).to receive(:load_file).and_return(fog_hash)
707
+ expect(load_fog_credentials).to eq(creds)
708
+ end
709
+
710
+ context 'raises errors' do
711
+ it 'if missing access_key credential' do
712
+ fog_hash = {:default => {:aws_secret_access_key => 'awspass'}}
713
+ err_text = "You must specify an aws_access_key_id in your .fog file (#{dot_fog}) for ec2 instances!"
714
+ expect(aws).to receive(:load_fog_credentials).and_call_original
715
+ expect(YAML).to receive(:load_file).and_return(fog_hash)
716
+ expect { load_fog_credentials }.to raise_error(err_text)
717
+ end
718
+
719
+ it 'if missing secret_key credential' do
720
+ dot_fog = '.fog'
721
+ fog_hash = {:default => {:aws_access_key_id => 'awskey'}}
722
+ err_text = "You must specify an aws_secret_access_key in your .fog file (#{dot_fog}) for ec2 instances!"
723
+ expect(aws).to receive(:load_fog_credentials).and_call_original
724
+ expect(YAML).to receive(:load_file).and_return(fog_hash)
725
+ expect { load_fog_credentials }.to raise_error(err_text)
726
+ end
727
+ end
728
+ end
192
729
  end
193
730
  end