convection 0.2.32 → 0.2.33

Sign up to get free protection for your applications and to get access to all the features.
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