chef-provisioning-aws 0.2.2 → 0.3
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.
- 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:
|