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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 336d0793083b6da83f022552b5388784cf8733ab
4
- data.tar.gz: e441903ab07300b474e22d6d1eae7f2e42327f37
3
+ metadata.gz: 5ec492afa3798bae513c3b61ea626958a6b7e954
4
+ data.tar.gz: 1b8ecf59b63a169aaa7d744efe491f3f1627065c
5
5
  SHA512:
6
- metadata.gz: b230e7c97d4508ee54fb026bcda2c8945ae1e3f52dce1b46d67a7981757e8c097ddfe8e10dd50688f65d1b6e41c1f7a912915ae965edb5a112ed2bca91d15ca3
7
- data.tar.gz: b3a7a575bba6c5a23ebbef6838f39b1055b55d8f83f0f723eb9137fda4e412d8fb3523ca86d491f3f464fdf0a5224884907d8358ead099681a87b4ec730769c2
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
- A mostly-functional implementation of an AWS driver that doesn't use fog
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 do
5
- require File.expand_path('spec/run')
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
- converge_by "Creating new Auto Scaling group #{id} in #{new_resource.region_name}" do
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
- :launch_configuration => new_resource.launch_config,
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 #{id} in #{new_resource.region_name}" do
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
@@ -32,7 +32,7 @@ class Chef::Provider::AwsSecurityGroup < Chef::Provider::AwsProvider
32
32
  end
33
33
 
34
34
  action :delete do
35
- if existing_vpc
35
+ if existing_sg
36
36
  converge_by "Deleting SG #{new_resource.name} in #{new_resource.region_name}" do
37
37
  existing_sg.delete
38
38
  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
- opts[:availability_zone] = new_resource.availability_zone if new_resource.availability_zone
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
- if listener.protocol != desired_listener[:protocol]
131
- perform_listener_action.call(" update protocol from #{listener.protocol.inspect} to #{desired_listener[:protocol].inspect}'") do
132
- listener.protocol = desired_listener[:protocol]
133
- end
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
- perform_listener_action.call(" update instance port from #{listener.instance_port.inspect} to #{desired_listener[:instance_port].inspect}'") do
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
- perform_listener_action.call(" update instance protocol from #{listener.instance_protocol.inspect} to #{desired_listener[:instance_protocol].inspect}'") do
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 listener.server_certificate != desired_listener[:server_certificate]
146
- perform_listener_action.call(" update server certificate from #{listener.server_certificate} to #{desired_listener[:server_certificate]}'") do
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
- unless snapshots.any?
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
- image_id = machine_options[:image_id] || default_ami_for_region(@region)
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
- instance = ec2.instances.create(bootstrap_options)
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' => machine_options[: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 start_machine(action_handler, machine_spec, machine_options, base_image_name)
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
- # TODO winrm
450
- create_ssh_transport(machine_spec, machine_options, instance)
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.delete(:aws_config_file))
505
+ credentials.load_ini(driver_options[:aws_config_file])
465
506
  elsif driver_options[:aws_csv_file]
466
- credentials.load_csv(driver_options.delete(:aws_csv_file))
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 = nil
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
- remote_host = instance.private_ip_address
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
- remote_host = instance.private_ip_address
614
+ instance.private_ip_address
519
615
  elsif instance.public_ip_address
520
- remote_host = instance.public_ip_address
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 = machine_options[: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
@@ -1,7 +1,7 @@
1
1
  class Chef
2
2
  module Provisioning
3
3
  module AWSDriver
4
- VERSION = '0.2.2'
4
+ VERSION = '0.3'
5
5
  end
6
6
  end
7
7
  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
@@ -0,0 +1,3 @@
1
+ {
2
+ "name": "ettores-mbp.lan"
3
+ }
@@ -0,0 +1,8 @@
1
+ # Cargo culted from John Keiser's
2
+ # support/shared/integration/integration_helper
3
+
4
+ require 'chef_zero/rspec'
5
+
6
+ module ChefZeroRspecHelper
7
+ include ChefZero::RSpec
8
+ end
@@ -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.2.2
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-04-10 00:00:00.000000000 Z
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.4.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: