beaker-aws 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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