beaker-aws 0.1.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.
@@ -0,0 +1,1018 @@
1
+ require 'spec_helper'
2
+
3
+ module Beaker
4
+ describe AwsSdk do
5
+ let( :options ) { make_opts.merge({ 'logger' => double().as_null_object, 'timestamp' => Time.now }) }
6
+ let(:aws) {
7
+ # Mock out the call to load_fog_credentials
8
+ allow_any_instance_of( Beaker::AwsSdk ).
9
+ to receive(:load_fog_credentials).
10
+ and_return({
11
+ :access_key => fog_file_contents[:default][:aws_access_key_id],
12
+ :secret_key => fog_file_contents[:default][:aws_secret_access_key],
13
+ })
14
+
15
+
16
+ # This is needed because the EC2 api looks up a local endpoints.json file
17
+ FakeFS.deactivate!
18
+ aws = Beaker::AwsSdk.new(@hosts, options)
19
+ FakeFS.activate!
20
+
21
+ aws
22
+ }
23
+ let(:amispec) {{
24
+ "centos-5-x86-64-west" => {
25
+ :image => {:pe => "ami-sekrit1"},
26
+ :region => "us-west-2",
27
+ },
28
+ "centos-6-x86-64-west" => {
29
+ :image => {:pe => "ami-sekrit2"},
30
+ :region => "us-west-2",
31
+ },
32
+ "centos-7-x86-64-west" => {
33
+ :image => {:pe => "ami-sekrit3"},
34
+ :region => "us-west-2",
35
+ },
36
+ "ubuntu-12.04-amd64-west" => {
37
+ :image => {:pe => "ami-sekrit4"},
38
+ :region => "us-west-2"
39
+ },
40
+ }}
41
+
42
+ before :each do
43
+ @hosts = make_hosts({:snapshot => :pe}, 6)
44
+ @hosts[0][:platform] = "centos-5-x86-64-west"
45
+ @hosts[1][:platform] = "centos-6-x86-64-west"
46
+ @hosts[2][:platform] = "centos-7-x86-64-west"
47
+ @hosts[3][:platform] = "ubuntu-12.04-amd64-west"
48
+ @hosts[3][:user] = "ubuntu"
49
+ @hosts[4][:platform] = 'f5-host'
50
+ @hosts[4][:user] = 'notroot'
51
+ @hosts[5][:platform] = 'netscaler-host'
52
+
53
+ ENV['AWS_ACCESS_KEY'] = nil
54
+ ENV['AWS_SECRET_ACCESS_KEY'] = nil
55
+ end
56
+
57
+ context 'loading credentials' do
58
+
59
+ it 'from .fog file' do
60
+ creds = aws.load_fog_credentials
61
+ expect( creds[:access_key] ).to eq("IMANACCESSKEY")
62
+ expect( creds[:secret_key] ).to eq("supersekritkey")
63
+ end
64
+
65
+
66
+ it 'from environment variables' do
67
+ ENV['AWS_ACCESS_KEY_ID'] = "IMANACCESSKEY"
68
+ ENV['AWS_SECRET_ACCESS_KEY'] = "supersekritkey"
69
+
70
+ creds = aws.load_env_credentials
71
+ expect( creds[:access_key] ).to eq("IMANACCESSKEY")
72
+ expect( creds[:secret_key] ).to eq("supersekritkey")
73
+ end
74
+ end
75
+
76
+ describe '#provision' do
77
+ before :each do
78
+ expect(aws).to receive(:launch_all_nodes)
79
+ expect(aws).to receive(:add_tags)
80
+ expect(aws).to receive(:populate_dns)
81
+ expect(aws).to receive(:enable_root_on_hosts)
82
+ expect(aws).to receive(:set_hostnames)
83
+ expect(aws).to receive(:configure_hosts)
84
+ end
85
+
86
+ it 'should step through provisioning' do
87
+ allow( aws ).to receive( :wait_for_status_netdev )
88
+ aws.provision
89
+ end
90
+
91
+ it 'should return nil' do
92
+ allow( aws ).to receive( :wait_for_status_netdev )
93
+ expect(aws.provision).to be_nil
94
+ end
95
+ end
96
+
97
+ describe '#kill_instances' do
98
+ let( :ec2_instance ) { double('ec2_instance', :nil? => false, :exists? => true, :id => "ec2", :terminate => nil) }
99
+ let( :vpc_instance ) { double('vpc_instance', :nil? => false, :exists? => true, :id => "vpc", :terminate => nil) }
100
+ let( :nil_instance ) { double('vpc_instance', :nil? => true, :exists? => true, :id => "nil", :terminate => nil) }
101
+ let( :unreal_instance ) { double('vpc_instance', :nil? => false, :exists? => false, :id => "unreal", :terminate => nil) }
102
+ subject(:kill_instances) { aws.kill_instances(instance_set) }
103
+
104
+ it 'should return nil' do
105
+ instance_set = [ec2_instance, vpc_instance, nil_instance, unreal_instance]
106
+ expect(aws.kill_instances(instance_set)).to be_nil
107
+ end
108
+
109
+ it 'cleanly handles an empty instance list' do
110
+ instance_set = []
111
+ expect(aws.kill_instances(instance_set)).to be_nil
112
+ end
113
+
114
+ context 'in general use' do
115
+ let( :instance_set ) { [ec2_instance, vpc_instance] }
116
+
117
+ it 'terminates each running instance' do
118
+ instance_set.each do |instance|
119
+ expect(instance).to receive(:terminate).once
120
+ end
121
+ expect(kill_instances).to be_nil
122
+ end
123
+
124
+ it 'verifies instances are not nil' do
125
+ instance_set.each do |instance|
126
+ expect(instance).to receive(:nil?)
127
+ allow(instance).to receive(:terminate).once
128
+ end
129
+ expect(kill_instances).to be_nil
130
+ end
131
+
132
+ it 'verifies instances exist in AWS' do
133
+ instance_set.each do |instance|
134
+ expect(instance).to receive(:exists?)
135
+ allow(instance).to receive(:terminate).once
136
+ end
137
+ expect(kill_instances).to be_nil
138
+ end
139
+ end
140
+
141
+ context 'for a single running instance' do
142
+ let( :instance_set ) { [ec2_instance] }
143
+
144
+ it 'terminates the running instance' do
145
+ instance_set.each do |instance|
146
+ expect(instance).to receive(:terminate).once
147
+ end
148
+ expect(kill_instances).to be_nil
149
+ end
150
+
151
+ it 'verifies instance is not nil' do
152
+ instance_set.each do |instance|
153
+ expect(instance).to receive(:nil?)
154
+ allow(instance).to receive(:terminate).once
155
+ end
156
+ expect(kill_instances).to be_nil
157
+ end
158
+
159
+ it 'verifies instance exists in AWS' do
160
+ instance_set.each do |instance|
161
+ expect(instance).to receive(:exists?)
162
+ allow(instance).to receive(:terminate).once
163
+ end
164
+ expect(kill_instances).to be_nil
165
+ end
166
+ end
167
+
168
+ context 'when an instance does not exist' do
169
+ let( :instance_set ) { [unreal_instance] }
170
+
171
+ it 'does not call terminate' do
172
+ instance_set.each do |instance|
173
+ expect(instance).to receive(:terminate).exactly(0).times
174
+ end
175
+ expect(kill_instances).to be_nil
176
+ end
177
+
178
+ it 'verifies instance does not exist' do
179
+ instance_set.each do |instance|
180
+ expect(instance).to receive(:exists?).once
181
+ allow(instance).to receive(:terminate).exactly(0).times
182
+ end
183
+ expect(kill_instances).to be_nil
184
+ end
185
+ end
186
+
187
+ context 'when an instance is nil' do
188
+ let( :instance_set ) { [nil_instance] }
189
+
190
+ it 'does not call terminate' do
191
+ instance_set.each do |instance|
192
+ expect(instance).to receive(:terminate).exactly(0).times
193
+ end
194
+ expect(kill_instances).to be_nil
195
+ end
196
+
197
+ it 'verifies instance is nil' do
198
+ instance_set.each do |instance|
199
+ expect(instance).to receive(:nil?).once
200
+ allow(instance).to receive(:terminate).exactly(0).times
201
+ end
202
+ expect(kill_instances).to be_nil
203
+ end
204
+ end
205
+
206
+ end
207
+
208
+ describe '#cleanup' do
209
+ subject(:cleanup) { aws.cleanup }
210
+ let( :ec2_instance ) { double('ec2_instance', :nil? => false, :exists? => true, :terminate => nil, :id => 'id') }
211
+
212
+ context 'with a list of hosts' do
213
+ before :each do
214
+ @hosts.each {|host| host['instance'] = ec2_instance}
215
+ expect(aws).to receive( :delete_key_pair_all_regions )
216
+ end
217
+
218
+ it { is_expected.to be_nil }
219
+
220
+ it 'kills instances' do
221
+ expect(aws).to receive(:kill_instances).once
222
+ expect(cleanup).to be_nil
223
+ end
224
+ end
225
+
226
+ context 'with an empty host list' do
227
+ before :each do
228
+ @hosts = []
229
+ expect(aws).to receive( :delete_key_pair_all_regions )
230
+ end
231
+
232
+ it { is_expected.to be_nil }
233
+
234
+ it 'kills instances' do
235
+ expect(aws).to receive(:kill_instances).once
236
+ expect(cleanup).to be_nil
237
+ end
238
+ end
239
+ end
240
+
241
+ describe '#log_instances', :wip do
242
+ end
243
+
244
+ describe '#instance_by_id' do
245
+ subject { aws.instance_by_id('my_id') }
246
+ it { is_expected.to be_instance_of(AWS::EC2::Instance) }
247
+ end
248
+
249
+ describe '#instances' do
250
+ subject { aws.instances }
251
+ it { is_expected.to be_instance_of(AWS::EC2::InstanceCollection) }
252
+ end
253
+
254
+ describe '#vpc_by_id' do
255
+ subject { aws.vpc_by_id('my_id') }
256
+ it { is_expected.to be_instance_of(AWS::EC2::VPC) }
257
+ end
258
+
259
+ describe '#vpcs' do
260
+ subject { aws.vpcs }
261
+ it { is_expected.to be_instance_of(AWS::EC2::VPCCollection) }
262
+ end
263
+
264
+ describe '#security_group_by_id' do
265
+ subject { aws.security_group_by_id('my_id') }
266
+ it { is_expected.to be_instance_of(AWS::EC2::SecurityGroup) }
267
+ end
268
+
269
+ describe '#security_groups' do
270
+ subject { aws.security_groups }
271
+ it { is_expected.to be_instance_of(AWS::EC2::SecurityGroupCollection) }
272
+ end
273
+
274
+ describe '#kill_zombies' do
275
+ it 'calls delete_key_pair_all_regions' do
276
+ ec2_mock = Object.new
277
+ allow(ec2_mock).to receive( :regions ).and_return( {} )
278
+ aws.instance_variable_set( :@ec2, ec2_mock )
279
+
280
+ expect( aws ).to receive( :delete_key_pair_all_regions ).once
281
+
282
+ aws.kill_zombies()
283
+ end
284
+ end
285
+
286
+ describe '#kill_zombie_volumes', :wip do
287
+ end
288
+
289
+ describe '#create_instance', :wip do
290
+ end
291
+
292
+ describe '#launch_nodes_on_some_subnet', :wip do
293
+ end
294
+
295
+ describe '#launch_all_nodes', :wip do
296
+ end
297
+
298
+ describe '#wait_for_status' do
299
+ let( :aws_instance ) { double('aws_instance', :id => "ec2", :terminate => nil) }
300
+ let( :instance_set ) { [{:instance => aws_instance}] }
301
+ subject(:wait_for_status) { aws.wait_for_status(:running, instance_set) }
302
+
303
+ context 'single instance' do
304
+ it 'behaves correctly in typical case' do
305
+ allow(aws_instance).to receive(:status).and_return(:waiting, :waiting, :running)
306
+ expect(aws).to receive(:backoff_sleep).exactly(3).times
307
+ expect(wait_for_status).to eq(instance_set)
308
+ end
309
+
310
+ it 'executes block correctly instead of status if given one' do
311
+ barn_value = 'did you grow up in a barn?'
312
+ allow(aws_instance).to receive( :[] ).with( :barn ) { barn_value }
313
+ expect(aws_instance).to receive(:status).exactly(0).times
314
+ expect(aws).to receive(:backoff_sleep).exactly(1).times
315
+ aws.wait_for_status(:running, instance_set) do |instance|
316
+ expect( instance[:barn] ).to be === barn_value
317
+ true
318
+ end
319
+ end
320
+ end
321
+
322
+ context 'with multiple instances' do
323
+ let( :instance_set ) { [{:instance => aws_instance}, {:instance => aws_instance}] }
324
+
325
+ it 'returns the instance set passed to it' do
326
+ allow(aws_instance).to receive(:status).and_return(:waiting, :waiting, :running, :waiting, :waiting, :running)
327
+ allow(aws).to receive(:backoff_sleep).exactly(6).times
328
+ expect(wait_for_status).to eq(instance_set)
329
+ end
330
+
331
+ it 'calls backoff_sleep once per instance.status call' do
332
+ allow(aws_instance).to receive(:status).and_return(:waiting, :waiting, :running, :waiting, :waiting, :running)
333
+ expect(aws).to receive(:backoff_sleep).exactly(6).times
334
+ expect(wait_for_status).to eq(instance_set)
335
+ end
336
+
337
+ it 'executes block correctly instead of status if given one' do
338
+ barn_value = 'did you grow up in a barn?'
339
+ not_barn_value = 'notabarn'
340
+ allow(aws_instance).to receive( :[] ).with( :barn ).and_return(not_barn_value, barn_value, not_barn_value, barn_value)
341
+ allow(aws_instance).to receive(:status).and_return(:waiting)
342
+ expect(aws).to receive(:backoff_sleep).exactly(4).times
343
+ aws.wait_for_status(:running, instance_set) do |instance|
344
+ instance[:barn] == barn_value
345
+ end
346
+ end
347
+ end
348
+
349
+ context 'after 10 tries' do
350
+ it 'raises RuntimeError' do
351
+ expect(aws_instance).to receive(:status).and_return(:waiting).exactly(10).times
352
+ expect(aws).to receive(:backoff_sleep).exactly(9).times
353
+ expect { wait_for_status }.to raise_error('Instance never reached state running')
354
+ end
355
+
356
+ it 'still raises RuntimeError if given a block' do
357
+ expect(aws_instance).to receive(:status).and_return(:waiting).exactly(10).times
358
+ expect(aws).to receive(:backoff_sleep).exactly(9).times
359
+ expect { wait_for_status { false } }.to raise_error('Instance never reached state running')
360
+ end
361
+ end
362
+
363
+ context 'with an invalid instance' do
364
+ it 'raises AWS::EC2::Errors::InvalidInstanceID::NotFound' do
365
+ expect(aws_instance).to receive(:status).and_raise(AWS::EC2::Errors::InvalidInstanceID::NotFound).exactly(10).times
366
+ allow(aws).to receive(:backoff_sleep).at_most(10).times
367
+ expect(wait_for_status).to eq(instance_set)
368
+ end
369
+ end
370
+ end
371
+
372
+ describe '#add_tags' do
373
+ let( :aws_instance ) { double('aws_instance', :add_tag => nil) }
374
+ subject(:add_tags) { aws.add_tags }
375
+
376
+ it 'returns nil' do
377
+ @hosts.each {|host| host['instance'] = aws_instance}
378
+ expect(add_tags).to be_nil
379
+ end
380
+
381
+ it 'handles a single host' do
382
+ @hosts[0]['instance'] = aws_instance
383
+ @hosts = [@hosts[0]]
384
+ expect(add_tags).to be_nil
385
+ end
386
+
387
+ context 'with multiple hosts' do
388
+ before :each do
389
+ @hosts.each {|host| host['instance'] = aws_instance}
390
+ end
391
+
392
+ it 'handles host_tags hash on host object' do
393
+ # set :host_tags on first host
394
+ aws.instance_eval {
395
+ @hosts[0][:host_tags] = {'test_tag' => 'test_value'}
396
+ }
397
+ expect(aws_instance).to receive(:add_tag).with('test_tag', hash_including(:value => 'test_value')).at_least(:once)
398
+ expect(add_tags).to be_nil
399
+ end
400
+
401
+ it 'adds tag for jenkins_build_url' do
402
+ aws.instance_eval('@options[:jenkins_build_url] = "my_build_url"')
403
+ expect(aws_instance).to receive(:add_tag).with('jenkins_build_url', hash_including(:value => 'my_build_url')).at_least(:once)
404
+ expect(add_tags).to be_nil
405
+ end
406
+
407
+ it 'adds tag for Name' do
408
+ expect(aws_instance).to receive(:add_tag).with('Name', hash_including(:value => /vm/)).at_least(@hosts.size).times
409
+ expect(add_tags).to be_nil
410
+ end
411
+
412
+ it 'adds tag for department' do
413
+ aws.instance_eval('@options[:department] = "my_department"')
414
+ expect(aws_instance).to receive(:add_tag).with('department', hash_including(:value => 'my_department')).at_least(:once)
415
+ expect(add_tags).to be_nil
416
+ end
417
+
418
+ it 'adds tag for project' do
419
+ aws.instance_eval('@options[:project] = "my_project"')
420
+ expect(aws_instance).to receive(:add_tag).with('project', hash_including(:value => 'my_project')).at_least(:once)
421
+ expect(add_tags).to be_nil
422
+ end
423
+
424
+ it 'adds tag for created_by' do
425
+ aws.instance_eval('@options[:created_by] = "my_created_by"')
426
+ expect(aws_instance).to receive(:add_tag).with('created_by', hash_including(:value => 'my_created_by')).at_least(:once)
427
+ expect(add_tags).to be_nil
428
+ end
429
+ end
430
+ end
431
+
432
+ describe '#populate_dns' do
433
+ let( :vpc_instance ) { {ip_address: nil, private_ip_address: "vpc_private_ip", dns_name: "vpc_dns_name"} }
434
+ let( :ec2_instance ) { {ip_address: "ec2_public_ip", private_ip_address: "ec2_private_ip", dns_name: "ec2_dns_name"} }
435
+ subject(:populate_dns) { aws.populate_dns }
436
+
437
+ context 'on a public EC2 instance' do
438
+ before :each do
439
+ @hosts.each {|host| host['instance'] = make_instance ec2_instance}
440
+ end
441
+
442
+ it 'sets host ip to instance.ip_address' do
443
+ populate_dns
444
+ hosts = aws.instance_variable_get( :@hosts )
445
+ hosts.each do |host|
446
+ expect(host['ip']).to eql(ec2_instance[:ip_address])
447
+ end
448
+ end
449
+
450
+ it 'sets host private_ip to instance.private_ip_address' do
451
+ populate_dns
452
+ hosts = aws.instance_variable_get( :@hosts )
453
+ hosts.each do |host|
454
+ expect(host['private_ip']).to eql(ec2_instance[:private_ip_address])
455
+ end
456
+ end
457
+
458
+ it 'sets host dns_name to instance.dns_name' do
459
+ populate_dns
460
+ hosts = aws.instance_variable_get( :@hosts )
461
+ hosts.each do |host|
462
+ expect(host['dns_name']).to eql(ec2_instance[:dns_name])
463
+ end
464
+ end
465
+ end
466
+
467
+ context 'on a VPC based instance' do
468
+ before :each do
469
+ @hosts.each {|host| host['instance'] = make_instance vpc_instance}
470
+ end
471
+
472
+ it 'sets host ip to instance.private_ip_address' do
473
+ populate_dns
474
+ hosts = aws.instance_variable_get( :@hosts )
475
+ hosts.each do |host|
476
+ expect(host['ip']).to eql(vpc_instance[:private_ip_address])
477
+ end
478
+ end
479
+
480
+ it 'sets host private_ip to instance.private_ip_address' do
481
+ populate_dns
482
+ hosts = aws.instance_variable_get( :@hosts )
483
+ hosts.each do |host|
484
+ expect(host['private_ip']).to eql(vpc_instance[:private_ip_address])
485
+ end
486
+ end
487
+
488
+ it 'sets host dns_name to instance.dns_name' do
489
+ populate_dns
490
+ hosts = aws.instance_variable_get( :@hosts )
491
+ hosts.each do |host|
492
+ expect(host['dns_name']).to eql(vpc_instance[:dns_name])
493
+ end
494
+ end
495
+ end
496
+ end
497
+
498
+ describe '#etc_hosts_entry' do
499
+ let( :host ) { @hosts[0] }
500
+ let( :interface ) { :ip }
501
+ subject(:etc_hosts_entry) { aws.etc_hosts_entry(host, interface) }
502
+
503
+ it 'returns a predictable host entry' do
504
+ expect(aws).to receive(:get_domain_name).and_return('lan')
505
+ expect(etc_hosts_entry).to eq("ip.address.for.vm1\tvm1 vm1.lan vm1.box.tld\n")
506
+ end
507
+
508
+ context 'when :private_ip is requested' do
509
+ let( :interface ) { :private_ip }
510
+ it 'returns host entry for the private_ip' do
511
+ host = @hosts[0]
512
+ expect(aws).to receive(:get_domain_name).and_return('lan')
513
+ expect(etc_hosts_entry).to eq("private.ip.for.vm1\tvm1 vm1.lan vm1.box.tld\n")
514
+ end
515
+ end
516
+ end
517
+
518
+ describe '#configure_hosts' do
519
+ subject(:configure_hosts) { aws.configure_hosts }
520
+
521
+ it { is_expected.to be_nil }
522
+
523
+ context 'calls #set_etc_hosts' do
524
+ it 'for each host (except the f5 ones)' do
525
+ non_netdev_hosts = @hosts.select{ |h| !(h['platform'] =~ /f5|netscaler/) }
526
+ expect(aws).to receive(:set_etc_hosts).exactly(non_netdev_hosts.size).times
527
+ expect(configure_hosts).to be_nil
528
+ end
529
+
530
+ it 'with predictable host entries' do
531
+ @hosts = [@hosts[0], @hosts[1]]
532
+ entries = "127.0.0.1\tlocalhost localhost.localdomain\n"\
533
+ "private.ip.for.vm1\tvm1 vm1.lan vm1.box.tld\n"\
534
+ "ip.address.for.vm2\tvm2 vm2.lan vm2.box.tld\n"
535
+ allow(aws).to receive(:get_domain_name).and_return('lan')
536
+ expect(aws).to receive(:set_etc_hosts).with(@hosts[0], entries)
537
+ expect(aws).to receive(:set_etc_hosts).with(@hosts[1], anything)
538
+ expect(configure_hosts).to be_nil
539
+ end
540
+ end
541
+ end
542
+
543
+ describe '#enable_root_on_hosts' do
544
+ context 'enabling root shall be called once for the ubuntu machine' do
545
+ it "should enable root once" do
546
+ allow(aws).to receive(:enable_root_netscaler)
547
+ expect( aws ).to receive(:copy_ssh_to_root).with( @hosts[3], options ).once()
548
+ expect( aws ).to receive(:enable_root_login).with( @hosts[3], options).once()
549
+ aws.enable_root_on_hosts();
550
+ end
551
+ end
552
+
553
+ it 'enables root once on the f5 host through its code path' do
554
+ allow(aws).to receive(:enable_root_netscaler)
555
+ expect( aws ).to receive(:enable_root_f5).with( @hosts[4] ).once()
556
+ aws.enable_root_on_hosts()
557
+ end
558
+ end
559
+
560
+ describe '#enable_root_f5' do
561
+ let( :f5_host ) { @hosts[4] }
562
+ subject(:enable_root_f5) { aws.enable_root_f5(f5_host) }
563
+
564
+ it 'creates a password on the host' do
565
+ result_mock = Beaker::Result.new(f5_host, '')
566
+ result_mock.exit_code = 0
567
+ allow( f5_host ).to receive( :exec ).and_return(result_mock)
568
+ allow( aws ).to receive( :backoff_sleep )
569
+ sha_mock = Object.new
570
+ allow( Digest::SHA256 ).to receive( :new ).and_return(sha_mock)
571
+ expect( sha_mock ).to receive( :hexdigest ).once()
572
+ enable_root_f5
573
+ end
574
+
575
+ it 'tries 10x before failing correctly' do
576
+ result_mock = Beaker::Result.new(f5_host, '')
577
+ result_mock.exit_code = 2
578
+ allow( f5_host ).to receive( :exec ).and_return(result_mock)
579
+ expect( aws ).to receive( :backoff_sleep ).exactly(9).times
580
+ expect{ enable_root_f5 }.to raise_error( RuntimeError, /unable/ )
581
+ end
582
+ end
583
+
584
+ describe '#enable_root_netscaler' do
585
+ let( :ns_host ) { @hosts[5] }
586
+ subject(:enable_root_netscaler) { aws.enable_root_netscaler(ns_host) }
587
+
588
+ it 'set password to instance id of the host' do
589
+ instance_mock = Object.new
590
+ allow( instance_mock ).to receive(:id).and_return("i-842018")
591
+ ns_host["instance"]=instance_mock
592
+ enable_root_netscaler
593
+ expect(ns_host['ssh'][:password]).to eql("i-842018")
594
+ end
595
+ end
596
+
597
+ describe '#set_hostnames' do
598
+ subject(:set_hostnames) { aws.set_hostnames }
599
+ it 'returns @hosts' do
600
+ expect(set_hostnames).to eq(@hosts)
601
+ end
602
+
603
+ context 'for each host' do
604
+ it 'calls exec' do
605
+ @hosts.each do |host|
606
+ expect(host).to receive(:exec).once unless host['platform'] =~ /netscaler/
607
+ end
608
+ expect(set_hostnames).to eq(@hosts)
609
+ end
610
+
611
+ it 'passes a Command instance to exec' do
612
+ @hosts.each do |host|
613
+ expect(host).to receive(:exec).with( instance_of(Beaker::Command) ).once unless host['platform'] =~ /netscaler/
614
+ end
615
+ expect(set_hostnames).to eq(@hosts)
616
+ end
617
+
618
+ it 'sets the the vmhostname to the dns_name for each host' do
619
+ expect(set_hostnames).to eq(@hosts)
620
+ @hosts.each do |host|
621
+ expect(host[:vmhostname]).to eq(host[:dns_name])
622
+ expect(host[:vmhostname]).to eq(host.hostname)
623
+ end
624
+ end
625
+
626
+ it 'sets the the vmhostname to the beaker config name for each host' do
627
+ options[:use_beaker_hostnames] = true
628
+ @hosts.each do |host|
629
+ host[:name] = "prettyponyprincess"
630
+ end
631
+ expect(set_hostnames).to eq(@hosts)
632
+ @hosts.each do |host|
633
+ expect(host[:vmhostname]).to eq(host[:name])
634
+ expect(host[:vmhostname]).to eq(host.hostname)
635
+ end
636
+ end
637
+
638
+ end
639
+ end
640
+
641
+ describe '#backoff_sleep' do
642
+ it "should call sleep 1024 times at attempt 10" do
643
+ expect_any_instance_of( Object ).to receive(:sleep).with(1024)
644
+ aws.backoff_sleep(10)
645
+ end
646
+ end
647
+
648
+ describe '#public_key' do
649
+ subject(:public_key) { aws.public_key }
650
+
651
+ it "retrieves contents from local ~/.ssh/id_rsa.pub file" do
652
+ # Stub calls to file read/exists
653
+ key_value = 'foobar_Rsa'
654
+ allow(File).to receive(:exist?).with(/id_dsa.pub/) { false }
655
+ allow(File).to receive(:exist?).with(/id_dsa/) { false }
656
+ allow(File).to receive(:exist?).with(/id_rsa.pub/) { true }
657
+ allow(File).to receive(:exist?).with(/id_rsa/) { true }
658
+ allow(File).to receive(:read).with(/id_rsa.pub/) { key_value }
659
+
660
+ # Should return contents of allow( previously ).to receivebed id_rsa.pub
661
+ expect(public_key).to be === key_value
662
+ end
663
+
664
+ it "retrieves contents from local ~/.ssh/id_dsa.pub file" do
665
+ # Stub calls to file read/exists
666
+ key_value = 'foobar_Dsa'
667
+ allow(File).to receive(:exist?).with(/id_rsa.pub/) { false }
668
+ allow(File).to receive(:exist?).with(/id_rsa/) { false }
669
+ allow(File).to receive(:exist?).with(/id_dsa.pub/) { true }
670
+ allow(File).to receive(:exist?).with(/id_dsa/) { true }
671
+ allow(File).to receive(:read).with(/id_dsa.pub/) { key_value }
672
+
673
+ expect(public_key).to be === key_value
674
+ end
675
+
676
+ it "should return an error if the files do not exist" do
677
+ expect { public_key }.to raise_error(RuntimeError, /Expected to find a public key/)
678
+ end
679
+
680
+ it "uses options-provided keys" do
681
+ opts = aws.instance_variable_get( :@options )
682
+ opts[:ssh][:keys] = ['fake_key1', 'fake_key2']
683
+ aws.instance_variable_set( :@options, opts )
684
+
685
+ key_value = 'foobar_Custom2'
686
+ allow(File).to receive(:exist?).with(anything) { false }
687
+ allow(File).to receive(:exist?).with(/fake_key2/) { true }
688
+ allow(File).to receive(:read).with(/fake_key2/) { key_value }
689
+
690
+ expect(public_key).to be === key_value
691
+ end
692
+ end
693
+
694
+ describe '#key_name' do
695
+ it 'returns a key name from the local hostname' do
696
+ # Mock out the hostname and local user calls
697
+ expect( Socket ).to receive(:gethostname) { "foobar" }
698
+ expect( aws ).to receive(:local_user) { "bob" }
699
+
700
+ # Should match the expected composite key name
701
+ expect(aws.key_name).to match(/^Beaker-bob-foobar-/)
702
+ end
703
+
704
+ it 'uses the generated random string from :aws_keyname_modifier' do
705
+ expect(aws.key_name).to match(/#{options[:aws_keyname_modifier]}/)
706
+ end
707
+
708
+ it 'uses nanosecond time value to make key name collision harder' do
709
+ options[:timestamp] = Time.now
710
+ nanosecond_value = options[:timestamp].strftime("%N")
711
+ expect(aws.key_name).to match(/#{nanosecond_value}/)
712
+ end
713
+ end
714
+
715
+ describe '#local_user' do
716
+ it 'returns ENV["USER"]' do
717
+ stub_const('ENV', ENV.to_hash.merge('USER' => 'SuperUser'))
718
+ expect(aws.local_user).to eq("SuperUser")
719
+ end
720
+ end
721
+
722
+ describe '#ensure_key_pair' do
723
+ let( :region ) { double('region', :name => 'test_region_name') }
724
+ subject(:ensure_key_pair) { aws.ensure_key_pair(region) }
725
+ let( :key_name ) { "Beaker-rspec-SUT" }
726
+
727
+ it 'deletes the given keypair, then recreates it' do
728
+ allow( aws ).to receive( :key_name ).and_return(key_name)
729
+
730
+ expect( aws ).to receive( :delete_key_pair ).with( region, key_name).once.ordered
731
+ expect( aws ).to receive( :create_new_key_pair ).with( region, key_name).once.ordered
732
+ ensure_key_pair
733
+ end
734
+ end
735
+
736
+ describe '#delete_key_pair_all_regions' do
737
+ it 'calls delete_key_pair over all regions' do
738
+ key_name = 'kname_test1538'
739
+ allow(aws).to receive( :key_name ).and_return(key_name)
740
+ regions = []
741
+ regions << double('region', :key_pairs => 'pair1', :name => 'name1')
742
+ regions << double('region', :key_pairs => 'pair2', :name => 'name2')
743
+ ec2_mock = Object.new
744
+ allow(ec2_mock).to receive( :regions ).and_return(regions)
745
+ aws.instance_variable_set( :@ec2, ec2_mock )
746
+ region_keypairs_hash_mock = {}
747
+ region_keypairs_hash_mock[double('region')] = ['key1', 'key2', 'key3']
748
+ region_keypairs_hash_mock[double('region')] = ['key4', 'key5', 'key6']
749
+ allow( aws ).to receive( :my_key_pairs ).and_return( region_keypairs_hash_mock )
750
+
751
+ region_keypairs_hash_mock.each_pair do |region, keyname_array|
752
+ keyname_array.each do |keyname|
753
+ expect( aws ).to receive( :delete_key_pair ).with( region, keyname )
754
+ end
755
+ end
756
+ aws.delete_key_pair_all_regions
757
+ end
758
+ end
759
+
760
+ describe '#my_key_pairs' do
761
+ let( :region ) { double('region', :name => 'test_region_name') }
762
+
763
+ it 'uses the default keyname if no filter is given' do
764
+ default_keyname_answer = 'test_pair_6193'
765
+ allow( aws ).to receive( :key_name ).and_return( default_keyname_answer )
766
+
767
+ kp_mock_1 = double('keypair')
768
+ kp_mock_2 = double('keypair')
769
+ regions = []
770
+ regions << double('region', :key_pairs => kp_mock_1, :name => 'name1')
771
+ regions << double('region', :key_pairs => kp_mock_2, :name => 'name2')
772
+ ec2_mock = Object.new
773
+ allow( ec2_mock ).to receive( :regions ).and_return( regions )
774
+ aws.instance_variable_set( :@ec2, ec2_mock )
775
+
776
+ kp_mock = double('keypair')
777
+ allow( region ).to receive( :key_pairs ).and_return( kp_mock )
778
+ expect( kp_mock_1 ).to receive( :filter ).with( 'key-name', default_keyname_answer ).and_return( [] )
779
+ expect( kp_mock_2 ).to receive( :filter ).with( 'key-name', default_keyname_answer ).and_return( [] )
780
+
781
+ aws.my_key_pairs()
782
+ end
783
+
784
+ it 'uses the filter passed if given' do
785
+ default_keyname_answer = 'test_pair_6194'
786
+ allow( aws ).to receive( :key_name ).and_return( default_keyname_answer )
787
+ name_filter = 'filter_pair_1597'
788
+ filter_star = "#{name_filter}-*"
789
+
790
+ kp_mock_1 = double('keypair')
791
+ kp_mock_2 = double('keypair')
792
+ regions = []
793
+ regions << double('region', :key_pairs => kp_mock_1, :name => 'name1')
794
+ regions << double('region', :key_pairs => kp_mock_2, :name => 'name2')
795
+ ec2_mock = Object.new
796
+ allow( ec2_mock ).to receive( :regions ).and_return( regions )
797
+ aws.instance_variable_set( :@ec2, ec2_mock )
798
+
799
+ kp_mock = double('keypair')
800
+ allow( region ).to receive( :key_pairs ).and_return( kp_mock )
801
+ expect( kp_mock_1 ).to receive( :filter ).with( 'key-name', filter_star ).and_return( [] )
802
+ expect( kp_mock_2 ).to receive( :filter ).with( 'key-name', filter_star ).and_return( [] )
803
+
804
+ aws.my_key_pairs(name_filter)
805
+ end
806
+ end
807
+
808
+ describe '#delete_key_pair' do
809
+ let( :region ) { double('region', :name => 'test_region_name') }
810
+
811
+ it 'calls delete on a keypair if it exists' do
812
+ pair_name = 'pair1'
813
+ kp_mock = double('keypair', :exists? => true)
814
+ expect( kp_mock ).to receive( :delete ).once
815
+ pairs = { pair_name => kp_mock }
816
+ allow( region ).to receive( :key_pairs ).and_return( pairs )
817
+ aws.delete_key_pair(region, pair_name)
818
+ end
819
+
820
+ it 'skips delete on a keypair if it does not exist' do
821
+ pair_name = 'pair1'
822
+ kp_mock = double('keypair', :exists? => false)
823
+ expect( kp_mock ).to receive( :delete ).never
824
+ pairs = { pair_name => kp_mock }
825
+ allow( region ).to receive( :key_pairs ).and_return( pairs )
826
+ aws.delete_key_pair(region, pair_name)
827
+ end
828
+ end
829
+
830
+ describe '#create_new_key_pair' do
831
+ let(:region) { double('region', :name => 'test_region_name') }
832
+ let(:ssh_string) { 'ssh_string_test_0867' }
833
+ let(:pairs) { double('keypairs') }
834
+ let(:pair) { double('keypair') }
835
+ let(:pair_name) { 'pair_name_1555432' }
836
+
837
+ before :each do
838
+ allow(aws).to receive(:public_key).and_return(ssh_string)
839
+ expect(pairs).to receive(:import).with(pair_name, ssh_string)
840
+ expect(pairs).to receive(:[]).with(pair_name).and_return(pair)
841
+ expect(region).to receive(:key_pairs).and_return(pairs).twice
842
+ end
843
+
844
+ it 'imports the key given from public_key' do
845
+ expect(pair).to receive(:exists?).and_return(true)
846
+ aws.create_new_key_pair(region, pair_name)
847
+ end
848
+
849
+ it 'raises an exception if subsequent keypair check is false' do
850
+ expect(pair).to receive(:exists?).and_return(false).exactly(5).times
851
+ expect(aws).to receive(:backoff_sleep).exactly(5).times
852
+ expect { aws.create_new_key_pair(region, pair_name) }.
853
+ to raise_error(RuntimeError,
854
+ "AWS key pair #{pair_name} can not be queried, even after import")
855
+ end
856
+ end
857
+
858
+ describe '#group_id' do
859
+ it 'should return a predicatable group_id from a port list' do
860
+ expect(aws.group_id([22, 1024])).to eq("Beaker-2799478787")
861
+ end
862
+
863
+ it 'should return a predicatable group_id from an empty list' do
864
+ expect { aws.group_id([]) }.to raise_error(ArgumentError, "Ports list cannot be nil or empty")
865
+ end
866
+ end
867
+
868
+ describe '#ensure_group' do
869
+ let( :vpc ) { double('vpc') }
870
+ let( :ports ) { [22, 80, 8080] }
871
+ subject(:ensure_group) { aws.ensure_group(vpc, ports) }
872
+
873
+ context 'for an existing group' do
874
+ before :each do
875
+ @group = double(:nil? => false)
876
+ end
877
+
878
+ it 'returns group from vpc lookup' do
879
+ expect(vpc).to receive_message_chain('security_groups.filter.first').and_return(@group)
880
+ expect(ensure_group).to eq(@group)
881
+ end
882
+
883
+ context 'during group lookup' do
884
+ it 'performs group_id lookup for ports' do
885
+ expect(aws).to receive(:group_id).with(ports)
886
+ expect(vpc).to receive_message_chain('security_groups.filter.first').and_return(@group)
887
+ expect(ensure_group).to eq(@group)
888
+ end
889
+
890
+ it 'filters on group_id' do
891
+ expect(vpc).to receive(:security_groups).and_return(vpc)
892
+ expect(vpc).to receive(:filter).with('group-name', 'Beaker-1521896090').and_return(vpc)
893
+ expect(vpc).to receive(:first).and_return(@group)
894
+ expect(ensure_group).to eq(@group)
895
+ end
896
+ end
897
+ end
898
+
899
+ context 'when group does not exist' do
900
+ it 'creates group if group.nil?' do
901
+ group = double(:nil? => true)
902
+ expect(aws).to receive(:create_group).with(vpc, ports).and_return(group)
903
+ expect(vpc).to receive_message_chain('security_groups.filter.first').and_return(group)
904
+ expect(ensure_group).to eq(group)
905
+ end
906
+ end
907
+ end
908
+
909
+ describe '#create_group' do
910
+ let( :rv ) { double('rv') }
911
+ let( :ports ) { [22, 80, 8080] }
912
+ subject(:create_group) { aws.create_group(rv, ports) }
913
+
914
+ before :each do
915
+ @group = double(:nil? => false)
916
+ end
917
+
918
+ it 'returns a newly created group' do
919
+ allow(rv).to receive_message_chain('security_groups.create').and_return(@group)
920
+ allow(@group).to receive(:authorize_ingress).at_least(:once)
921
+ expect(create_group).to eq(@group)
922
+ end
923
+
924
+ it 'requests group_id for ports given' do
925
+ expect(aws).to receive(:group_id).with(ports)
926
+ allow(rv).to receive_message_chain('security_groups.create').and_return(@group)
927
+ allow(@group).to receive(:authorize_ingress).at_least(:once)
928
+ expect(create_group).to eq(@group)
929
+ end
930
+
931
+ it 'creates group with expected arguments' do
932
+ group_name = "Beaker-1521896090"
933
+ group_desc = "Custom Beaker security group for #{ports.to_a}"
934
+ expect(rv).to receive_message_chain('security_groups.create')
935
+ .with(group_name, :description => group_desc)
936
+ .and_return(@group)
937
+ allow(@group).to receive(:authorize_ingress).at_least(:once)
938
+ expect(create_group).to eq(@group)
939
+ end
940
+
941
+ it 'authorizes requested ports for group' do
942
+ expect(rv).to receive_message_chain('security_groups.create').and_return(@group)
943
+ ports.each do |port|
944
+ expect(@group).to receive(:authorize_ingress).with(:tcp, port).once
945
+ end
946
+ expect(create_group).to eq(@group)
947
+ end
948
+ end
949
+
950
+ describe '#load_fog_credentials' do
951
+ # Receive#and_call_original below allows us to test the core load_fog_credentials method
952
+ let(:creds) { {:access_key => 'awskey', :secret_key => 'awspass'} }
953
+ let(:dot_fog) { '.fog' }
954
+ subject(:load_fog_credentials) { aws.load_fog_credentials(dot_fog) }
955
+
956
+ it 'returns loaded fog credentials' do
957
+ fog_hash = {:default => {:aws_access_key_id => 'awskey', :aws_secret_access_key => 'awspass'}}
958
+ expect(aws).to receive(:load_fog_credentials).and_call_original
959
+ expect(YAML).to receive(:load_file).and_return(fog_hash)
960
+ expect(load_fog_credentials).to eq(creds)
961
+ end
962
+
963
+ context 'raises errors' do
964
+ it 'if missing access_key credential' do
965
+ fog_hash = {:default => {:aws_secret_access_key => 'awspass'}}
966
+ err_text = "You must specify an aws_access_key_id in your .fog file (#{dot_fog}) for ec2 instances!"
967
+ expect(aws).to receive(:load_fog_credentials).and_call_original
968
+ expect(YAML).to receive(:load_file).and_return(fog_hash)
969
+ expect { load_fog_credentials }.to raise_error(err_text)
970
+ end
971
+
972
+ it 'if missing secret_key credential' do
973
+ dot_fog = '.fog'
974
+ fog_hash = {:default => {:aws_access_key_id => 'awskey'}}
975
+ err_text = "You must specify an aws_secret_access_key in your .fog file (#{dot_fog}) for ec2 instances!"
976
+ expect(aws).to receive(:load_fog_credentials).and_call_original
977
+ expect(YAML).to receive(:load_file).and_return(fog_hash)
978
+ expect { load_fog_credentials }.to raise_error(err_text)
979
+ end
980
+ end
981
+ end
982
+
983
+ describe 'test_split_install' do
984
+ it 'does not add port 8143 if master, dashboard and database are on the same host' do
985
+ @hosts = [@hosts[0]]
986
+ @hosts[0][:roles] = ["master", "dashboard", "database"]
987
+ allow(aws).to receive(:test_split_install)
988
+ expect(@hosts[0]).not_to have_key(:additional_ports)
989
+ aws
990
+ end
991
+
992
+ it 'does not add port 8143 if host does not have master, dashboard or database at all' do
993
+ @hosts = [@hosts[0]]
994
+ @hosts[0][:roles] = ["agent", "frictionless"]
995
+ allow(aws).to receive(:test_split_install)
996
+ expect(@hosts[0]).not_to have_key(:additional_ports)
997
+ aws
998
+ end
999
+
1000
+ it 'adds port 8143 to all the hosts for split install that has either master, dashboard or database' do
1001
+ @hosts = [@hosts[0], @hosts[1], @hosts[2], @hosts[3]]
1002
+ @hosts[0][:roles] = ["master"]
1003
+ @hosts[1][:roles] = ["dashboard"]
1004
+ @hosts[2][:roles] = ["database"]
1005
+ @hosts[3][:roles] = ["agent"]
1006
+ allow(aws).to receive(:test_split_install)
1007
+ expect(@hosts[0]).to have_key(:additional_ports)
1008
+ expect(@hosts[0][:additional_ports]).to include(8143)
1009
+ expect(@hosts[1]).to have_key(:additional_ports)
1010
+ expect(@hosts[1][:additional_ports]).to include(8143)
1011
+ expect(@hosts[2]).to have_key(:additional_ports)
1012
+ expect(@hosts[2][:additional_ports]).to include(8143)
1013
+ expect(@hosts[3]).not_to have_key(:additional_ports)
1014
+ aws
1015
+ end
1016
+ end
1017
+ end
1018
+ end