convection 0.2.32 → 0.2.33

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -1
  3. data/.rubocop_todo.yml +1 -2
  4. data/Gemfile +2 -1
  5. data/Rakefile +3 -7
  6. data/lib/convection.rb +2 -2
  7. data/lib/convection/control/stack.rb +49 -10
  8. data/lib/convection/model/event.rb +3 -2
  9. data/lib/convection/model/template.rb +13 -1
  10. data/lib/convection/model/template/metadata.rb +22 -0
  11. data/lib/convection/model/template/resource/aws_ec2_subnet.rb +1 -0
  12. data/lib/convection/model/template/resource/aws_events_rule.rb +4 -0
  13. data/spec/cf_client_context.rb +10 -0
  14. data/spec/collect_availability_zones_task_context.rb +17 -0
  15. data/spec/convection/control/stack/after_create_tasks_spec.rb +51 -0
  16. data/spec/convection/control/stack/after_delete_tasks_spec.rb +51 -0
  17. data/spec/convection/control/stack/after_update_tasks_spec.rb +54 -0
  18. data/spec/convection/control/stack/before_create_tasks_spec.rb +52 -0
  19. data/spec/convection/control/stack/before_delete_tasks_spec.rb +51 -0
  20. data/spec/convection/control/stack/before_update_tasks_spec.rb +55 -0
  21. data/spec/convection/dsl/intrinsic_functions_spec.rb +88 -0
  22. data/spec/convection/model/template/condition_spec.rb +38 -0
  23. data/spec/convection/model/template/resource/directoryservice_simple_ad_spec.rb +39 -0
  24. data/spec/convection/model/template/resource/ec2_security_group_spec.rb +39 -0
  25. data/spec/convection/model/template/resource/ec2_subnet_spec.rb +48 -0
  26. data/spec/convection/model/template/resource/elasticache_cache_cluster_spec.rb +52 -0
  27. data/spec/convection/model/template/resource/elasticache_parameter_group_spec.rb +38 -0
  28. data/spec/convection/model/template/resource/elasticache_security_group_ingress_spec.rb +40 -0
  29. data/spec/convection/model/template/resource/elasticache_security_group_spec.rb +32 -0
  30. data/spec/convection/model/template/resource/events_rule_spec.rb +44 -0
  31. data/spec/convection/model/template/resource/iam_role_spec.rb +37 -0
  32. data/spec/convection/model/template/resource/lambdas_spec.rb +70 -0
  33. data/spec/convection/model/template/resource/loggroups_spec.rb +34 -0
  34. data/spec/convection/model/template/resource/permission_spec.rb +43 -0
  35. data/spec/convection/model/template/resource/rds_security_groups_spec.rb +50 -0
  36. data/spec/convection/model/template/resource/vpc_endpoints_spec.rb +65 -0
  37. data/spec/convection/model/template/resource_attribute/update_policies_spec.rb +66 -0
  38. data/spec/convection/model/template/template_spec.rb +60 -0
  39. data/spec/convection/model/template/validate_bytesize_spec.rb +49 -0
  40. data/spec/convection/model/template/validate_description_spec.rb +31 -0
  41. data/spec/convection/model/template/validate_mappings_spec.rb +88 -0
  42. data/spec/convection/model/template/validate_outputs_spec.rb +62 -0
  43. data/spec/convection/model/template/validate_parameters_spec.rb +84 -0
  44. data/spec/convection/model/template/validate_resources_spec.rb +50 -0
  45. data/spec/ec2_client_context.rb +18 -0
  46. data/spec/spec_helper.rb +11 -0
  47. metadata +72 -40
  48. data/test/convection/model/test_conditions.rb +0 -121
  49. data/test/convection/model/test_directory_service.rb +0 -40
  50. data/test/convection/model/test_elasticache.rb +0 -97
  51. data/test/convection/model/test_lambdas.rb +0 -53
  52. data/test/convection/model/test_loggroups.rb +0 -25
  53. data/test/convection/model/test_permission.rb +0 -31
  54. data/test/convection/model/test_rds.rb +0 -76
  55. data/test/convection/model/test_template.rb +0 -64
  56. data/test/convection/model/test_trust.rb +0 -28
  57. data/test/convection/model/test_update_policies.rb +0 -54
  58. data/test/convection/model/test_validation.rb +0 -216
  59. data/test/convection/model/test_vpc_endpoint.rb +0 -51
  60. data/test/convection/tasks/test_after_create_tasks.rb +0 -66
  61. data/test/convection/tasks/test_after_delete_tasks.rb +0 -66
  62. data/test/convection/tasks/test_after_update_tasks.rb +0 -71
  63. data/test/convection/tasks/test_before_create_tasks.rb +0 -66
  64. data/test/convection/tasks/test_before_delete_tasks.rb +0 -66
  65. data/test/convection/tasks/test_before_update_tasks.rb +0 -71
  66. data/test/test_helper.rb +0 -72
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f916acd8ca0393e33372be95052b9e9bec6085ba
4
- data.tar.gz: 68fdb161218ff72237d6319eeb7afc91ee8a9a23
3
+ metadata.gz: 70262a473c642f00cc51b9077ca02c2617194320
4
+ data.tar.gz: bba7acb1a3242c6b354a384e9924f33c8a69ce13
5
5
  SHA512:
6
- metadata.gz: 3e74cbb7c38420ecd793f817b49bba6fb5ccc0f8b08ea0e0f70727d86753225108eaa21b6cc71665038fc3dd33be9dec6d471b9565d417e9b4a30ec7bf18bb21
7
- data.tar.gz: d0695a09dfefbdd5ed9730b5d11185897e9c2914d27ae5721516679be5d9f7726a009bcf191005d98d61f687fa751dbe3d73de7efd949a478902ae3da8cd971f
6
+ metadata.gz: df10a72ab338b4e6dec65b93942a0577d53db0d88bbe30e820a1536918192a8adee745b016c4067e3973a1bffccac2ee3cf9016d0930e3a52774185e7bca1edc
7
+ data.tar.gz: 61f8a1e4761e464a1a3a18139c02caa49839d93fb89be9cc68affe13d4baa9dc2bfbbd47cdcea31e442d4e1d154e6878da50dae25304fa09ce6f7850949eb677
@@ -4,7 +4,6 @@ AllCops:
4
4
  Include:
5
5
  - lib/**/*
6
6
  - bin/**/*
7
- - test/**/*
8
7
  - Gemfile
9
8
  - Rakefile
10
9
  - Thorfile
@@ -70,13 +70,12 @@ Style/Alias:
70
70
  Style/ClassAndModuleChildren:
71
71
  Exclude:
72
72
  - 'lib/convection/model/template.rb'
73
- - 'test/test_helper.rb'
73
+ - 'spec/**/*'
74
74
 
75
75
  # Offense count: 4
76
76
  Style/Documentation:
77
77
  Exclude:
78
78
  - 'spec/**/*'
79
- - 'test/**/*'
80
79
  - 'lib/convection/model/attributes.rb'
81
80
  - 'lib/convection/model/mixin/colorize.rb'
82
81
  - 'lib/convection/model/template/condition.rb'
data/Gemfile CHANGED
@@ -5,9 +5,10 @@ gemspec
5
5
 
6
6
  group :development do
7
7
  gem 'bundler', '~> 1.7'
8
- gem 'minitest'
8
+ gem 'rspec'
9
9
  gem 'rake', '~> 10.0'
10
10
  gem 'rubocop', '~> 0.40.0'
11
11
  gem 'simplecov'
12
12
  gem 'thor-scmversion', '= 1.7.0'
13
+ gem 'yard'
13
14
  end
data/Rakefile CHANGED
@@ -1,12 +1,8 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rubocop/rake_task'
3
- require 'rake/testtask'
3
+ require 'rspec/core/rake_task'
4
4
 
5
5
  RuboCop::RakeTask.new
6
+ RSpec::Core::RakeTask.new(:spec)
6
7
 
7
- Rake::TestTask.new do |t|
8
- t.libs << 'test'
9
- t.pattern = 'test/**/test_*.rb'
10
- end
11
-
12
- task :default => [:test, :rubocop]
8
+ task :default => [:spec, :rubocop]
@@ -7,8 +7,8 @@ module Convection
7
7
  Model::Template.new(*args, &block)
8
8
  end
9
9
 
10
- def stack(*args)
11
- Control::Stack.new(*args)
10
+ def stack(*args, &block)
11
+ Control::Stack.new(*args, &block)
12
12
  end
13
13
  end
14
14
  end
@@ -52,6 +52,9 @@ module Convection
52
52
 
53
53
  ## Internal status
54
54
  NOT_CREATED = 'NOT_CREATED'.freeze
55
+ TASK_COMPLETE = 'TASK_COMPLETE'.freeze
56
+ TASK_FAILED = 'TASK_FAILED'.freeze
57
+ TASK_IN_PROGRESS = 'TASK_IN_PROGRESS'.freeze
55
58
 
56
59
  def initialize(name, template, options = {}, &block)
57
60
  @name = name
@@ -177,6 +180,26 @@ module Convection
177
180
  @template.diff(@current_template)
178
181
  end
179
182
 
183
+ def resource_changes?
184
+ ours = { 'Resources' => @template.resources.map(&:render) }
185
+ thiers = { 'Resources' => @current_template['Resources'] }
186
+
187
+ ours.diff(thiers).any?
188
+ end
189
+
190
+ def resource_dependent_changes?
191
+ ours = {
192
+ 'Conditions' => @template.conditions.map(&:render),
193
+ 'Outputs' => @template.outputs.map(&:render)
194
+ }
195
+ theirs = {
196
+ 'Conditions' => @current_template['Conditions'],
197
+ 'Outputs' => @current_template['Outputs']
198
+ }
199
+
200
+ ours.diff(theirs).any?
201
+ end
202
+
180
203
  ##
181
204
  # Controllers
182
205
  ##
@@ -194,12 +217,16 @@ module Convection
194
217
  block.call(Model::Event.new(:complete, "Stack #{ name } has no changes", :info)) if block
195
218
  get_status
196
219
  return
220
+ elsif !resource_changes? && resource_dependent_changes?
221
+ message = "Stack #{ name } has no convergable changes (you must update Resources to update Conditions, Metadata, or Outputs)"
222
+ block.call(Model::Event.new(UPDATE_FAILED, message, :warn)) if block
223
+ get_status
224
+ return
197
225
  end
198
226
 
199
227
  ## Execute before update tasks
200
228
  @tasks[:before_update].delete_if do |task|
201
- task.call(self)
202
- task.success?
229
+ run_task(:before_update, task, &block)
203
230
  end
204
231
 
205
232
  ## Update
@@ -209,8 +236,7 @@ module Convection
209
236
  else
210
237
  ## Execute before create tasks
211
238
  @tasks[:before_create].delete_if do |task|
212
- task.call(self)
213
- task.success?
239
+ run_task(:before_create, task, &block)
214
240
  end
215
241
 
216
242
  ## Create
@@ -229,8 +255,7 @@ module Convection
229
255
  ## Execute after create tasks
230
256
  after_task_type = existing_stack ? :after_update : :after_create
231
257
  @tasks[after_task_type].delete_if do |task|
232
- task.call(self)
233
- task.success?
258
+ run_task(after_task_type, task, &block)
234
259
  end
235
260
  rescue Aws::Errors::ServiceError => e
236
261
  @errors << e
@@ -239,8 +264,7 @@ module Convection
239
264
  def delete(&block)
240
265
  ## Execute before delete tasks
241
266
  @tasks[:before_delete].delete_if do |task|
242
- task.call(self)
243
- task.success?
267
+ run_task(:before_delete, task, &block)
244
268
  end
245
269
 
246
270
  @cf_client.delete_stack(
@@ -254,8 +278,7 @@ module Convection
254
278
 
255
279
  ## Execute after delete tasks
256
280
  @tasks[:after_delete].delete_if do |task|
257
- task.call(self)
258
- task.success?
281
+ run_task(:after_delete, task, &block)
259
282
  end
260
283
  rescue Aws::Errors::ServiceError => e
261
284
  @errors << e
@@ -426,6 +449,22 @@ module Convection
426
449
  }
427
450
  end
428
451
  end
452
+
453
+ def run_task(phase, task, &block)
454
+ phase = phase.to_s.split.join(' ')
455
+ block.call(Model::Event.new(TASK_IN_PROGRESS, "Task (#{phase}) #{task} in progress for stack #{name}.", :info)) if block
456
+
457
+ task.call(self)
458
+ return task.success? unless block
459
+
460
+ if task.success?
461
+ block.call(Model::Event.new(TASK_COMPLETE, "Task (#{phase}) #{task} successfully completed for stack #{name}.", :info))
462
+ true
463
+ else
464
+ block.call(Model::Event.new(TASK_FAILED, "Task (#{phase}) #{task} failed to complete for stack #{name}.", :error))
465
+ false
466
+ end
467
+ end
429
468
  end
430
469
  end
431
470
  end
@@ -14,11 +14,12 @@ module Convection
14
14
  attr_accessor :level
15
15
  attr_accessor :timestamp
16
16
  colorize :level,
17
- :green => [:info, :success, Control::Stack::CREATE_COMPLETE, Control::Stack::UPDATE_COMPLETE, Control::Stack::UPDATE_ROLLBACK_COMPLETE],
17
+ :green => [:info, :success, Control::Stack::CREATE_COMPLETE, Control::Stack::UPDATE_COMPLETE, Control::Stack::UPDATE_ROLLBACK_COMPLETE,
18
+ Control::Stack::TASK_COMPLETE],
18
19
  :red => [:error, :fail, Control::Stack::CREATE_FAILED, Control::Stack::ROLLBACK_FAILED,
19
20
  Control::Stack::DELETE_FAILED, Control::Stack::UPDATE_FAILED,
20
21
  Control::Stack::UPDATE_ROLLBACK_IN_PROGRESS, Control::Stack::UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS,
21
- Control::Stack::UPDATE_ROLLBACK_FAILED],
22
+ Control::Stack::UPDATE_ROLLBACK_FAILED, Control::Stack::TASK_FAILED],
22
23
  :default => :yellow
23
24
 
24
25
  class << self
@@ -82,6 +82,15 @@ module Convection
82
82
  o.instance_exec(&block) if block
83
83
  outputs[name] = o
84
84
  end
85
+
86
+ # @param name [String] the name of the new metadata configuration to set
87
+ # @param value [Hash] an arbritrary JSON object to set as the
88
+ # value of the new metadata configuration
89
+ def metadata(name = nil, value = nil)
90
+ return @metadata unless name
91
+
92
+ @metadata[name] = Model::Template::Metadata.new(name, value)
93
+ end
85
94
  end
86
95
  end
87
96
 
@@ -199,6 +208,7 @@ module Convection
199
208
  @conditions = Collection.new
200
209
  @resources = Collection.new
201
210
  @outputs = Collection.new
211
+ @metadata = Collection.new
202
212
  end
203
213
 
204
214
  def clone(stack_)
@@ -222,7 +232,8 @@ module Convection
222
232
  'Mappings' => mappings.map(&:render),
223
233
  'Conditions' => conditions.map(&:render),
224
234
  'Resources' => resources.map(&:render),
225
- 'Outputs' => outputs.map(&:render)
235
+ 'Outputs' => outputs.map(&:render),
236
+ 'Metadata' => metadata.map(&:render)
226
237
  }
227
238
  end
228
239
 
@@ -343,3 +354,4 @@ require_relative 'template/resource'
343
354
  require_relative 'template/resource_property'
344
355
  require_relative 'template/resource_attribute'
345
356
  require_relative 'template/output'
357
+ require_relative 'template/metadata'
@@ -0,0 +1,22 @@
1
+ module Convection
2
+ module Model
3
+ class Template
4
+ ##
5
+ # Metadata Attribute
6
+ ##
7
+ class Metadata
8
+ attr_accessor :name
9
+ attr_accessor :value
10
+
11
+ def initialize(name, value)
12
+ @name = name
13
+ @value = value
14
+ end
15
+
16
+ def render
17
+ value
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -45,6 +45,7 @@ module Convection
45
45
  type 'AWS::EC2::Subnet'
46
46
  property :availability_zone, 'AvailabilityZone'
47
47
  property :vpc, 'VpcId'
48
+ property :public_ips, 'MapPublicIpOnLaunch'
48
49
  cidr_property :network, 'CidrBlock'
49
50
 
50
51
  def render(*args)
@@ -11,6 +11,10 @@ module Convection
11
11
  type 'AWS::Events::Rule'
12
12
  property :description, 'Description'
13
13
  property :domain, 'Domain'
14
+ # Event patterns are documented as the type "JSON Object".
15
+ # We can define it here as a Hash. Example usage of the
16
+ # `event_pattern` method property being used can be found in
17
+ # the EventsRule spec.
14
18
  property :event_pattern, 'EventPattern', :type => :hash
15
19
  property :name, 'Name'
16
20
  property :role_arn, 'RoleArn'
@@ -0,0 +1,10 @@
1
+ RSpec.shared_context 'with a mock CloudFormation client' do
2
+ let(:cf_client) do
3
+ client = double(:cf_client, create_stack: nil, delete_stack: nil, update_stack: nil)
4
+ allow(client).to receive(:describe_stacks) {
5
+ fail Aws::CloudFormation::Errors::ValidationError.new(nil, 'Stack does not exist.')
6
+ }
7
+
8
+ client
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ RSpec.shared_context 'with a CollectAvailabilityZonesTask defined' do
2
+ class CollectAvailabilityZonesTask
3
+ attr_writer :availability_zones
4
+
5
+ def availability_zones
6
+ @availability_zones ||= []
7
+ end
8
+
9
+ def call(stack)
10
+ self.availability_zones += stack.availability_zones
11
+ end
12
+
13
+ def success?
14
+ availability_zones.any?
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ module Convection::Control
4
+ describe Stack do
5
+ let(:template) do
6
+ Convection.template do
7
+ description 'EC2 VPC Test Template'
8
+
9
+ ec2_vpc 'TargetVPC' do
10
+ network '10.0.0.0'
11
+ subnet_length 24
12
+ enable_dns
13
+ end
14
+ end
15
+ end
16
+
17
+ describe 'after create tasks' do
18
+ include_context 'with a CollectAvailabilityZonesTask defined'
19
+ include_context 'with a mock CloudFormation client'
20
+ include_context 'with a mock EC2 client'
21
+
22
+ before do
23
+ allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf_client)
24
+ allow(Aws::EC2::Client).to receive(:new).and_return(ec2_client)
25
+ end
26
+ let(:task) { CollectAvailabilityZonesTask.new }
27
+ subject do
28
+ scope = self
29
+ Convection::Control::Stack.new('EC2 VPC Test Stack', template) do
30
+ after_create_task scope.task
31
+ end
32
+ end
33
+
34
+ it 'is registered after Stack#apply is called' do
35
+ expect(subject.tasks[:after_create]).to_not be_empty
36
+ end
37
+
38
+ it 'is executed on Stack#apply' do
39
+ subject.apply
40
+
41
+ expect(task.availability_zones).to include('eu-central-1')
42
+ end
43
+
44
+ it 'is deregistered after Stack#apply is called' do
45
+ subject.apply
46
+
47
+ expect(subject.tasks[:after_create]).to be_empty
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ module Convection::Control
4
+ describe Stack do
5
+ let(:template) do
6
+ Convection.template do
7
+ description 'EC2 VPC Test Template'
8
+
9
+ ec2_vpc 'TargetVPC' do
10
+ network '10.0.0.0'
11
+ subnet_length 24
12
+ enable_dns
13
+ end
14
+ end
15
+ end
16
+
17
+ describe 'after delete tasks' do
18
+ include_context 'with a CollectAvailabilityZonesTask defined'
19
+ include_context 'with a mock CloudFormation client'
20
+ include_context 'with a mock EC2 client'
21
+
22
+ before do
23
+ allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf_client)
24
+ allow(Aws::EC2::Client).to receive(:new).and_return(ec2_client)
25
+ end
26
+ let(:task) { CollectAvailabilityZonesTask.new }
27
+ subject do
28
+ scope = self
29
+ Convection::Control::Stack.new('EC2 VPC Test Stack', template) do
30
+ after_delete_task scope.task
31
+ end
32
+ end
33
+
34
+ it 'is registered before Stack#delete is called' do
35
+ expect(subject.tasks[:after_delete]).to_not be_empty
36
+ end
37
+
38
+ it 'is executed on Stack#delete' do
39
+ subject.delete
40
+
41
+ expect(task.availability_zones).to include('eu-central-1')
42
+ end
43
+
44
+ it 'is deregistered after Stack#delete is called' do
45
+ subject.delete
46
+
47
+ expect(subject.tasks[:after_delete]).to be_empty
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ module Convection::Control
4
+ describe Stack do
5
+ let(:template) do
6
+ Convection.template do
7
+ description 'EC2 VPC Test Template'
8
+
9
+ ec2_vpc 'TargetVPC' do
10
+ network '10.0.0.0'
11
+ subnet_length 24
12
+ enable_dns
13
+ end
14
+ end
15
+ end
16
+
17
+ describe 'after update tasks' do
18
+ include_context 'with a CollectAvailabilityZonesTask defined'
19
+ include_context 'with a mock CloudFormation client'
20
+ include_context 'with a mock EC2 client'
21
+
22
+ before do
23
+ allow(Aws::CloudFormation::Client).to receive(:new).and_return(cf_client)
24
+ allow(Aws::EC2::Client).to receive(:new).and_return(ec2_client)
25
+ end
26
+ let(:task) { CollectAvailabilityZonesTask.new }
27
+ subject do
28
+ scope = self
29
+ stack = Convection::Control::Stack.new('EC2 VPC Test Stack', template) do
30
+ after_update_task scope.task
31
+ end
32
+ allow(stack).to receive(:exist).and_return(true)
33
+ allow(stack).to receive(:exist?).and_return(true)
34
+ stack
35
+ end
36
+
37
+ it 'is registered after Stack#apply is called' do
38
+ expect(subject.tasks[:after_update]).to_not be_empty
39
+ end
40
+
41
+ it 'is executed on Stack#apply' do
42
+ subject.apply
43
+
44
+ expect(task.availability_zones).to include('eu-central-1')
45
+ end
46
+
47
+ it 'is deregistered after Stack#apply is called' do
48
+ subject.apply
49
+
50
+ expect(subject.tasks[:after_update]).to be_empty
51
+ end
52
+ end
53
+ end
54
+ end