chef-provisioning-aws 0.2.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -13
- data/Rakefile +6 -2
- data/lib/chef/provider/aws_auto_scaling_group.rb +13 -6
- data/lib/chef/provider/aws_security_group.rb +1 -1
- data/lib/chef/provider/aws_subnet.rb +1 -1
- data/lib/chef/provisioning/aws_driver/driver.rb +152 -49
- data/lib/chef/provisioning/aws_driver/version.rb +1 -1
- data/lib/chef/resource/aws_auto_scaling_group.rb +1 -0
- data/spec/acceptance/aws_ebs_volume/nodes/ettores-mbp.lan.json +3 -0
- data/spec/chef_zero_rspec_helper.rb +8 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/unit/provider/aws_subnet_spec.rb +67 -0
- data/spec/unit/resource/aws_subnet_spec.rb +23 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5ec492afa3798bae513c3b61ea626958a6b7e954
|
4
|
+
data.tar.gz: 1b8ecf59b63a169aaa7d744efe491f3f1627065c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcbdddb1f0cc451ecd9b345e62fc95d6049e5e1d9c715dff1b000f859a80728d1670fb42064e65b4ca1b2e93d26e9c036177b36d40a3a5802ad67a1e14a344d4
|
7
|
+
data.tar.gz: 8e17c7774db57b2e9774d366ffbe04f301c8bb5d09761a7a9f6fb1892567cd2163a6197a31591ca7a249f8f4216c1f65790a3c91f6e074c45b1a7ad197ab448f
|
data/README.md
CHANGED
@@ -1,18 +1,6 @@
|
|
1
1
|
# chef-provisioning-aws
|
2
2
|
|
3
|
-
|
4
|
-
(for a variety of reasons). It also implements AWS-specific resources to
|
5
|
-
manage such as SQS queues and SNS topics (currently) along with load
|
6
|
-
balancers (needs a non-production branch of chef_provisioning at the moment)
|
7
|
-
|
8
|
-
This is not quite ready for public consumption and is under active
|
9
|
-
development.
|
10
|
-
|
11
|
-
This requires the latest from chef-provisioning's load_balancer branch for some
|
12
|
-
features like region support (until it gets merged into master, which should be soon)
|
13
|
-
|
14
|
-
|
15
|
-
List of resources (these may not have 100% of SDK functionality mapped but can be provisioned):
|
3
|
+
An implementation of the AWS driver using the AWS Ruby SDK (v1). It also implements a large number of AWS-specific resources such as:
|
16
4
|
|
17
5
|
* SQS Queues
|
18
6
|
* SNS Topics
|
data/Rakefile
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'bundler'
|
2
2
|
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
3
4
|
|
4
|
-
task :spec
|
5
|
-
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
desc "Run specs"
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
9
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
6
10
|
end
|
@@ -3,13 +3,20 @@ require 'chef/provider/aws_provider'
|
|
3
3
|
class Chef::Provider::AwsAutoScalingGroup < Chef::Provider::AwsProvider
|
4
4
|
action :create do
|
5
5
|
if existing_group.nil?
|
6
|
-
|
6
|
+
auto_scaling_opts = {
|
7
|
+
:launch_configuration => new_resource.launch_config,
|
8
|
+
:min_size => new_resource.min_size,
|
9
|
+
:max_size => new_resource.max_size,
|
10
|
+
:availability_zones => availability_zones
|
11
|
+
}
|
12
|
+
|
13
|
+
auto_scaling_opts[:desired_capacity] = new_resource.desired_capacity if new_resource.desired_capacity
|
14
|
+
auto_scaling_opts[:load_balancers] = new_resource.load_balancers if new_resource.load_balancers
|
15
|
+
|
16
|
+
converge_by "Creating new Auto Scaling group #{new_resource.name} in #{new_resource.region_name}" do
|
7
17
|
@existing_group = auto_scaling.groups.create(
|
8
18
|
new_resource.name,
|
9
|
-
|
10
|
-
:min_size => new_resource.min_size,
|
11
|
-
:max_size => new_resource.max_size,
|
12
|
-
:availability_zones => availability_zones
|
19
|
+
auto_scaling_opts
|
13
20
|
)
|
14
21
|
|
15
22
|
new_resource.save
|
@@ -19,7 +26,7 @@ class Chef::Provider::AwsAutoScalingGroup < Chef::Provider::AwsProvider
|
|
19
26
|
|
20
27
|
action :delete do
|
21
28
|
if existing_group
|
22
|
-
converge_by "Deleting Auto Scaling group #{
|
29
|
+
converge_by "Deleting Auto Scaling group #{new_resource.name} in #{new_resource.region_name}" do
|
23
30
|
existing_group.delete!
|
24
31
|
end
|
25
32
|
end
|
@@ -9,7 +9,7 @@ class Chef::Provider::AwsSubnet < Chef::Provider::AwsProvider
|
|
9
9
|
if existing_subnet == nil
|
10
10
|
converge_by "Creating new Subnet #{fqn} in VPC #{new_resource.vpc} in #{new_resource.region_name}" do
|
11
11
|
opts = { :vpc => vpc_id }
|
12
|
-
|
12
|
+
opts[:availability_zone] = new_resource.availability_zone if new_resource.availability_zone
|
13
13
|
subnet = ec2.subnets.create(new_resource.cidr_block, opts)
|
14
14
|
subnet.tags['Name'] = new_resource.name
|
15
15
|
subnet.tags['VPC'] = new_resource.vpc
|
@@ -2,8 +2,10 @@ require 'chef/mixin/shell_out'
|
|
2
2
|
require 'chef/provisioning/driver'
|
3
3
|
require 'chef/provisioning/convergence_strategy/install_cached'
|
4
4
|
require 'chef/provisioning/convergence_strategy/install_sh'
|
5
|
+
require 'chef/provisioning/convergence_strategy/install_msi'
|
5
6
|
require 'chef/provisioning/convergence_strategy/no_converge'
|
6
7
|
require 'chef/provisioning/transport/ssh'
|
8
|
+
require 'chef/provisioning/transport/winrm'
|
7
9
|
require 'chef/provisioning/machine/windows_machine'
|
8
10
|
require 'chef/provisioning/machine/unix_machine'
|
9
11
|
require 'chef/provisioning/machine_spec'
|
@@ -59,6 +61,7 @@ module AWSDriver
|
|
59
61
|
|
60
62
|
# Load balancer methods
|
61
63
|
def allocate_load_balancer(action_handler, lb_spec, lb_options, machine_specs)
|
64
|
+
lb_options ||= {}
|
62
65
|
if lb_options[:security_group_id]
|
63
66
|
security_group = ec2.security_groups[:security_group_id]
|
64
67
|
elsif lb_options[:security_group_name]
|
@@ -68,6 +71,8 @@ module AWSDriver
|
|
68
71
|
availability_zones = lb_options[:availability_zones]
|
69
72
|
listeners = lb_options[:listeners]
|
70
73
|
|
74
|
+
validate_listeners(listeners)
|
75
|
+
|
71
76
|
lb_optionals = {}
|
72
77
|
lb_optionals[:security_groups] = [security_group] if security_group
|
73
78
|
lb_optionals[:availability_zones] = availability_zones if availability_zones
|
@@ -117,43 +122,44 @@ module AWSDriver
|
|
117
122
|
end
|
118
123
|
end
|
119
124
|
|
120
|
-
# Update listeners
|
121
|
-
perform_listener_action = proc do |desc, &block|
|
122
|
-
perform_listener_action = proc { |desc, &block| perform_action(desc, &block) }
|
123
|
-
perform_action([ " update listener #{listener.port}", desc ], &block)
|
124
|
-
end
|
125
|
+
# Update listeners - THIS IS NOT ATOMIC
|
125
126
|
add_listeners = {}
|
126
127
|
listeners.each { |l| add_listeners[l[:port]] = l } if listeners
|
127
128
|
actual_elb.listeners.each do |listener|
|
128
129
|
desired_listener = add_listeners.delete(listener.port)
|
129
130
|
if desired_listener
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
131
|
+
|
132
|
+
# listener.(port|protocol|instance_port|instance_protocol) are immutable for the life
|
133
|
+
# of the listener - must create a new one and delete old one
|
134
|
+
immutable_updates = []
|
135
|
+
if listener.protocol != desired_listener[:protocol].to_sym.downcase
|
136
|
+
immutable_updates << " update protocol from #{listener.protocol.inspect} to #{desired_listener[:protocol].inspect}"
|
134
137
|
end
|
135
138
|
if listener.instance_port != desired_listener[:instance_port]
|
136
|
-
|
137
|
-
listener.instance_port = desired_listener[:instance_port]
|
138
|
-
end
|
139
|
+
immutable_updates << " update instance port from #{listener.instance_port.inspect} to #{desired_listener[:instance_port].inspect}"
|
139
140
|
end
|
140
|
-
if listener.instance_protocol != desired_listener[:instance_protocol]
|
141
|
-
|
142
|
-
listener.instance_protocol = desired_listener[:instance_protocol]
|
143
|
-
end
|
141
|
+
if listener.instance_protocol != desired_listener[:instance_protocol].to_sym.downcase
|
142
|
+
immutable_updates << " update instance protocol from #{listener.instance_protocol.inspect} to #{desired_listener[:instance_protocol].inspect}"
|
144
143
|
end
|
145
|
-
if
|
146
|
-
|
144
|
+
if !immutable_updates.empty?
|
145
|
+
perform_action.call(immutable_updates) do
|
146
|
+
listener.delete
|
147
|
+
actual_elb.listeners.create(desired_listener)
|
148
|
+
end
|
149
|
+
elsif listener.server_certificate != desired_listener[:server_certificate]
|
150
|
+
# Server certificate is mutable - if no immutable changes required a full recreate, update cert
|
151
|
+
perform_action.call(" update server certificate from #{listener.server_certificate} to #{desired_listener[:server_certificate]}") do
|
147
152
|
listener.server_certificate = desired_listener[:server_certificate]
|
148
153
|
end
|
149
154
|
end
|
155
|
+
|
150
156
|
else
|
151
157
|
perform_action.call(" remove listener #{listener.port}") do
|
152
158
|
listener.delete
|
153
159
|
end
|
154
160
|
end
|
155
161
|
end
|
156
|
-
add_listeners.each do |listener|
|
162
|
+
add_listeners.values.each do |listener|
|
157
163
|
updates = [ " add listener #{listener[:port]}" ]
|
158
164
|
updates << " set protocol to #{listener[:protocol].inspect}"
|
159
165
|
updates << " set instance port to #{listener[:instance_port].inspect}"
|
@@ -213,7 +219,7 @@ module AWSDriver
|
|
213
219
|
image_options[:instance_id] ||= machine_spec.location['instance_id']
|
214
220
|
image_options[:description] ||= "Image #{image_spec.name} created from machine #{machine_spec.name}"
|
215
221
|
Chef::Log.debug "AWS Image options: #{image_options.inspect}"
|
216
|
-
image = ec2.images.create(image_options)
|
222
|
+
image = ec2.images.create(image_options.to_hash)
|
217
223
|
image_spec.location = {
|
218
224
|
'driver_url' => driver_url,
|
219
225
|
'driver_version' => Chef::Provisioning::AWSDriver::VERSION,
|
@@ -253,7 +259,7 @@ module AWSDriver
|
|
253
259
|
action_handler.perform_action "De-registering image #{image_spec.name}" do
|
254
260
|
actual_image.deregister
|
255
261
|
end
|
256
|
-
|
262
|
+
if snapshots.any?
|
257
263
|
action_handler.perform_action "Deleting image #{image_spec.name} snapshots" do
|
258
264
|
snapshots.each do |snap|
|
259
265
|
snap.delete
|
@@ -263,22 +269,37 @@ module AWSDriver
|
|
263
269
|
end
|
264
270
|
end
|
265
271
|
|
272
|
+
def user_data
|
273
|
+
# TODO: Make this use HTTPS at some point.
|
274
|
+
<<EOD
|
275
|
+
<powershell>
|
276
|
+
winrm quickconfig -q
|
277
|
+
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="300"}'
|
278
|
+
winrm set winrm/config '@{MaxTimeoutms="1800000"}'
|
279
|
+
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
|
280
|
+
winrm set winrm/config/service/auth '@{Basic="true"}'
|
281
|
+
|
282
|
+
netsh advfirewall firewall add rule name="WinRM 5985" protocol=TCP dir=in localport=5985 action=allow
|
283
|
+
netsh advfirewall firewall add rule name="WinRM 5986" protocol=TCP dir=in localport=5986 action=allow
|
284
|
+
|
285
|
+
net stop winrm
|
286
|
+
sc config winrm start=auto
|
287
|
+
net start winrm
|
288
|
+
</powershell>
|
289
|
+
EOD
|
290
|
+
end
|
291
|
+
|
266
292
|
# Machine methods
|
267
293
|
def allocate_machine(action_handler, machine_spec, machine_options)
|
268
294
|
actual_instance = instance_for(machine_spec)
|
269
295
|
if actual_instance == nil || !actual_instance.exists? || actual_instance.status == :terminated
|
270
|
-
|
271
|
-
bootstrap_options = (machine_options[:bootstrap_options] || {}).to_h.dup
|
272
|
-
bootstrap_options[:image_id] = image_id
|
273
|
-
if !bootstrap_options[:key_name]
|
274
|
-
Chef::Log.debug('No key specified, generating a default one...')
|
275
|
-
bootstrap_options[:key_name] = default_aws_keypair(action_handler, machine_spec)
|
276
|
-
end
|
277
|
-
Chef::Log.debug "AWS Bootstrap options: #{bootstrap_options.inspect}"
|
296
|
+
bootstrap_options = bootstrap_options_for(action_handler, machine_spec, machine_options)
|
278
297
|
|
279
|
-
action_handler.perform_action "Create #{machine_spec.name} with AMI #{image_id} in #{@region}" do
|
298
|
+
action_handler.perform_action "Create #{machine_spec.name} with AMI #{bootstrap_options[:image_id]} in #{@region}" do
|
280
299
|
Chef::Log.debug "Creating instance with bootstrap options #{bootstrap_options}"
|
281
|
-
|
300
|
+
|
301
|
+
instance = ec2.instances.create(bootstrap_options.to_hash)
|
302
|
+
|
282
303
|
# Make sure the instance is ready to be tagged
|
283
304
|
sleep 5 while instance.status == :pending
|
284
305
|
# TODO add other tags identifying user / node url (same as fog)
|
@@ -288,7 +309,7 @@ module AWSDriver
|
|
288
309
|
'driver_version' => Chef::Provisioning::AWSDriver::VERSION,
|
289
310
|
'allocated_at' => Time.now.utc.to_s,
|
290
311
|
'host_node' => action_handler.host_node,
|
291
|
-
'image_id' =>
|
312
|
+
'image_id' => bootstrap_options[:image_id],
|
292
313
|
'instance_id' => instance.id
|
293
314
|
}
|
294
315
|
machine_spec.location['key_name'] = bootstrap_options[:key_name] if bootstrap_options[:key_name]
|
@@ -374,7 +395,24 @@ module AWSDriver
|
|
374
395
|
end
|
375
396
|
end
|
376
397
|
|
377
|
-
def
|
398
|
+
def bootstrap_options_for(action_handler, machine_spec, machine_options)
|
399
|
+
bootstrap_options = (machine_options[:bootstrap_options] || {}).to_h.dup
|
400
|
+
image_id = bootstrap_options[:image_id] || machine_options[:image_id] || default_ami_for_region(@region)
|
401
|
+
bootstrap_options[:image_id] = image_id
|
402
|
+
if !bootstrap_options[:key_name]
|
403
|
+
Chef::Log.debug('No key specified, generating a default one...')
|
404
|
+
bootstrap_options[:key_name] = default_aws_keypair(action_handler, machine_spec)
|
405
|
+
end
|
406
|
+
|
407
|
+
if machine_options[:is_windows]
|
408
|
+
Chef::Log.debug "Setting WinRM userdata..."
|
409
|
+
bootstrap_options[:user_data] = user_data
|
410
|
+
else
|
411
|
+
Chef::Log.debug "Non-windows, not setting userdata"
|
412
|
+
end
|
413
|
+
|
414
|
+
Chef::Log.debug "AWS Bootstrap options: #{bootstrap_options.inspect}"
|
415
|
+
bootstrap_options
|
378
416
|
end
|
379
417
|
|
380
418
|
def ec2
|
@@ -446,8 +484,11 @@ module AWSDriver
|
|
446
484
|
end
|
447
485
|
|
448
486
|
def transport_for(machine_spec, machine_options, instance)
|
449
|
-
|
450
|
-
|
487
|
+
if machine_spec.location['is_windows']
|
488
|
+
create_winrm_transport(machine_spec, machine_options, instance)
|
489
|
+
else
|
490
|
+
create_ssh_transport(machine_spec, machine_options, instance)
|
491
|
+
end
|
451
492
|
end
|
452
493
|
|
453
494
|
def compute_options
|
@@ -461,9 +502,9 @@ module AWSDriver
|
|
461
502
|
else
|
462
503
|
credentials = Credentials.new
|
463
504
|
if driver_options[:aws_config_file]
|
464
|
-
credentials.load_ini(driver_options
|
505
|
+
credentials.load_ini(driver_options[:aws_config_file])
|
465
506
|
elsif driver_options[:aws_csv_file]
|
466
|
-
credentials.load_csv(driver_options
|
507
|
+
credentials.load_csv(driver_options[:aws_csv_file])
|
467
508
|
else
|
468
509
|
credentials.load_default
|
469
510
|
end
|
@@ -498,6 +539,53 @@ module AWSDriver
|
|
498
539
|
end
|
499
540
|
end
|
500
541
|
|
542
|
+
def create_winrm_transport(machine_spec, machine_options, instance)
|
543
|
+
remote_host = determine_remote_host(machine_spec, instance)
|
544
|
+
|
545
|
+
port = machine_spec.location['winrm_port'] || 5985
|
546
|
+
endpoint = "http://#{remote_host}:#{port}/wsman"
|
547
|
+
type = :plaintext
|
548
|
+
pem_bytes = get_private_key(instance.key_name)
|
549
|
+
encrypted_admin_password = wait_for_admin_password(machine_spec)
|
550
|
+
|
551
|
+
decoded = Base64.decode64(encrypted_admin_password)
|
552
|
+
private_key = OpenSSL::PKey::RSA.new(pem_bytes)
|
553
|
+
decrypted_password = private_key.private_decrypt decoded
|
554
|
+
|
555
|
+
winrm_options = {
|
556
|
+
:user => machine_spec.location['winrm_username'] || 'Administrator',
|
557
|
+
:pass => decrypted_password,
|
558
|
+
:disable_sspi => true,
|
559
|
+
:basic_auth_only => true
|
560
|
+
}
|
561
|
+
|
562
|
+
Chef::Provisioning::Transport::WinRM.new("#{endpoint}", type, winrm_options, {})
|
563
|
+
end
|
564
|
+
|
565
|
+
def wait_for_admin_password(machine_spec)
|
566
|
+
time_elapsed = 0
|
567
|
+
sleep_time = 10
|
568
|
+
max_wait_time = 900 # 15 minutes
|
569
|
+
encrypted_admin_password = nil
|
570
|
+
instance_id = machine_spec.location['instance_id']
|
571
|
+
|
572
|
+
Chef::Log.info "waiting for #{machine_spec.name}'s admin password to be available..."
|
573
|
+
while time_elapsed < max_wait_time && encrypted_admin_password.nil?
|
574
|
+
response = ec2.client.get_password_data({ :instance_id => instance_id })
|
575
|
+
encrypted_admin_password = response['password_data'.to_sym]
|
576
|
+
|
577
|
+
if encrypted_admin_password.nil?
|
578
|
+
Chef::Log.info "#{time_elapsed}/#{max_wait_time}s elapsed -- sleeping #{sleep_time} for #{machine_spec.name}'s admin password."
|
579
|
+
sleep(sleep_time)
|
580
|
+
time_elapsed += sleep_time
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
Chef::Log.info "#{machine_spec.name}'s admin password is available!"
|
585
|
+
|
586
|
+
encrypted_admin_password
|
587
|
+
end
|
588
|
+
|
501
589
|
def create_ssh_transport(machine_spec, machine_options, instance)
|
502
590
|
ssh_options = ssh_options_for(machine_spec, machine_options, instance)
|
503
591
|
username = machine_spec.location['ssh_username'] || machine_options[:ssh_username] || default_ssh_username
|
@@ -509,24 +597,26 @@ module AWSDriver
|
|
509
597
|
options[:prefix] = 'sudo '
|
510
598
|
end
|
511
599
|
|
512
|
-
remote_host =
|
600
|
+
remote_host = determine_remote_host(machine_spec, instance)
|
601
|
+
|
602
|
+
#Enable pty by default
|
603
|
+
options[:ssh_pty_enable] = true
|
604
|
+
options[:ssh_gateway] = machine_spec.location['ssh_gateway'] if machine_spec.location.has_key?('ssh_gateway')
|
605
|
+
|
606
|
+
Chef::Provisioning::Transport::SSH.new(remote_host, username, ssh_options, options, config)
|
607
|
+
end
|
513
608
|
|
609
|
+
def determine_remote_host(machine_spec, instance)
|
514
610
|
if machine_spec.location['use_private_ip_for_ssh']
|
515
|
-
|
611
|
+
instance.private_ip_address
|
516
612
|
elsif !instance.public_ip_address
|
517
613
|
Chef::Log.warn("Server #{machine_spec.name} has no public ip address. Using private ip '#{instance.private_ip_address}'. Set driver option 'use_private_ip_for_ssh' => true if this will always be the case ...")
|
518
|
-
|
614
|
+
instance.private_ip_address
|
519
615
|
elsif instance.public_ip_address
|
520
|
-
|
616
|
+
instance.public_ip_address
|
521
617
|
else
|
522
618
|
raise "Server #{instance.id} has no private or public IP address!"
|
523
619
|
end
|
524
|
-
|
525
|
-
#Enable pty by default
|
526
|
-
options[:ssh_pty_enable] = true
|
527
|
-
options[:ssh_gateway] = machine_spec.location['ssh_gateway'] if machine_spec.location.has_key?('ssh_gateway')
|
528
|
-
|
529
|
-
Chef::Provisioning::Transport::SSH.new(remote_host, username, ssh_options, options, config)
|
530
620
|
end
|
531
621
|
|
532
622
|
def ssh_options_for(machine_spec, machine_options, instance)
|
@@ -692,7 +782,7 @@ module AWSDriver
|
|
692
782
|
Chef::Log.warn "Machine #{machine_spec.name} (#{machine_spec.location['instance_id']} on #{driver_url}) no longer exists. Recreating ..."
|
693
783
|
end
|
694
784
|
|
695
|
-
bootstrap_options =
|
785
|
+
bootstrap_options = bootstrap_options_for(action_handler, machine_spec, machine_options)
|
696
786
|
by_bootstrap_options[bootstrap_options] ||= []
|
697
787
|
by_bootstrap_options[bootstrap_options] << machine_spec
|
698
788
|
end
|
@@ -742,13 +832,26 @@ module AWSDriver
|
|
742
832
|
def create_many_instances(num_servers, bootstrap_options, parallelizer)
|
743
833
|
parallelizer.parallelize(1.upto(num_servers)) do |i|
|
744
834
|
clean_bootstrap_options = Marshal.load(Marshal.dump(bootstrap_options))
|
745
|
-
instance = ec2.instances.create(clean_bootstrap_options)
|
835
|
+
instance = ec2.instances.create(clean_bootstrap_options.to_hash)
|
746
836
|
|
747
837
|
yield instance if block_given?
|
748
838
|
instance
|
749
839
|
end.to_a
|
750
840
|
end
|
751
841
|
|
842
|
+
# The listeners API is different between the SDK v1 and v2
|
843
|
+
# http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/ELB/Listener.html
|
844
|
+
VALID_LISTENER_KEYS = [:port, :protocol, :instance_port, :instance_protocol]
|
845
|
+
def validate_listeners(listeners)
|
846
|
+
listeners.each do |listener|
|
847
|
+
listener.keys.each do |k|
|
848
|
+
unless VALID_LISTENER_KEYS.include?(k)
|
849
|
+
raise "#{k} is an invalid listener key, can be one of #{VALID_LISTENER_KEYS.inspect}"
|
850
|
+
end
|
851
|
+
end
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
752
855
|
end
|
753
856
|
end
|
754
857
|
end
|
@@ -13,4 +13,5 @@ class Chef::Resource::AwsAutoScalingGroup < Chef::Resource::AwsResource
|
|
13
13
|
attribute :launch_config, :kind_of => String
|
14
14
|
attribute :min_size, :kind_of => Integer, :default => 1
|
15
15
|
attribute :max_size, :kind_of => Integer, :default => 4
|
16
|
+
attribute :load_balancers, :kind_of => Array
|
16
17
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
+
require 'chef/mixin/shell_out'
|
1
2
|
require 'chef/dsl/recipe'
|
2
3
|
require 'chef/provisioning'
|
3
4
|
require 'chef/provisioning/aws_driver'
|
5
|
+
require 'chef/platform'
|
6
|
+
require 'chef/run_context'
|
7
|
+
require 'chef/event_dispatch/dispatcher'
|
4
8
|
|
5
9
|
RSpec.configure do |rspec|
|
6
10
|
rspec.run_all_when_everything_filtered = true
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'chef_zero_rspec_helper'
|
3
|
+
AWS.stub!
|
4
|
+
|
5
|
+
describe Chef::Provider::AwsSubnet do
|
6
|
+
extend ChefZeroRspecHelper
|
7
|
+
let(:new_resource) {
|
8
|
+
Chef::Resource::AwsSubnet.new('my_subnet', run_context)
|
9
|
+
}
|
10
|
+
let(:my_node) {
|
11
|
+
node = Chef::Node.new
|
12
|
+
node.automatic['platform'] = 'ubuntu'
|
13
|
+
node.automatic['platform_version'] = '12.04'
|
14
|
+
node
|
15
|
+
}
|
16
|
+
let(:events) { Chef::EventDispatch::Dispatcher.new }
|
17
|
+
let(:run_context) {
|
18
|
+
cookbook_collection = {}
|
19
|
+
Chef::RunContext.new(my_node, cookbook_collection ,events)
|
20
|
+
}
|
21
|
+
|
22
|
+
subject(:provider) {
|
23
|
+
described_class.new(new_resource, run_context)
|
24
|
+
}
|
25
|
+
|
26
|
+
when_the_chef_server "is empty" do
|
27
|
+
describe '#action_create' do
|
28
|
+
it 'requires cidr_block' do
|
29
|
+
expect{ provider.action_create }
|
30
|
+
.to raise_error(
|
31
|
+
RuntimeError, "Can't create a Subnet without a CIDR block"
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'requires VPC to exist' do
|
36
|
+
new_resource.cidr_block('1.2.3.4/24')
|
37
|
+
new_resource.vpc('my_vpc')
|
38
|
+
allow_any_instance_of(AWS::EC2::VPCCollection)
|
39
|
+
.to receive(:with_tag)
|
40
|
+
.and_return(nil)
|
41
|
+
expect{ provider.action_create }
|
42
|
+
.to raise_error(AWS::Core::OptionGrammar::FormatError)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should work with a VPC object' do
|
46
|
+
new_resource.cidr_block('1.2.3.4/24')
|
47
|
+
allow_any_instance_of(AWS::EC2::VPCCollection)
|
48
|
+
.to receive(:with_tag)
|
49
|
+
.and_return( [ AWS::EC2::VPC.new('vpc-abcd1234') ] )
|
50
|
+
allow_any_instance_of(AWS::EC2::SubnetCollection)
|
51
|
+
.to receive(:create)
|
52
|
+
.and_return(AWS::EC2::Subnet.new('subnet-feeddeed'))
|
53
|
+
expect(new_resource).to receive(:save)
|
54
|
+
provider.action_create
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should not converge if subnet already exists' do
|
58
|
+
new_resource.cidr_block('1.2.3.4/24')
|
59
|
+
allow_any_instance_of(AWS::EC2::SubnetCollection)
|
60
|
+
.to receive(:with_tag)
|
61
|
+
.and_return([AWS::EC2::Subnet.new('subnet-feeddeed')])
|
62
|
+
expect(provider).to_not receive(:converge_by)
|
63
|
+
provider.action_create
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'chef_zero_rspec_helper'
|
3
|
+
|
4
|
+
describe Chef::Resource::AwsSubnet do
|
5
|
+
extend ChefZeroRspecHelper
|
6
|
+
let(:my_node) { Chef::Node.new() }
|
7
|
+
let(:events) { Chef::EventDispatch::Dispatcher.new }
|
8
|
+
let(:run_context) { Chef::RunContext.new(my_node,{},events) }
|
9
|
+
|
10
|
+
subject(:resource) {
|
11
|
+
described_class.new('my_subnet', run_context)
|
12
|
+
}
|
13
|
+
|
14
|
+
when_the_chef_server "is empty" do
|
15
|
+
it 'should match resource name' do
|
16
|
+
expect(resource.resource_name).to eq(:aws_subnet)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should match name' do
|
20
|
+
expect(resource.name).to eq('my_subnet')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chef-provisioning-aws
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Ewart
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef
|
@@ -136,8 +136,12 @@ files:
|
|
136
136
|
- lib/chef/resource/aws_sqs_queue.rb
|
137
137
|
- lib/chef/resource/aws_subnet.rb
|
138
138
|
- lib/chef/resource/aws_vpc.rb
|
139
|
+
- spec/acceptance/aws_ebs_volume/nodes/ettores-mbp.lan.json
|
140
|
+
- spec/chef_zero_rspec_helper.rb
|
139
141
|
- spec/spec_helper.rb
|
140
142
|
- spec/unit/aws_driver/credentials_spec.rb
|
143
|
+
- spec/unit/provider/aws_subnet_spec.rb
|
144
|
+
- spec/unit/resource/aws_subnet_spec.rb
|
141
145
|
homepage: https://github.com/opscode/chef-provisioning-aws
|
142
146
|
licenses: []
|
143
147
|
metadata: {}
|
@@ -157,8 +161,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
157
161
|
version: '0'
|
158
162
|
requirements: []
|
159
163
|
rubyforge_project:
|
160
|
-
rubygems_version: 2.
|
164
|
+
rubygems_version: 2.2.2
|
161
165
|
signing_key:
|
162
166
|
specification_version: 4
|
163
167
|
summary: Provisioner for creating aws containers in Chef Provisioning.
|
164
168
|
test_files: []
|
169
|
+
has_rdoc:
|