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 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: