convection 0.4.3 → 1.0.0.pre.beta.1
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/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
- data/.github/ISSUE_TEMPLATE.md +23 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +18 -0
- data/{LICENSE → LICENSE.md} +1 -0
- data/README.md +6 -30
- data/bin/convection +172 -45
- data/docs/getting-started.md +2 -2
- data/docs/index.md +6 -30
- data/lib/convection/control/cloud.rb +41 -1
- data/lib/convection/control/stack.rb +22 -2
- data/lib/convection/dsl/intrinsic_functions.rb +6 -0
- data/lib/convection/model/attributes.rb +15 -0
- data/lib/convection/model/cloudfile.rb +18 -0
- data/lib/convection/model/template.rb +16 -0
- data/lib/convection/model/template/output.rb +2 -0
- data/lib/convection/model/template/resource/aws_auto_scaling_auto_scaling_group.rb +24 -2
- data/lib/convection/model/template/resource_property/aws_cloudfront_defaultcachebehavior.rb +2 -0
- data/spec/convection/model/attributes_spec.rb +39 -0
- data/spec/convection/model/template/condition_spec.rb +1 -2
- data/spec/convection/model/template/resource/aws_auto_scaling_auto_scaling_group_spec.rb +31 -0
- data/spec/convection/model/template/template_spec.rb +30 -0
- data/spec/convection/model/template/validate_bytesize_spec.rb +5 -5
- metadata +13 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ad55266b8d4a5fd1a16d8b8c396a3c2ecc22b27
|
4
|
+
data.tar.gz: 4c5ae734856b196c13f39fc42fd85fc69745a6bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 018341e258e456483c9f5ebde1116cd8a71196fb972b865c62f08f0f3b87dca8a0f010e6b9e9d995c36b39240b9cdc54b0dbb7abd9f58dd252951eed766c9447
|
7
|
+
data.tar.gz: 8a9a17d9ee215c6b6c1c26340e99bae7a13518f7a24d62362d02aa3302916a7f8cea93b07840c003cfa8f4859c65ffdf4af4b6496b256fb1101a2de42943cf96
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<!-- Remove any sections that do not apply to your problems. HTML comments are ignored. -->
|
2
|
+
# Description
|
3
|
+
<!-- Describe your problem here. -->
|
4
|
+
<!-- Include references to relevant pull requests/commits here. -->
|
5
|
+
|
6
|
+
## Expected behavior
|
7
|
+
<!-- Describe your expected behavior here. -->
|
8
|
+
|
9
|
+
## Observed behavior
|
10
|
+
<!-- Describe your observed behavior here. -->
|
11
|
+
|
12
|
+
## Steps to Reproduce
|
13
|
+
<!-- Describe the steps to reproduce the observed behavior. -->
|
14
|
+
<!-- Have a failing spec for this? :) -->
|
15
|
+
|
16
|
+
# Stack Trace or Log Messages
|
17
|
+
<!-- Use the following format for showing stack traces: -->
|
18
|
+
<details>
|
19
|
+
<summary>Stack trace for `brief error context`</summary>
|
20
|
+
<pre>
|
21
|
+
... Full error context (collapses by default) ...
|
22
|
+
</pre>
|
23
|
+
</details>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<!-- Remove any sections that do not apply to your changes. HTML comments are ignored. -->
|
2
|
+
# Description
|
3
|
+
<!-- Describe your changes here. -->
|
4
|
+
<!-- Include references to relevant pull requests/commits here. -->
|
5
|
+
<!-- * Did you update documentation? -->
|
6
|
+
<!-- * Did you update test coverage? -->
|
7
|
+
|
8
|
+
# Motivation and Context
|
9
|
+
<!-- Describe the use case that requires your changes. -->
|
10
|
+
|
11
|
+
# Usage Examples
|
12
|
+
<!-- Code or screenshot examples of using your feature, if applicable. -->
|
13
|
+
|
14
|
+
# Testing Steps
|
15
|
+
<!-- List any steps required to test your changes. -->
|
16
|
+
|
17
|
+
# Post-merge Steps
|
18
|
+
<!-- List any steps required after merging your changes. -->
|
data/{LICENSE → LICENSE.md}
RENAMED
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# Convection [](https://travis-ci.org/rapid7/convection)
|
1
|
+
# Convection [](https://travis-ci.org/rapid7/convection)
|
2
2
|
_A fully generic, modular DSL for AWS CloudFormation_
|
3
3
|
|
4
4
|
This gem aims to provide a reusable model for AWS CloudFormation in Ruby. It exposes a DSL for template definition, and a simple, decoupled abstraction of a CloudFormation Stack to compile and apply templates.
|
5
5
|
|
6
6
|
## Contributing
|
7
|
-
Please read our [Contributing guidelines](CONTRIBUTING.md) for more information on contributing to Convection.
|
7
|
+
Please read our [Contributing guidelines](.github/CONTRIBUTING.md) for more information on contributing to Convection.
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
Add this line to your application's Gemfile:
|
@@ -23,9 +23,10 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
##CLI Commands
|
25
25
|
###### Converging
|
26
|
-
- To converge all stacks in your cloudfile run `convection converge
|
26
|
+
- To converge all stacks in your cloudfile run `convection converge` in the same directory as your cloudfile or use `--cloudfiles` and specify the path to the cloudfile. If you provide the name of your stack as a additional argument such as `convection converge my-stack-name` then all stacks above and including the stack you specified will be converged.
|
27
27
|
- To converge a stack group run `convection converge --stack_group YOUR_STACK_GROUP_NAME`
|
28
28
|
- To converge a specific stack or a list of stacks run `convection converge --stacks stackA stackB ...`
|
29
|
+
- To converge multiple cloudfiles at the same time run use the `--cloudfiles` option providing the path to the cloudfiles. Example `bundle exec convection converge --cloudfiles us-east-1/Cloudfile eu-central-1/Cloudfile`
|
29
30
|
|
30
31
|
###### Diff
|
31
32
|
- To display a diff between your local changes and the version of your stack in cloud formation of your changes run `convection diff`.
|
@@ -36,7 +37,7 @@ Or install it yourself as:
|
|
36
37
|
- To print out a list of available cli options with their descriptions run `convection help`.
|
37
38
|
|
38
39
|
###### Print
|
39
|
-
- To print out the cloud formation template for a specific stack run `convection print my-stack-name`.
|
40
|
+
- To print out the cloud formation template for a specific stack run `convection print-template my-stack-name`.
|
40
41
|
|
41
42
|
###### Validate
|
42
43
|
- To validate your stack is not missing a required resource run `convection validate my-stack-name`.
|
@@ -45,29 +46,4 @@ Or install it yourself as:
|
|
45
46
|
We highly recommend consulting the [getting started guide](./docs/getting-started.md) for a in depth walk through on how to to set up your project and create and deploy a stack. Example stacks and resources are available in the [convection/example](https://github.com/rapid7/convection/tree/master/example) folder
|
46
47
|
|
47
48
|
## License
|
48
|
-
|
49
|
-
|
50
|
-
```
|
51
|
-
MIT License
|
52
|
-
===========
|
53
|
-
|
54
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
55
|
-
a copy of this software and associated documentation files (the
|
56
|
-
"Software"), to deal in the Software without restriction, including
|
57
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
58
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
59
|
-
permit persons to whom the Software is furnished to do so, subject to
|
60
|
-
the following conditions:
|
61
|
-
|
62
|
-
The above copyright notice and this permission notice shall be
|
63
|
-
included in all copies or substantial portions of the Software.
|
64
|
-
|
65
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
66
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
67
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
68
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
69
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
70
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
71
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
72
|
-
|
73
|
-
```
|
49
|
+
Convection is distributed under the MIT license - please refer to the [LICENSE](LICENSE.md) for more information.
|
data/bin/convection
CHANGED
@@ -1,47 +1,42 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'thor'
|
3
3
|
require_relative '../lib/convection/control/cloud'
|
4
|
-
|
4
|
+
require 'thread'
|
5
5
|
module Convection
|
6
6
|
##
|
7
7
|
# Convection CLI
|
8
8
|
##
|
9
9
|
class CLI < Thor
|
10
|
-
class_option :cloudfile, :type => :string, :default => 'Cloudfile'
|
11
10
|
def initialize(*args)
|
12
11
|
super
|
13
|
-
@cloud = Control::Cloud.new
|
14
12
|
@cwd = Dir.getwd
|
13
|
+
@errors = false
|
15
14
|
end
|
16
15
|
|
17
16
|
desc 'converge STACK', 'Converge your cloud'
|
18
17
|
option :stack_group, :type => :string, :desc => 'The name of a stack group defined in your cloudfile to converge'
|
19
18
|
option :stacks, :type => :array, :desc => 'A ordered space separated list of stacks to converge'
|
19
|
+
option :verbose, :type => :boolean, :aliases => '--v', :desc => 'Show stack progress', default: true
|
20
|
+
option :'very-verbose', :type => :boolean, :aliases => '--vv', :desc => 'Show unchanged stacks', default: true
|
21
|
+
option :cloudfiles, :type => :array, :default => %w(Cloudfile)
|
22
|
+
option :delayed_output, :type => :boolean, :desc => 'Delay output until operation completion.', :default => false
|
20
23
|
def converge(stack = nil)
|
21
|
-
@
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
say "* #{ error.message }"
|
26
|
-
error.backtrace.each { |b| say " #{ b }" }
|
27
|
-
end unless errors.nil?
|
28
|
-
end
|
24
|
+
@outputs = []
|
25
|
+
operation('converge', stack)
|
26
|
+
print_outputs(@outputs) if @outputs && @outputs.any?
|
27
|
+
exit 1 if @errors
|
29
28
|
end
|
30
29
|
|
31
30
|
desc 'delete STACK', 'Delete stack(s) from your cloud'
|
32
31
|
option :stack_group, :type => :string, :desc => 'The name of a stack group defined in your cloudfile to delete'
|
33
32
|
option :stacks, :type => :array, :desc => 'A ordered space separated list of stacks to delete'
|
33
|
+
option :cloudfile, :type => :string, :default => 'Cloudfile'
|
34
|
+
option :verbose, :type => :boolean, :aliases => '--v', :desc => 'Show stack progress', default: true
|
35
|
+
option :'very-verbose', :type => :boolean, :aliases => '--vv', :desc => 'Show unchanged stacks', default: true
|
34
36
|
def delete(stack = nil)
|
35
|
-
|
36
|
-
print_status = lambda do |event, *errors|
|
37
|
-
say_status(*event.to_thor)
|
38
|
-
errors.flatten.each do |error|
|
39
|
-
say "* #{ error.message }"
|
40
|
-
error.backtrace.each { |b| say " #{ b }" }
|
41
|
-
end unless errors.nil?
|
42
|
-
end
|
37
|
+
init_cloud
|
43
38
|
|
44
|
-
stacks = @cloud.stacks_until(stack, options, &
|
39
|
+
stacks = @cloud.stacks_until(stack, options, &method(:emit_events))
|
45
40
|
if stacks.empty?
|
46
41
|
say_status(:delete_failed, 'No stacks found matching the provided input (STACK, --stack-group, and/or --stacks).', :red)
|
47
42
|
return
|
@@ -50,51 +45,183 @@ module Convection
|
|
50
45
|
|
51
46
|
confirmation = ask('Are you sure you want to delete the above stack(s)?', limited_to: %w(yes no))
|
52
47
|
if confirmation.eql?('yes')
|
53
|
-
@cloud.delete(stacks, &
|
48
|
+
@cloud.delete(stacks, &method(:emit_events))
|
54
49
|
else
|
55
50
|
say_status(:delete_aborted, 'Aborted deletion of the above stack(s).', :green)
|
56
51
|
end
|
57
52
|
end
|
58
53
|
|
59
54
|
desc 'diff STACK', 'Show changes that will be applied by converge'
|
60
|
-
option :verbose, :type => :boolean, :aliases => '--v', :desc => 'Show stack progress'
|
61
|
-
option :'very-verbose', :type => :boolean, :aliases => '--vv', :desc => 'Show unchanged stacks'
|
62
55
|
option :stack_group, :type => :string, :desc => 'The name of a stack group defined in your cloudfile to diff'
|
63
56
|
option :stacks, :type => :array, :desc => 'A ordered space separated list of stacks to diff'
|
57
|
+
option :verbose, :type => :boolean, :aliases => '--v', :desc => 'Show stack progress'
|
58
|
+
option :'very-verbose', :type => :boolean, :aliases => '--vv', :desc => 'Show unchanged stacks'
|
59
|
+
option :cloudfiles, :type => :array, :default => %w(Cloudfile)
|
60
|
+
option :delayed_output, :type => :boolean, :desc => 'Delay output until operation completion.', :default => false
|
64
61
|
def diff(stack = nil)
|
62
|
+
@outputs = []
|
63
|
+
operation('diff', stack)
|
64
|
+
print_outputs(@outputs) if @outputs && @outputs.any?
|
65
|
+
exit 1 if @errors
|
66
|
+
end
|
67
|
+
|
68
|
+
desc 'print_template STACK', 'Print the rendered template for STACK'
|
69
|
+
option :cloudfile, :type => :string, :default => 'Cloudfile'
|
70
|
+
def print_template(stack)
|
71
|
+
init_cloud
|
72
|
+
puts @cloud.stacks[stack].to_json(true)
|
73
|
+
end
|
74
|
+
|
75
|
+
desc 'describe-tasks [--stacks STACKS]', 'Describe tasks for a given stack'
|
76
|
+
option :stacks, :type => :array, :desc => 'A ordered space separated list of stacks to diff', default: []
|
77
|
+
def describe_tasks
|
78
|
+
@cloud = Control::Cloud.new
|
65
79
|
@cloud.configure(File.absolute_path(options['cloudfile'], @cwd))
|
66
|
-
last_event = nil
|
67
80
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
81
|
+
describe_stack_tasks(options[:stacks])
|
82
|
+
end
|
83
|
+
|
84
|
+
desc 'run-tasks [--stack STACK]', 'Run tasks for a given stack'
|
85
|
+
option :stack, :desc => 'The stack to run tasks for', :required => true
|
86
|
+
def run_tasks
|
87
|
+
@cloud = Control::Cloud.new
|
88
|
+
@cloud.configure(File.absolute_path(options['cloudfile'], @cwd))
|
89
|
+
|
90
|
+
run_stack_tasks(options[:stack])
|
91
|
+
end
|
92
|
+
|
93
|
+
desc 'validate STACK', 'Validate the rendered template for STACK'
|
94
|
+
option :cloudfile, :type => :string, :default => 'Cloudfile'
|
95
|
+
def validate(stack)
|
96
|
+
init_cloud
|
97
|
+
@cloud.stacks[stack].validate
|
98
|
+
end
|
99
|
+
|
100
|
+
no_commands do
|
101
|
+
attr_accessor :last_event
|
102
|
+
|
103
|
+
private
|
104
|
+
|
105
|
+
def operation(task_name, stack)
|
106
|
+
work_q = Queue.new
|
107
|
+
semaphore = Mutex.new
|
108
|
+
unless options[:delayed_output]
|
109
|
+
puts 'For easier reading when using multiple cloudfiles output can be delayed until task completion.'
|
110
|
+
puts 'If you would like delayed output please use the "--delayed_output true" option.'
|
111
|
+
end
|
112
|
+
options[:cloudfiles].each { |cloudfile| work_q.push(cloud: Control::Cloud.new, cloudfile_path: cloudfile) }
|
113
|
+
workers = (0...options[:cloudfiles].length).map do
|
114
|
+
Thread.new do
|
115
|
+
until work_q.empty?
|
116
|
+
output = []
|
117
|
+
cloud_array = work_q.pop(true)
|
118
|
+
cloud_array[:cloud].configure(File.absolute_path(cloud_array[:cloudfile_path], @cwd))
|
119
|
+
cloud = cloud_array[:cloud]
|
120
|
+
region = cloud.cloudfile.region
|
121
|
+
cloud.send(task_name, stack, stack_group: options[:stack_group], stacks: options[:stacks]) do |event, errors|
|
122
|
+
if options[:cloudfiles].length > 1 && options[:delayed_output]
|
123
|
+
output << { event: event, errors: errors }
|
124
|
+
else
|
125
|
+
emit_events(event, *errors, region: region)
|
126
|
+
end
|
127
|
+
semaphore.synchronize { @errors = errors.any? if errors }
|
128
|
+
end
|
129
|
+
if options[:cloudfiles].length > 1 && options[:delayed_output]
|
130
|
+
semaphore.synchronize { @outputs << { cloud_name: cloud.cloudfile.name, region: region, logging: output } }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
workers.each(&:join)
|
136
|
+
end
|
137
|
+
|
138
|
+
def describe_stack_tasks(stacks_to_include)
|
139
|
+
@cloud.stacks.map do |stack_name, stack|
|
140
|
+
next if stacks_to_include.any? && !stacks_to_include.include?(stack_name)
|
141
|
+
tasks = stack.tasks.values.flatten.uniq
|
142
|
+
next if tasks.empty?
|
143
|
+
|
144
|
+
puts "Stack #{stack_name} (#{stack.cloud_name}) includes the following tasks:"
|
145
|
+
tasks.each_with_index do |task, index|
|
146
|
+
puts " #{index}. #{task}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def run_stack_tasks(stack_name)
|
152
|
+
stack = @cloud.stacks[stack_name]
|
153
|
+
tasks = stack.tasks.values.flatten.uniq
|
154
|
+
if !stack
|
155
|
+
say_status(:task_failed, 'No stacks found matching the provided input (--stack).', :red)
|
156
|
+
exit 1
|
157
|
+
elsif tasks.empty?
|
158
|
+
say_status(:task_failed, "No tasks defined for the stack #{stack_name}. Define them in your Cloudfile.", :red)
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
|
162
|
+
puts "The following tasks are available to execute for the stack #{stack_name} (#{stack.cloud_name}):"
|
163
|
+
tasks.each_with_index do |task, index|
|
164
|
+
puts " #{index}. #{task}"
|
165
|
+
end
|
166
|
+
choices = 0.upto(tasks.length - 1).map(&:to_s)
|
167
|
+
choice = ask('Which stack task would you like to execute? (ctrl-c to exit)', limited_to: choices)
|
168
|
+
task = tasks[choice.to_i]
|
169
|
+
|
170
|
+
say_status(:task_in_progress, "Task #{task} in progress for stack #{stack_name}.", :yellow)
|
171
|
+
task.call(stack)
|
172
|
+
|
173
|
+
if task.success?
|
174
|
+
say_status(:task_complete, "Task #{task} successfully completed for stack #{stack_name}.", :green)
|
175
|
+
else
|
176
|
+
say_status(:task_failed, "Task #{task} failed to complete for stack #{stack_name}.", :red)
|
177
|
+
exit 1
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def emit_events(event, *errors, region: nil)
|
182
|
+
if event.is_a? Model::Event
|
183
|
+
if options[:'very-verbose'] || event.name == :error
|
184
|
+
print_info(event, region: region)
|
72
185
|
elsif options[:verbose]
|
73
|
-
|
186
|
+
print_info(event, region: region) if event.name == :compare
|
74
187
|
end
|
75
|
-
last_event =
|
76
|
-
elsif
|
188
|
+
@last_event = event
|
189
|
+
elsif event.is_a? Model::Diff
|
77
190
|
if !options[:'very-verbose'] && !options[:verbose]
|
78
|
-
|
79
|
-
last_event = nil
|
191
|
+
print_info(last_event, region: region) unless last_event.nil?
|
192
|
+
@last_event = nil
|
80
193
|
end
|
81
|
-
|
194
|
+
print_info(event, region: region)
|
82
195
|
else
|
83
|
-
|
196
|
+
print_info(event, region: region)
|
197
|
+
end
|
198
|
+
|
199
|
+
errors.each do |error|
|
200
|
+
say "* #{ error.message }"
|
201
|
+
error.backtrace.each { |trace| say " #{ trace }" }
|
84
202
|
end
|
85
203
|
end
|
86
|
-
end
|
87
204
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
205
|
+
def print_info(say, region: nil)
|
206
|
+
print "#{region} " if region
|
207
|
+
say_status(*say.to_thor)
|
208
|
+
end
|
93
209
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
210
|
+
def print_outputs(outputs)
|
211
|
+
outputs.each do |output|
|
212
|
+
puts '********'
|
213
|
+
puts "Cloud name: #{output[:cloud_name]}. Region: #{output[:region]}."
|
214
|
+
puts '********'
|
215
|
+
output[:logging].each do |hash|
|
216
|
+
emit_events(hash[:event], *hash[:errors])
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def init_cloud
|
222
|
+
@cloud = Control::Cloud.new
|
223
|
+
@cloud.configure(File.absolute_path(options['cloudfile'], @cwd))
|
224
|
+
end
|
98
225
|
end
|
99
226
|
end
|
100
227
|
end
|
data/docs/getting-started.md
CHANGED
@@ -202,11 +202,11 @@ compare Compare local state of stack vpc (convection-demo-vpc) with remote temp
|
|
202
202
|
create .Resources.DemoVPC.Properties.Tags.0.Key: Name
|
203
203
|
create .Resources.DemoVPC.Properties.Tags.0.Value: convection-demo-vpc
|
204
204
|
```
|
205
|
-
To see what the cloud formation template for your vpc template would look like you can run `convection
|
205
|
+
To see what the cloud formation template for your vpc template would look like you can run `convection print_template vpc`.
|
206
206
|
This can help you verify that values referenced under the `stack` namespace are set correctly.
|
207
207
|
|
208
208
|
```json
|
209
|
-
$> convection print vpc
|
209
|
+
$> convection print-template vpc
|
210
210
|
{
|
211
211
|
"AWSTemplateFormatVersion": "2010-09-09",
|
212
212
|
"Description": "VPC with Public and Private Subnets (NAT)",
|
data/docs/index.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# Convection [](https://travis-ci.org/rapid7/convection)
|
1
|
+
# Convection [](https://travis-ci.org/rapid7/convection)
|
2
2
|
_A fully generic, modular DSL for AWS CloudFormation_
|
3
3
|
|
4
4
|
This gem aims to provide a reusable model for AWS CloudFormation in Ruby. It exposes a DSL for template definition, and a simple, decoupled abstraction of a CloudFormation Stack to compile and apply templates.
|
5
5
|
|
6
6
|
## Contributing
|
7
|
-
Please read our [Contributing guidelines](CONTRIBUTING.md) for more information on contributing to Convection.
|
7
|
+
Please read our [Contributing guidelines](.github/CONTRIBUTING.md) for more information on contributing to Convection.
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
Add this line to your application's Gemfile:
|
@@ -23,9 +23,10 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
##CLI Commands
|
25
25
|
###### Converging
|
26
|
-
- To converge all stacks in your cloudfile run `convection converge
|
26
|
+
- To converge all stacks in your cloudfile run `convection converge` in the same directory as your cloudfile or use `--cloudfiles` and specify the path to the cloudfile. If you provide the name of your stack as a additional argument such as `convection converge my-stack-name` then all stacks above and including the stack you specified will be converged.
|
27
27
|
- To converge a stack group run `convection converge --stack_group YOUR_STACK_GROUP_NAME`
|
28
28
|
- To converge a specific stack or a list of stacks run `convection converge --stacks stackA stackB ...`
|
29
|
+
- To converge multiple cloudfiles at the same time run use the `--cloudfiles` option providing the path to the cloudfiles. Example `bundle exec convection converge --cloudfiles us-east-1/Cloudfile eu-central-1/Cloudfile`
|
29
30
|
|
30
31
|
###### Diff
|
31
32
|
- To display a diff between your local changes and the version of your stack in cloud formation of your changes run `convection diff`.
|
@@ -36,7 +37,7 @@ Or install it yourself as:
|
|
36
37
|
- To print out a list of available cli options with their descriptions run `convection help`.
|
37
38
|
|
38
39
|
###### Print
|
39
|
-
- To print out the cloud formation template for a specific stack run `convection print my-stack-name`.
|
40
|
+
- To print out the cloud formation template for a specific stack run `convection print-template my-stack-name`.
|
40
41
|
|
41
42
|
###### Validate
|
42
43
|
- To validate your stack is not missing a required resource run `convection validate my-stack-name`.
|
@@ -45,29 +46,4 @@ Or install it yourself as:
|
|
45
46
|
We highly recommend consulting the [getting started guide](./docs/getting-started.md) for a in depth walk through on how to to set up your project and create and deploy a stack. Example stacks and resources are available in the [convection/example](https://github.com/rapid7/convection/tree/master/example) folder
|
46
47
|
|
47
48
|
## License
|
48
|
-
|
49
|
-
|
50
|
-
```
|
51
|
-
MIT License
|
52
|
-
===========
|
53
|
-
|
54
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
55
|
-
a copy of this software and associated documentation files (the
|
56
|
-
"Software"), to deal in the Software without restriction, including
|
57
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
58
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
59
|
-
permit persons to whom the Software is furnished to do so, subject to
|
60
|
-
the following conditions:
|
61
|
-
|
62
|
-
The above copyright notice and this permission notice shall be
|
63
|
-
included in all copies or substantial portions of the Software.
|
64
|
-
|
65
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
66
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
67
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
68
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
69
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
70
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
71
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
72
|
-
|
73
|
-
```
|
49
|
+
Convection is distributed under the MIT license - please refer to the [LICENSE](LICENSE.md) for more information.
|
@@ -7,6 +7,7 @@ module Convection
|
|
7
7
|
# Control tour Clouds
|
8
8
|
##
|
9
9
|
class Cloud
|
10
|
+
attr_accessor :cloudfile
|
10
11
|
def configure(cloudfile)
|
11
12
|
@cloudfile = Model::Cloudfile.new(cloudfile)
|
12
13
|
end
|
@@ -69,10 +70,13 @@ module Convection
|
|
69
70
|
return
|
70
71
|
end
|
71
72
|
|
73
|
+
exit 1 if stack_initialization_errors?(&block)
|
74
|
+
|
72
75
|
filter_deck(options, &block).each_value do |stack|
|
73
76
|
block.call(Model::Event.new(:converge, "Stack #{ stack.name }", :info)) if block
|
74
77
|
stack.apply(&block)
|
75
78
|
|
79
|
+
emit_credential_error_and_exit!(stack, &block) if stack.credential_error?
|
76
80
|
if stack.error?
|
77
81
|
block.call(Model::Event.new(:error, "Error converging stack #{ stack.name }", :error), stack.errors) if block
|
78
82
|
break
|
@@ -88,6 +92,8 @@ module Convection
|
|
88
92
|
end
|
89
93
|
|
90
94
|
def delete(stacks, &block)
|
95
|
+
exit 1 if stack_initialization_errors?(&block)
|
96
|
+
|
91
97
|
stacks.each do |stack|
|
92
98
|
unless stack.exist?
|
93
99
|
block.call(Model::Event.new(:delete_skipped, "Stack #{ stack.cloud_name } does not exist remotely", :warn))
|
@@ -114,10 +120,21 @@ module Convection
|
|
114
120
|
return
|
115
121
|
end
|
116
122
|
|
123
|
+
exit 1 if stack_initialization_errors?(&block)
|
124
|
+
|
117
125
|
filter_deck(options, &block).each_value do |stack|
|
118
126
|
block.call(Model::Event.new(:compare, "Compare local state of stack #{ stack.name } (#{ stack.cloud_name }) with remote template", :info))
|
119
127
|
|
120
128
|
difference = stack.diff
|
129
|
+
# Find errors during diff
|
130
|
+
emit_credential_error_and_exit!(stack, &block) if stack.credential_error?
|
131
|
+
if stack.error?
|
132
|
+
errors = stack.errors.collect { |x| x.exception.message }
|
133
|
+
errors = errors.uniq.flatten
|
134
|
+
block.call(Model::Event.new(:error, "Error diffing stack #{ stack.name} Error(s): #{errors.join(', ')}", :error), stack.errors) if block
|
135
|
+
break
|
136
|
+
end
|
137
|
+
|
121
138
|
if difference.empty?
|
122
139
|
difference << Model::Event.new(:unchanged, "Stack #{ stack.cloud_name } has no changes", :info)
|
123
140
|
end
|
@@ -125,9 +142,32 @@ module Convection
|
|
125
142
|
difference.each { |diff| block.call(diff) }
|
126
143
|
|
127
144
|
break if !to_stack.nil? && stack.name == to_stack
|
128
|
-
sleep rand @cloudfile.splay || 2
|
129
145
|
end
|
130
146
|
end
|
147
|
+
|
148
|
+
def stack_initialization_errors?(&block)
|
149
|
+
errors = []
|
150
|
+
# Find errors during stack init
|
151
|
+
stacks.each_value do |stack|
|
152
|
+
if stack.error?
|
153
|
+
errors << stack.errors.collect { |x| x.exception.message }
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
unless errors.empty?
|
158
|
+
errors = errors.uniq.flatten
|
159
|
+
block.call(Model::Event.new(:error, "Error(s) during stack initialization #{errors.join(', ')}", :error), errors) if block
|
160
|
+
return true
|
161
|
+
end
|
162
|
+
false
|
163
|
+
end
|
164
|
+
|
165
|
+
def emit_credential_error_and_exit!(stack, &block)
|
166
|
+
event = Model::Event.new(:error, "Credentials expired while converging #{ stack.name }. " \
|
167
|
+
'Visit the AWS console to track progress of the stack being created/updated.', :error)
|
168
|
+
block.call(event, stack.errors) if block
|
169
|
+
exit 1
|
170
|
+
end
|
131
171
|
end
|
132
172
|
end
|
133
173
|
end
|
@@ -115,10 +115,12 @@ module Convection
|
|
115
115
|
|
116
116
|
@attributes = options.delete(:attributes) { |_| Model::Attributes.new }
|
117
117
|
@options = options
|
118
|
+
@retry_limit = options[:retry_limit] || 7
|
118
119
|
|
119
120
|
client_options = {}.tap do |opt|
|
120
121
|
opt[:region] = @region
|
121
122
|
opt[:credentials] = @credentials unless @credentials.nil?
|
123
|
+
opt[:retry_limit] = @retry_limit
|
122
124
|
end
|
123
125
|
@ec2_client = Aws::EC2::Client.new(client_options)
|
124
126
|
@cf_client = Aws::CloudFormation::Client.new(client_options)
|
@@ -143,10 +145,17 @@ module Convection
|
|
143
145
|
# in the dependency tree to avoid the chicken-and-egg problem.
|
144
146
|
@template.execute
|
145
147
|
|
146
|
-
|
148
|
+
rescue Aws::Errors::ServiceError => e
|
149
|
+
@errors << e
|
150
|
+
end
|
151
|
+
|
152
|
+
def template_status
|
147
153
|
get_status(cloud_name)
|
148
|
-
|
154
|
+
rescue Aws::Errors::ServiceError => e
|
155
|
+
@errors << e
|
156
|
+
end
|
149
157
|
|
158
|
+
def load_template_info
|
150
159
|
get_resources
|
151
160
|
get_template
|
152
161
|
resource_attributes
|
@@ -185,6 +194,11 @@ module Convection
|
|
185
194
|
@attributes.set(name, key, value)
|
186
195
|
end
|
187
196
|
|
197
|
+
# @see Convection::Model::Attributes#fetch
|
198
|
+
def fetch(*args)
|
199
|
+
@attributes.fetch(*args)
|
200
|
+
end
|
201
|
+
|
188
202
|
# @see Convection::Model::Attributes#get
|
189
203
|
def get(*args)
|
190
204
|
@attributes.get(*args)
|
@@ -209,6 +223,12 @@ module Convection
|
|
209
223
|
[CREATE_COMPLETE, ROLLBACK_COMPLETE, UPDATE_COMPLETE, UPDATE_ROLLBACK_COMPLETE].include?(status)
|
210
224
|
end
|
211
225
|
|
226
|
+
# @return [Boolean] whether a credential error occurred is the reason
|
227
|
+
# accessing the CloudFormation stack failed.
|
228
|
+
def credential_error?
|
229
|
+
error? && errors.all? { |e| e.class == Aws::EC2::Errors::RequestExpired }
|
230
|
+
end
|
231
|
+
|
212
232
|
# @return [Boolean] whether the CloudFormation Stack is in one of
|
213
233
|
# the several *_COMPLETE states.
|
214
234
|
def delete_complete?
|
@@ -19,6 +19,14 @@ module Convection
|
|
19
19
|
@parameters.include?(key) || @outputs.include?(key) || @resources.include?(key)
|
20
20
|
end
|
21
21
|
|
22
|
+
def fetch(key, default = nil)
|
23
|
+
@parameters.fetch(key.to_s) do
|
24
|
+
@outputs.fetch(key.to_s) do
|
25
|
+
@resources.fetch(key.to_s, default)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
22
30
|
def [](key)
|
23
31
|
@parameters[key.to_s] || @outputs[key.to_s] || @resources[key.to_s]
|
24
32
|
end
|
@@ -40,6 +48,13 @@ module Convection
|
|
40
48
|
@stacks.include?(stack) && @stacks[stack].include?(key)
|
41
49
|
end
|
42
50
|
|
51
|
+
def fetch(stack, key, default = nil)
|
52
|
+
return get(stack, key, default) unless default.nil?
|
53
|
+
raise KeyError, "key '#{key}' not found for stack '#{stack}'" unless include?(stack, key)
|
54
|
+
|
55
|
+
@stacks[stack.to_s].fetch(key.to_s)
|
56
|
+
end
|
57
|
+
|
43
58
|
def get(stack, key, default = nil)
|
44
59
|
include?(stack, key) ? @stacks[stack.to_s][key.to_s] : default
|
45
60
|
end
|
@@ -2,6 +2,7 @@ require_relative '../control/stack'
|
|
2
2
|
require_relative '../dsl/helpers'
|
3
3
|
require_relative '../model/attributes'
|
4
4
|
require_relative '../model/template'
|
5
|
+
require 'thread'
|
5
6
|
|
6
7
|
module Convection
|
7
8
|
module DSL
|
@@ -14,6 +15,8 @@ module Convection
|
|
14
15
|
attribute :name
|
15
16
|
attribute :region
|
16
17
|
attribute :splay
|
18
|
+
attribute :retry_limit
|
19
|
+
attribute :thread_count
|
17
20
|
|
18
21
|
## Helper to define a template in-line
|
19
22
|
def template(*args, &block)
|
@@ -31,6 +34,7 @@ module Convection
|
|
31
34
|
options[:region] ||= region
|
32
35
|
options[:cloud] = name
|
33
36
|
options[:attributes] = attributes
|
37
|
+
options[:retry_limit] = retry_limit
|
34
38
|
|
35
39
|
@stacks[stack_name] = Control::Stack.new(stack_name, template, options, &block)
|
36
40
|
@deck << @stacks[stack_name]
|
@@ -59,8 +63,22 @@ module Convection
|
|
59
63
|
@stacks = {}
|
60
64
|
@deck = []
|
61
65
|
@stack_groups = {}
|
66
|
+
@thread_count ||= 2
|
62
67
|
|
63
68
|
instance_eval(IO.read(cloudfile), cloudfile, 1)
|
69
|
+
|
70
|
+
work_q = Queue.new
|
71
|
+
@deck.each { |stack| work_q.push(stack) }
|
72
|
+
workers = (0...@thread_count).map do
|
73
|
+
Thread.new do
|
74
|
+
until work_q.empty?
|
75
|
+
stack = work_q.pop(true)
|
76
|
+
stack.template_status
|
77
|
+
stack.load_template_info if stack.exist?
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
workers.each(&:join)
|
64
82
|
end
|
65
83
|
end
|
66
84
|
end
|
@@ -17,6 +17,7 @@ module Convection
|
|
17
17
|
class << self
|
18
18
|
## Wrap private define_method
|
19
19
|
def attach_resource(name, klass)
|
20
|
+
resource_dsl_methods[name.to_s] = klass
|
20
21
|
define_method(name) do |rname, &block|
|
21
22
|
resource = klass.new(rname, self)
|
22
23
|
resource.instance_exec(&block) if block
|
@@ -26,10 +27,19 @@ module Convection
|
|
26
27
|
end
|
27
28
|
|
28
29
|
def attach_resource_collection(name, klass)
|
30
|
+
resource_collection_dsl_methods[name.to_s] = klass
|
29
31
|
define_method(name) do |rname, &block|
|
30
32
|
resource_collections[rname] = klass.new(rname, self, &block)
|
31
33
|
end
|
32
34
|
end
|
35
|
+
|
36
|
+
def resource_dsl_methods
|
37
|
+
@resource_dsl_methods ||= {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def resource_collection_dsl_methods
|
41
|
+
@resource_collection_dsl_methods ||= {}
|
42
|
+
end
|
33
43
|
end
|
34
44
|
end
|
35
45
|
|
@@ -79,6 +89,12 @@ module Convection
|
|
79
89
|
r = Model::Template::Resource.new(name, self)
|
80
90
|
|
81
91
|
r.instance_exec(&block) if block
|
92
|
+
predefined_resources = DSL::Template::Resource.resource_dsl_methods.select { |_, resource_class| resource_class.type == r.type }.keys
|
93
|
+
if predefined_resources.any?
|
94
|
+
dsl_methods = predefined_resources.map { |resource| "##{resource}" }.join(', ')
|
95
|
+
warn "WARNING: The resource type #{r.type} is already defined. " \
|
96
|
+
"You can use any of the following resource methods instead of manually constructing a resource: #{dsl_methods}"
|
97
|
+
end
|
82
98
|
resources[name] = r
|
83
99
|
end
|
84
100
|
|
@@ -15,6 +15,7 @@ module Convection
|
|
15
15
|
attribute :name
|
16
16
|
attribute :value
|
17
17
|
attribute :description
|
18
|
+
attribute :export_as
|
18
19
|
attr_reader :template
|
19
20
|
|
20
21
|
def initialize(name, parent)
|
@@ -31,6 +32,7 @@ module Convection
|
|
31
32
|
'Description' => description
|
32
33
|
}.tap do |output|
|
33
34
|
render_condition(output)
|
35
|
+
output['Export'] = { 'Name' => export_as } if export_as
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
@@ -8,8 +8,6 @@ module Convection
|
|
8
8
|
# AWS::AutoScaling::AutoScalingGroup
|
9
9
|
##
|
10
10
|
class AutoScalingGroup < Resource
|
11
|
-
include Model::Mixin::Taggable
|
12
|
-
|
13
11
|
type 'AWS::AutoScaling::AutoScalingGroup'
|
14
12
|
property :availability_zone, 'AvailabilityZones', :array
|
15
13
|
property :cooldown, 'Cooldown'
|
@@ -33,10 +31,34 @@ module Convection
|
|
33
31
|
end
|
34
32
|
end
|
35
33
|
|
34
|
+
def tag(key, value, propagate_at_launch: nil)
|
35
|
+
tags[key] = { value: value }
|
36
|
+
tags[key][:propagate_at_launch] = propagate_at_launch unless propagate_at_launch.nil?
|
37
|
+
|
38
|
+
tags[key]
|
39
|
+
end
|
40
|
+
|
41
|
+
def tags
|
42
|
+
@tags ||= {}
|
43
|
+
end
|
44
|
+
|
36
45
|
def update_policy(&block)
|
37
46
|
policy = ResourceAttribute::UpdatePolicy.new(self)
|
38
47
|
policy.instance_exec(&block) if block
|
39
48
|
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def render_tags(resource)
|
53
|
+
rendered_tags = tags.map do |key, tag|
|
54
|
+
rendered = { 'Key' => key, 'Value' => tag[:value] }
|
55
|
+
rendered['PropagateAtLaunch'] = tag[:propagate_at_launch] if tag.key?(:propagate_at_launch)
|
56
|
+
|
57
|
+
rendered
|
58
|
+
end
|
59
|
+
|
60
|
+
resource['Properties']['Tags'] = rendered_tags unless rendered_tags.empty?
|
61
|
+
end
|
40
62
|
end
|
41
63
|
end
|
42
64
|
end
|
@@ -9,6 +9,8 @@ module Convection
|
|
9
9
|
class CloudFrontDefaultCacheBehavior < ResourceProperty
|
10
10
|
property :allowed_methods, 'AllowedMethods', :type => :list, :default => %w(HEAD GET)
|
11
11
|
property :cached_methods, 'CachedMethods', :type => :list
|
12
|
+
property :compress, 'Compress'
|
13
|
+
property :default_ttl, 'DefaultTTL'
|
12
14
|
property :forwarded_values, 'ForwardedValues'
|
13
15
|
property :min_ttl, 'MinTTL'
|
14
16
|
property :smooth_streaming, 'SmoothStreaming'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Convection::Model
|
4
|
+
describe Attributes do
|
5
|
+
subject { Attributes.new }
|
6
|
+
|
7
|
+
describe '#fetch' do
|
8
|
+
it 'raises a key error if the key was not defined' do
|
9
|
+
expect { subject.fetch('<stack-name>', '<attribute-key>') }.to raise_error(KeyError)
|
10
|
+
end
|
11
|
+
|
12
|
+
['<truthy object>', true, false].each do |default|
|
13
|
+
it "supports #{default.inspect} as the default value when no value was set" do
|
14
|
+
expect { subject.fetch('<stack-name>', '<attribute-key>', default) }.to_not raise_error
|
15
|
+
end
|
16
|
+
|
17
|
+
it "supports #{default.inspect} as a default value" do
|
18
|
+
observed = subject.fetch('<stack-name>', '<attribute-key>', default)
|
19
|
+
expect(observed).to eq(default)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
['truthy object', true, false, nil].each do |value|
|
24
|
+
it "does not raise a key error when #{value.inspect} was previously set" do
|
25
|
+
subject.set('<stack-name>', '<attribute-key>', value)
|
26
|
+
|
27
|
+
expect { subject.fetch('<stack-name>', '<attribute-key>') }.to_not raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
it "supports #{value.inspect} as a return value" do
|
31
|
+
subject.set('<stack-name>', '<attribute-key>', value)
|
32
|
+
|
33
|
+
observed = subject.fetch('<stack-name>', '<attribute-key>')
|
34
|
+
expect(observed).to eq(value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class Convection::Model::Template::Resource
|
4
|
+
describe AutoScalingGroup do
|
5
|
+
subject do
|
6
|
+
parent = double(:template)
|
7
|
+
allow(parent).to receive(:template).and_return(parent)
|
8
|
+
|
9
|
+
described_class.new('MyAutoScalingGroup', parent)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should not render tags when none have been defined' do
|
13
|
+
expect(subject.render['Properties']['Tags']).to be_nil
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'renders a regular tag' do
|
17
|
+
subject.tag '<tag-name>', '<tag-value>'
|
18
|
+
expect(subject.render['Properties']['Tags']).to include(a_hash_including('Key' => '<tag-name>'))
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'renders a tag that should be propagated at launch' do
|
22
|
+
subject.tag '<tag-name>', '<tag-value>', propagate_at_launch: true
|
23
|
+
expect(subject.render['Properties']['Tags']).to include(a_hash_including('Key' => '<tag-name>', 'PropagateAtLaunch' => true))
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'renders a tag that should not be propagated at launch' do
|
27
|
+
subject.tag '<tag-name>', '<tag-value>', propagate_at_launch: false
|
28
|
+
expect(subject.render['Properties']['Tags']).to include(a_hash_including('Key' => '<tag-name>', 'PropagateAtLaunch' => false))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -19,6 +19,36 @@ class Convection::Model::Template
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
describe '#resource' do
|
23
|
+
context 'when defining a predefined resource' do
|
24
|
+
subject do
|
25
|
+
Convection.template do
|
26
|
+
resource 'FooInstance' do
|
27
|
+
type 'AWS::EC2::Instance'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'warns the user when they are using a predefined resource.' do
|
33
|
+
expect { subject.render }.to output(/.*already defined.*/).to_stderr
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when defining a undefined resource' do
|
38
|
+
subject do
|
39
|
+
Convection.template do
|
40
|
+
resource 'FooInstance' do
|
41
|
+
type 'FakeResource'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'warns the user when they are using a undefined resource.' do
|
47
|
+
expect { subject.render }.to_not output.to_stderr
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
22
52
|
subject do
|
23
53
|
template_json
|
24
54
|
end
|
@@ -21,13 +21,13 @@ class Convection::Model::Template
|
|
21
21
|
description 'Validations Test Template - Excessive Bytesize'
|
22
22
|
|
23
23
|
200.times do |count|
|
24
|
-
|
25
|
-
|
26
|
-
property 'AvailabilityZone', 'us-east-1a'
|
24
|
+
ec2_instance "EC2_INSTANCE_#{count}" do
|
25
|
+
availability_zone 'us-east-1a'
|
27
26
|
property 'ImageId', 'ami-76e27e1e'
|
28
27
|
property 'KeyName', 'test'
|
29
|
-
|
30
|
-
|
28
|
+
security_group 'sg-dd733c41'
|
29
|
+
security_group 'sg-dd738df3'
|
30
|
+
tag 'Name', 'test-1'
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: convection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.pre.beta.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Manero
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|
@@ -76,15 +76,17 @@ executables:
|
|
76
76
|
extensions: []
|
77
77
|
extra_rdoc_files: []
|
78
78
|
files:
|
79
|
+
- ".github/CONTRIBUTING.md"
|
80
|
+
- ".github/ISSUE_TEMPLATE.md"
|
81
|
+
- ".github/PULL_REQUEST_TEMPLATE.md"
|
79
82
|
- ".gitignore"
|
80
83
|
- ".rubocop.yml"
|
81
84
|
- ".rubocop_todo.yml"
|
82
85
|
- ".ruby-version"
|
83
86
|
- ".travis.yml"
|
84
87
|
- ".yardopts"
|
85
|
-
- CONTRIBUTING.md
|
86
88
|
- Gemfile
|
87
|
-
- LICENSE
|
89
|
+
- LICENSE.md
|
88
90
|
- README.md
|
89
91
|
- Rakefile
|
90
92
|
- Thorfile
|
@@ -242,7 +244,9 @@ files:
|
|
242
244
|
- spec/convection/control/stack/before_delete_tasks_spec.rb
|
243
245
|
- spec/convection/control/stack/before_update_tasks_spec.rb
|
244
246
|
- spec/convection/dsl/intrinsic_functions_spec.rb
|
247
|
+
- spec/convection/model/attributes_spec.rb
|
245
248
|
- spec/convection/model/template/condition_spec.rb
|
249
|
+
- spec/convection/model/template/resource/aws_auto_scaling_auto_scaling_group_spec.rb
|
246
250
|
- spec/convection/model/template/resource/directoryservice_simple_ad_spec.rb
|
247
251
|
- spec/convection/model/template/resource/ec2_dhcp_options_spec.rb
|
248
252
|
- spec/convection/model/template/resource/ec2_security_group_spec.rb
|
@@ -287,12 +291,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
287
291
|
version: '0'
|
288
292
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
289
293
|
requirements:
|
290
|
-
- - "
|
294
|
+
- - ">"
|
291
295
|
- !ruby/object:Gem::Version
|
292
|
-
version:
|
296
|
+
version: 1.3.1
|
293
297
|
requirements: []
|
294
298
|
rubyforge_project:
|
295
|
-
rubygems_version: 2.4.
|
299
|
+
rubygems_version: 2.4.5
|
296
300
|
signing_key:
|
297
301
|
specification_version: 4
|
298
302
|
summary: A fully generic, modular DSL for AWS CloudFormation
|
@@ -306,7 +310,9 @@ test_files:
|
|
306
310
|
- spec/convection/control/stack/before_delete_tasks_spec.rb
|
307
311
|
- spec/convection/control/stack/before_update_tasks_spec.rb
|
308
312
|
- spec/convection/dsl/intrinsic_functions_spec.rb
|
313
|
+
- spec/convection/model/attributes_spec.rb
|
309
314
|
- spec/convection/model/template/condition_spec.rb
|
315
|
+
- spec/convection/model/template/resource/aws_auto_scaling_auto_scaling_group_spec.rb
|
310
316
|
- spec/convection/model/template/resource/directoryservice_simple_ad_spec.rb
|
311
317
|
- spec/convection/model/template/resource/ec2_dhcp_options_spec.rb
|
312
318
|
- spec/convection/model/template/resource/ec2_security_group_spec.rb
|