cfn-flow 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 96e7a912fc803e4d51267f48e138ae273455d424
4
- data.tar.gz: 0827e20cf4a86e42063c55648d50531522bdc769
3
+ metadata.gz: d70dd0718f3ff8dd77315c1478efa29bde5b9411
4
+ data.tar.gz: a15a7d6ee25f1d91908d7d437cde77443f8e1a24
5
5
  SHA512:
6
- metadata.gz: e20d070594f969140f946c1f2cc37ae777bbc795ef846a375d9e064c5078912810d8200018bca251ad0a73bbd6c9437f453fe71915c3875a12a15526ef6ac9b6
7
- data.tar.gz: f0eb7fb958048dc13915255067efdb45e7d06941a50e291a0949aa90f6627f616bc0bc8b3de31ed0236224def48651a974130b68917e58cc992260af74bebdaf
6
+ metadata.gz: 50a5acc781978e22d1a8513d90b14609331de3951b93d51988c7e92c777842d7e7288e1f9dfe830fd9b84f6fad31d7bdaca36a64d5c51784eba2f39d1d78935d
7
+ data.tar.gz: b412de302fb2bab1ee786f9910354e656f4b60796a172e54705012502cf581847770244672afeec8d0ef13cdfebde09ae7808a455f4ea928bf9a52af557a4d24
data/README.md CHANGED
@@ -1,22 +1,56 @@
1
1
  # cfn-flow
2
- `cfn-flow` is an command-line tool for developing [AWS CloudFormation](https://aws.amazon.com/cloudformation/) templates and deploying stacks.
2
+ `cfn-flow` is a command-line tool for developing [AWS CloudFormation](https://aws.amazon.com/cloudformation/) templates and deploying stacks.
3
3
 
4
4
  It provides a *simple*, *standard*, and *flexible* process for using CloudFormation, ideal for DevOps-style organizations.
5
5
 
6
- #### Opinions
6
+ <!-- START doctoc generated TOC please keep comment here to allow auto update -->
7
+ <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
8
+ ## Table of Contents
9
+
10
+ - [Opinions](#opinions)
11
+ - [Installation](#installation)
12
+ - [Key concepts](#key-concepts)
13
+ - [Services](#services)
14
+ - [Environments](#environments)
15
+ - [Deploying](#deploying)
16
+ - [AWS credentials](#aws-credentials)
17
+ - [Configuration](#configuration)
18
+ - [UX improvements](#ux-improvements)
19
+ - [YAML > JSON](#yaml--json)
20
+ - [Embedded ruby in `cfn-flow.yml`](#embedded-ruby-in-cfn-flowyml)
21
+ - [Usage](#usage)
22
+ - [Working with stacks](#working-with-stacks)
23
+ - [Deploy (launch) a stack](#deploy-launch-a-stack)
24
+ - [List stacks for your service or environment](#list-stacks-for-your-service-or-environment)
25
+ - [Inspect a stack](#inspect-a-stack)
26
+ - [Show stack events](#show-stack-events)
27
+ - [Delete a stack](#delete-a-stack)
28
+ - [Common workflows](#common-workflows)
29
+ - [Deploying to production](#deploying-to-production)
30
+ - [Launching a development environment](#launching-a-development-environment)
31
+ - [Working with templates](#working-with-templates)
32
+ - [Validate templates](#validate-templates)
33
+ - [Publish templates to S3](#publish-templates-to-s3)
34
+ - [License](#license)
35
+
36
+ <!-- END doctoc generated TOC please keep comment here to allow auto update -->
37
+
38
+ ## Opinions
7
39
 
8
40
  `cfn-flow` introduces a consist, convenient workflow that encourages good template organization
9
41
  and deploy practices.
10
42
 
11
- 1. *Optimize for happiness.* The workflow should be easy and enjoyable to use.
12
- 2. *Optimize for onboarding.* The workflow should be simple to learn & understand.
13
- 3. *Auditable changes.* Know who changed what when. Leverage git history.
43
+ 1. *Optimize for happiness.* The workflow should be easy & enjoyable to use.
44
+ 2. *Optimize for onboarding.* The workflow should be simple to learn, understand, & debug.
45
+ 3. *Auditable changes.* Know who changed what when & why. Leverage git history.
14
46
  4. *Immutable releases.* The code in a release never changes. To make a change,
15
47
  launch a new stack.
16
48
 
17
49
  The features & implementation of `cfn-flow` itself must also be simple. This follows the Unix philosophy of "[worse is
18
50
  better](http://www.jwz.org/doc/worse-is-better.html)". `cfn-flow` values a simple design and implementation, and being composable with other workflows over handling every edge case out of the box.
19
51
 
52
+ See [this introductory blog post](https://www.kickstarter.com/backing-and-hacking/introducing-cfn-flow-a-practical-workflow-for-aws-cloudformation) for our motivation behind `cfn-flow`.
53
+
20
54
  ## Installation
21
55
 
22
56
  Via [rubygems](https://rubygems.org/gems/cfn-flow):
@@ -26,23 +60,7 @@ gem install cfn-flow
26
60
 
27
61
  The `git` command is also needed.
28
62
 
29
- ## Usage
30
-
31
- ```
32
- # Get help
33
- cfn-flow help
34
-
35
- cfn-flow help COMMAND
36
- # E.g.:
37
- cfn-flow help deploy
38
- ```
39
-
40
- Launch a CloudFormation stack:
41
- ```
42
- cfn-flow deploy production
43
- ```
44
-
45
- ## How it works
63
+ ## Key concepts
46
64
 
47
65
  `cfn-flow` works from a directory containing a `cfn-flow.yml` config file, and a CloudFormation template.
48
66
  Presumably your app code is in the same directory, but it doesn't have to be.
@@ -51,8 +69,8 @@ There are two key concepts for `cfn-flow`: **services** and **environments**.
51
69
 
52
70
  #### Services
53
71
 
54
- A service is a name for your project and comprises a set of resources that
55
- change together. Each service has it's own `cfn-flow.yml` config file. A service
72
+ A service comprises a set of resources that change together.
73
+ Each service has its own `cfn-flow.yml` config file. A service
56
74
  can be instantiated as several distinct environments.
57
75
 
58
76
  For example, a `WebApp` service could have a CloudFormation template that
@@ -64,7 +82,7 @@ service to an environment will create a new ELB, LaunchConfig, and AutoScalingGr
64
82
  Resources that *do not* change across deploys are not part of the service (from
65
83
  `cfn-flow`'s perspective).
66
84
  Say all `WebApp` EC2 servers connect to a long-running RDS database. That
67
- database is not part of the cfn-flow service because it should re-used across
85
+ database is not part of the cfn-flow service because it is re-used across
68
86
  deploys. The database is a *backing resource* the service uses; not part
69
87
  of the service itself.
70
88
 
@@ -76,8 +94,9 @@ could deploy your `WebApp` service to both a `development` and `production` envi
76
94
  `cfn-flow` is designed to support arbitrary environments like git supports
77
95
  arbitrary branches.
78
96
 
79
- Then `CFN_FLOW_ENVIRONMENT` environment variable can be used in
80
- `cfn-flow.yml` to use the environment in your template parameters.
97
+ **Pro tip:** Use the `CFN_FLOW_ENVIRONMENT` environment variable in
98
+ `cfn-flow.yml` config to use the environment in your template parameters.
99
+ See [Configuration](#configuration) for examples.
81
100
 
82
101
  #### Deploying
83
102
 
@@ -131,7 +150,6 @@ stack:
131
150
  And here's a maximal config file:
132
151
 
133
152
  ```yaml
134
- ---
135
153
  # Example cfn-flow.yml
136
154
 
137
155
  service: MyService
@@ -157,7 +175,8 @@ templates:
157
175
  # parameters and tags are hashes. See http://amzn.to/1M0nBuq
158
176
 
159
177
  stack:
160
- stack_name: MyService-<%= Time.now.to_i %>
178
+ # Use the CFN_FLOW_ENVIRONMENT var & git sha in stack name
179
+ stack_name: MyService-<%= ENV['CFN_FLOW_ENVIRONMENT'] %>-<%= `git rev-parse --short HEAD`.chomp %>
161
180
  # NB: template_body is a local path to the template
162
181
  template_body: path/to/template.yml
163
182
  template_url: http://...
@@ -165,6 +184,21 @@ stack:
165
184
  # Your parameters, e.g.:
166
185
  vpcid: vpc-1234
167
186
  ami: ami-abcd
187
+
188
+ ##
189
+ # Use outputs from other stacks
190
+
191
+ # This set the `load_balancer` parameter to the value of the
192
+ # `elbname` output of `my-elb-stack`
193
+ load_balancer:
194
+ stack: my-elb-stack
195
+ output: elbname
196
+
197
+ # If you don't specify the output name, it's assumed to be same
198
+ # as the parameter key:
199
+ ssh_security_group:
200
+ stack: my-bastion-stack
201
+
168
202
  disable_rollback: true,
169
203
  timeout_in_minutes: 1,
170
204
  notification_arns: ["NotificationARN"],
@@ -182,7 +216,7 @@ stack:
182
216
  BillingType: <%= ENV['CFN_FLOW_ENVIRONMENT'] == 'production' ? 'production' : 'development' %>
183
217
  ```
184
218
 
185
- ### UX improvements:
219
+ ## UX improvements
186
220
 
187
221
  `cfn-flow` includes a few developer-friendly features:
188
222
 
@@ -199,7 +233,7 @@ YAML.
199
233
 
200
234
  #### Embedded ruby in `cfn-flow.yml`
201
235
 
202
- To allow dynamic/programatic attributes, use
236
+ To allow dynamic/programmatic attributes, use
203
237
  [ERB](https://en.wikipedia.org/wiki/ERuby) in `cfn-flow.yml`. For example:
204
238
 
205
239
  ```yaml
@@ -210,8 +244,41 @@ stack:
210
244
  git_sha: <%= `git rev-parse --verify HEAD`.chomp %>
211
245
  ```
212
246
 
247
+ #### Use stack outputs as parameters
248
+ `cfn-flow` lets you easily reference stack outputs as parameters for new stacks.
249
+
250
+ ```yaml
251
+ # cfn-flow.yml
252
+ stack:
253
+ parameters:
254
+ # Set my-param to the `my-param` output of `another-stack`
255
+ my-param:
256
+ stack: another-stack
257
+
258
+ # Set my-param to the `my-output` output of `another-stack`
259
+ my-param:
260
+ stack: another-stack
261
+ output: my-output
262
+ ```
263
+
213
264
  ## Usage
214
265
 
266
+ Getting help:
267
+
268
+ ```
269
+ # Get help
270
+ cfn-flow help
271
+
272
+ cfn-flow help COMMAND
273
+ # E.g.:
274
+ cfn-flow help deploy
275
+ ```
276
+
277
+ Launch a CloudFormation stack:
278
+ ```
279
+ cfn-flow deploy production
280
+ ```
281
+
215
282
  ### Working with stacks
216
283
 
217
284
  `cfn-flow` automatically sets two tags on any stack it launches:
@@ -223,42 +290,64 @@ CfnFlowEnvironment | `production`
223
290
 
224
291
  These tags let `cfn-flow` associate stacks back to services & environments.
225
292
 
226
- #### `cfn-flow deploy ENVIRONMENT`
293
+ #### Deploy (launch) a stack
294
+
295
+ ```
296
+ cfn-flow deploy ENVIRONMENT
297
+ ```
227
298
 
228
299
  Launches a stack in ENVIRONMENT. E.g. `cfn-flow deploy production`
229
300
 
230
301
  Add the `--cleanup` option to be prompted to shut down other stacks in the environment.
231
302
 
232
- #### `cfn-flow list ENVIRONMENT`
303
+ #### List stacks for your service or environment
233
304
 
234
- Show running stacks for ENVIRONMENT.
305
+ ```
306
+ cfn-flow list [ENVIRONMENT]
307
+ ```
308
+
309
+ Show all stacks running in your service, or just in an ENVIRONMENT.
235
310
 
236
311
  ```
312
+ # For example:
237
313
  $ cfn-flow list production
238
314
 
239
315
  myapp-production-aaa (CREATE_COMPLETE)
240
316
  myapp-production-bbb (CREATE_FAILED)
241
317
  ```
242
318
 
243
- #### `cfn-flow delete STACK`
244
-
245
- Deletes a stack.
319
+ #### Inspect a stack
246
320
 
247
321
  ```
248
- $ cfn-flow delete myapp-production-aaa
322
+ cfn-flow show STACK
249
323
  ```
250
324
 
251
- #### `cfn-flow show STACK`
252
-
253
325
  Show the status of STACK.
254
326
 
255
- #### `cfn-flow events STACK`
327
+ #### Show stack events
328
+
329
+ ```
330
+ cfn-flow events STACK
331
+ ```
256
332
 
257
333
  List events for STACK
258
334
 
259
- Use the `--tail` option to poll for new events until the stack status is no
335
+ Use the `--poll` option to poll for new events until the stack status is no
260
336
  longer `*_IN_PROGRESS`
261
337
 
338
+ #### Delete a stack
339
+
340
+ ```
341
+ cfn-flow delete STACK
342
+ ```
343
+
344
+ Deletes a stack.
345
+
346
+ ```
347
+ # For example:
348
+ $ cfn-flow delete myapp-production-aaa
349
+ ```
350
+
262
351
  ### Common workflows
263
352
 
264
353
  #### Deploying to production
@@ -289,22 +378,33 @@ cfn-flow deploy myenv
289
378
 
290
379
  ### Working with templates
291
380
 
292
- #### `cfn-flow validate`
381
+ #### Validate templates
293
382
 
294
383
  ```
295
- # Runs validate-template on all templates.
296
- # returns an error on any failure.
297
- # does not persist to S3
384
+ cfn-flow validate TEMPLATE [...]
385
+ ```
298
386
 
387
+ Validates CloudFormation templates; does not persist to S3.
388
+
389
+ ```
390
+ # For example:
299
391
  $ cfn-flow validate path/to/template.yml
300
392
  ```
301
393
 
302
- #### `cfn-flow publish`
394
+ #### Publish templates to S3
395
+
396
+ ```
397
+ cfn-flow publish TEMPLATE [...]
398
+ ```
303
399
 
304
400
  Publish templates to S3 with immutable release names, or overwrite "dev names"
305
- for quicker testing. *This is only needed if you want to use nested stack resources.*
401
+ for quicker testing.
402
+
403
+ **Note:** Publishing to S3 is only needed if you want to use [nested stack resources](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html),
404
+ (that is, stacks that lainclude other stacks).
306
405
 
307
406
  ```
407
+ # For example:
308
408
  $ cfn-flow publish path/to/template.yml
309
409
  # validates & uploads templates to dev path
310
410
  # Env var CFN_FLOW_DEV_NAME=aaron
@@ -39,43 +39,11 @@ module CfnFlow
39
39
  unless config['stack'].is_a? Hash
40
40
  raise Thor::Error.new("No stack defined in #{config_path}. Add 'stack: ...'.")
41
41
  end
42
+ params = StackParams.expanded(config['stack'])
42
43
 
43
- # Dup & symbolize keys
44
- params = config['stack'].map{|k,v| [k.to_sym, v]}.to_h
45
-
46
- # Expand params
47
- if params[:parameters].is_a? Hash
48
- expanded_params = params[:parameters].map do |key,value|
49
- { parameter_key: key, parameter_value: value }
50
- end
51
- params[:parameters] = expanded_params
52
- end
53
-
54
- # Expand tags
55
- if params[:tags].is_a? Hash
56
- tags = params[:tags].map do |key, value|
57
- {key: key, value: value}
58
- end
59
-
60
- params[:tags] = tags
61
- end
62
-
63
- # Append CfnFlow tags
64
- params[:tags] ||= []
65
- params[:tags] << { key: 'CfnFlowService', value: service }
66
- params[:tags] << { key: 'CfnFlowEnvironment', value: environment }
67
-
68
- # Expand template body
69
- if params[:template_body].is_a? String
70
- begin
71
- body = CfnFlow::Template.new(params[:template_body]).to_json
72
- params[:template_body] = body
73
- rescue CfnFlow::Template::Error
74
- # Do nothing
75
- end
76
- end
77
-
78
- params
44
+ params.
45
+ add_tag('CfnFlowService' => service).
46
+ add_tag('CfnFlowEnvironment' => environment)
79
47
  end
80
48
 
81
49
  def template_s3_bucket
@@ -113,6 +81,7 @@ module CfnFlow
113
81
  # Clear aws sdk clients & config (for tests)
114
82
  def clear!
115
83
  @config = @cfn_client = @cfn_resource = nil
84
+ CachedStack.stack_cache.clear
116
85
  end
117
86
 
118
87
  # Exit with status code = 1 when raising a Thor::Error
@@ -131,6 +100,8 @@ module CfnFlow
131
100
  end
132
101
  end
133
102
 
103
+ require 'cfn_flow/cached_stack'
104
+ require 'cfn_flow/stack_params'
134
105
  require 'cfn_flow/template'
135
106
  require 'cfn_flow/git'
136
107
  require 'cfn_flow/event_presenter'
@@ -0,0 +1,32 @@
1
+ module CfnFlow
2
+ class CachedStack
3
+
4
+ class MissingOutput < StandardError; end
5
+
6
+ def self.stack_cache
7
+ @stack_cache ||= {}
8
+ end
9
+
10
+ def self.get_output(stack:, output:)
11
+ new(stack).output(output)
12
+ end
13
+
14
+ attr_reader :stack_name
15
+
16
+ def initialize(stack_name)
17
+ @stack_name = stack_name
18
+ end
19
+
20
+ def output(name)
21
+ output = stack_cache.outputs.detect{|out| out.output_key == name }
22
+ unless output
23
+ raise MissingOutput.new("Can't find outpout #{name} for stack #{stack_name}")
24
+ end
25
+ output.output_value
26
+ end
27
+
28
+ def stack_cache
29
+ self.class.stack_cache[stack_name] ||= CfnFlow.cfn_resource.stack(stack_name).load
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,71 @@
1
+ module CfnFlow
2
+ # Extend hash with some special behavior to generate the
3
+ # style of hash aws-sdk expects
4
+ class StackParams < Hash
5
+
6
+ def self.expanded(hash)
7
+ self[hash].
8
+ with_symbolized_keys.
9
+ with_expanded_parameters.
10
+ with_expanded_tags.
11
+ with_expanded_template_body
12
+ end
13
+
14
+ def with_symbolized_keys
15
+ self.inject(StackParams.new) do |accum, pair|
16
+ key, value = pair
17
+ accum.merge(key.to_sym => value)
18
+ end
19
+ end
20
+
21
+ def with_expanded_parameters
22
+ return self unless self[:parameters].is_a? Hash
23
+
24
+ expanded_params = self[:parameters].map do |key,value|
25
+ { parameter_key: key, parameter_value: fetch_value(key, value) }
26
+ end
27
+
28
+ self.merge(parameters: expanded_params)
29
+ end
30
+
31
+ def with_expanded_tags
32
+ return self unless self[:tags].is_a? Hash
33
+
34
+ tags = self[:tags].map do |key, value|
35
+ {key: key, value: value}
36
+ end
37
+
38
+ self.merge(tags: tags)
39
+ end
40
+
41
+ def add_tag(hash)
42
+ new_tags = hash.map do |k,v|
43
+ {key: k, value: v }
44
+ end
45
+ tags = (self[:tags] || []) + new_tags
46
+ self.merge(tags: tags)
47
+ end
48
+
49
+ def with_expanded_template_body
50
+ return self unless self[:template_body].is_a? String
51
+ body = CfnFlow::Template.new(self[:template_body]).to_json
52
+ self.merge(template_body: body)
53
+ rescue CfnFlow::Template::Error
54
+ # Do nothing
55
+ self
56
+ end
57
+
58
+ def fetch_value(key, value)
59
+ # Dereference stack output params
60
+ if value.is_a?(Hash) && value.key?('stack')
61
+ stack_name = value['stack']
62
+ stack_output_name = value['output'] || key
63
+
64
+ value = CachedStack.get_output(stack: stack_name, output: stack_output_name)
65
+ else
66
+ value
67
+ end
68
+ end
69
+ private :fetch_value
70
+ end
71
+ end
@@ -1,3 +1,3 @@
1
1
  module CfnFlow
2
- VERSION = '0.8.0'
2
+ VERSION = '0.9.0'
3
3
  end
@@ -0,0 +1,71 @@
1
+ require_relative '../helper'
2
+
3
+ describe 'CfnFlow::CachedStack' do
4
+ subject { CfnFlow::CachedStack }
5
+
6
+ describe '.stack_cache' do
7
+ it 'defaults to a hash' do
8
+ subject.stack_cache.must_equal({})
9
+ end
10
+ end
11
+
12
+ describe '.get_output' do
13
+ let(:output_value) { 'myvalue' }
14
+
15
+ before do
16
+ Aws.config[:cloudformation]= {
17
+ stub_responses: {
18
+ describe_stacks: { stacks: [ stub_stack_data.merge(outputs: [{ output_key: "myoutput", output_value: output_value } ]) ] }
19
+ }
20
+ }
21
+ end
22
+
23
+ it 'returns the output' do
24
+ subject.get_output(stack: 'mystack', output: 'myoutput').must_equal output_value
25
+ end
26
+
27
+ it 'has required kwargs' do
28
+ -> { subject.get_output }.must_raise(ArgumentError)
29
+ end
30
+ end
31
+
32
+ describe 'an instance' do
33
+ subject { CfnFlow::CachedStack.new('mystack') }
34
+ let(:output_value) { 'myvalue' }
35
+
36
+ before do
37
+ Aws.config[:cloudformation]= {
38
+ stub_responses: {
39
+ describe_stacks: { stacks: [ stub_stack_data.merge(outputs: [{ output_key: "myoutput", output_value: output_value } ]) ] }
40
+ }
41
+ }
42
+ end
43
+
44
+ it "should return the output value" do
45
+ subject.output('myoutput').must_equal output_value
46
+ end
47
+
48
+ describe "with a missing output" do
49
+ it "should raise an error" do
50
+ -> { subject.output("no-such-output") }.must_raise(CfnFlow::CachedStack::MissingOutput)
51
+ end
52
+ end
53
+
54
+ describe "with a missing stack" do
55
+
56
+ subject { CfnFlow::CachedStack.new('no-such-stack') }
57
+ before do
58
+ Aws.config[:cloudformation]= {
59
+ stub_responses: {
60
+ describe_stacks: 'ValidationError'
61
+ }
62
+ }
63
+ end
64
+
65
+ it "should raise an error" do
66
+ -> { subject.output('blah') }.must_raise(Aws::CloudFormation::Errors::ValidationError)
67
+ end
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,114 @@
1
+ require_relative '../helper'
2
+
3
+ describe 'CfnFlow::StackParams' do
4
+ subject { CfnFlow::StackParams }
5
+
6
+ it 'should be a hash' do
7
+ subject.new.must_be_kind_of Hash
8
+ end
9
+
10
+ describe '.expanded' do
11
+ it "returns a StackParams hash" do
12
+ subject.expanded({}).must_be_kind_of subject
13
+ end
14
+ end
15
+
16
+ describe '#with_symbolized_keys' do
17
+ it 'works' do
18
+ subject[{'foo' => 1, :bar => true}].with_symbolized_keys.must_equal({foo: 1, bar: true})
19
+ end
20
+ end
21
+
22
+ describe '#with_expanded_parameters' do
23
+ it 'reformats parameters hash to array of hashes' do
24
+ hash = {
25
+ parameters: { 'k1' => 'v1', 'k2' => 'v2' }
26
+ }
27
+
28
+ expected = {
29
+ parameters: [
30
+ {parameter_key: 'k1', parameter_value: 'v1'},
31
+ {parameter_key: 'k2', parameter_value: 'v2'}
32
+ ]
33
+ }
34
+
35
+ subject[hash].with_expanded_parameters.must_equal expected
36
+ end
37
+
38
+ describe 'with stack outputs' do
39
+ let(:output_key) { 'my-output-key' }
40
+ let(:output_value) { 'my-output-value' }
41
+
42
+ before do
43
+ Aws.config[:cloudformation]= {
44
+ stub_responses: {
45
+ describe_stacks: { stacks: [ stub_stack_data.merge(outputs: [{ output_key: output_key, output_value: output_value } ]) ] }
46
+ }
47
+ }
48
+ end
49
+
50
+ it 'fetches stack outputs with explicit output key' do
51
+ hash = {
52
+ parameters: {
53
+ 'my-key' => { 'stack' => 'my-stack', 'output' => output_key}
54
+ }
55
+ }
56
+ expected = {
57
+ parameters: [ {parameter_key: 'my-key', parameter_value: output_value} ]
58
+ }
59
+
60
+ subject[hash].with_expanded_parameters.must_equal expected
61
+ end
62
+
63
+ it 'fetches stack outputs with implicit output key' do
64
+ hash = {
65
+ parameters: {
66
+ output_key => { 'stack' => 'my-stack'}
67
+ }
68
+ }
69
+ expected = {
70
+ parameters: [ {parameter_key: output_key, parameter_value: output_value} ]
71
+ }
72
+
73
+ subject[hash].with_expanded_parameters.must_equal expected
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#with_expanded_tags' do
79
+ it 'expands tags hash to array of hashes' do
80
+ hash = {tags: {'k' => 'v'} }
81
+ expected = {tags: [{key: 'k', value: 'v'}]}
82
+ subject[hash].with_expanded_tags.must_equal expected
83
+ end
84
+ end
85
+
86
+ describe '#add_tag' do
87
+ it 'sets an empty tag hash' do
88
+ subject.new.add_tag('k' => 'v').must_equal({tags: [{key: 'k', value: 'v'}]})
89
+
90
+ end
91
+ it 'appends to existing tag hash' do
92
+ orig = subject[{tags: [{key: 'k1', value: 'v1'}] }]
93
+ expected = {tags: [{key: 'k1', value: 'v1'}, {key: 'k2', value: 'v2'}] }
94
+
95
+ orig.add_tag('k2' => 'v2').must_equal expected
96
+
97
+ end
98
+ end
99
+
100
+ describe '#with_expanded_template_body' do
101
+ it 'does not expand invalid templates' do
102
+ hash = { template_body: 'spec/data/invalid.yml' }
103
+ subject[hash].with_expanded_template_body.must_equal hash
104
+ end
105
+
106
+ it 'expands valid template paths' do
107
+ template_path = 'spec/data/sqs.template'
108
+ result = subject[template_body: template_path].with_expanded_template_body
109
+
110
+ result.must_equal({template_body: CfnFlow::Template.new(template_path).to_json})
111
+ end
112
+ end
113
+
114
+ end
@@ -50,22 +50,9 @@ describe 'CfnFlow' do
50
50
  error.message.must_match 'No stack defined'
51
51
  end
52
52
 
53
- it('expands parameters') do
54
- stack = {'parameters' => {'ami' => 'ami-12345' } }
55
- subject.instance_variable_set(:@config, {'service' => 'myservice', 'stack' => stack})
56
- subject.stack_params('env')[:parameters].must_equal [ { parameter_key: 'ami', parameter_value: 'ami-12345' } ]
57
- end
58
-
59
- it('expands tags') do
60
- stack = {'tags' => {'Deployer' => 'Aaron' } }
61
- subject.instance_variable_set(:@config, {'service' => 'myservice', 'stack' => stack})
62
- expected = [
63
- { key: 'Deployer', value: 'Aaron' },
64
- { key: 'CfnFlowService', value: 'myservice' },
65
- { key: 'CfnFlowEnvironment', value: 'env' }
66
- ]
67
-
68
- subject.stack_params('env')[:tags].must_equal expected
53
+ it('returns a StackParams hash') do
54
+ subject.instance_variable_set(:@config, {'service' => 'myservice', 'stack' => {}})
55
+ subject.stack_params('env').must_be_kind_of CfnFlow::StackParams
69
56
  end
70
57
 
71
58
  it 'appends CfnFlow tags' do
@@ -77,14 +64,6 @@ describe 'CfnFlow' do
77
64
 
78
65
  subject.stack_params('env')[:tags].must_equal expected
79
66
  end
80
-
81
- it 'expands template body' do
82
- template_path = 'spec/data/sqs.template'
83
- stack = {'template_body' => template_path}
84
- subject.instance_variable_set(:@config, {'service' => 'myservice', 'stack' => stack})
85
- subject.stack_params('env')[:template_body].must_equal CfnFlow::Template.new(template_path).to_json
86
- end
87
-
88
67
  end
89
68
 
90
69
  describe '.template_s3_bucket' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfn-flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Suggs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-02 00:00:00.000000000 Z
11
+ date: 2015-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk
@@ -80,7 +80,7 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description: An opinionated worflow for AWS CloudFormation
83
+ description: A practical worflow for AWS CloudFormation
84
84
  email: aaron@ktheory.com
85
85
  executables:
86
86
  - cfn-flow
@@ -93,13 +93,17 @@ files:
93
93
  - bin/rake
94
94
  - lib/cfn-flow.rb
95
95
  - lib/cfn_flow.rb
96
+ - lib/cfn_flow/cached_stack.rb
96
97
  - lib/cfn_flow/cli.rb
97
98
  - lib/cfn_flow/event_presenter.rb
98
99
  - lib/cfn_flow/git.rb
100
+ - lib/cfn_flow/stack_params.rb
99
101
  - lib/cfn_flow/template.rb
100
102
  - lib/cfn_flow/version.rb
103
+ - spec/cfn_flow/cached_stack_spec.rb
101
104
  - spec/cfn_flow/cli_spec.rb
102
105
  - spec/cfn_flow/event_presenter_spec.rb
106
+ - spec/cfn_flow/stack_params_spec.rb
103
107
  - spec/cfn_flow/template_spec.rb
104
108
  - spec/cfn_flow_spec.rb
105
109
  - spec/data/cfn-flow.yml
@@ -129,13 +133,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
133
  version: '0'
130
134
  requirements: []
131
135
  rubyforge_project:
132
- rubygems_version: 2.4.5
136
+ rubygems_version: 2.4.5.1
133
137
  signing_key:
134
138
  specification_version: 4
135
139
  summary: A CLI for CloudFormation templates
136
140
  test_files:
141
+ - spec/cfn_flow/cached_stack_spec.rb
137
142
  - spec/cfn_flow/cli_spec.rb
138
143
  - spec/cfn_flow/event_presenter_spec.rb
144
+ - spec/cfn_flow/stack_params_spec.rb
139
145
  - spec/cfn_flow/template_spec.rb
140
146
  - spec/cfn_flow_spec.rb
141
147
  - spec/data/cfn-flow.yml