cfn-flow 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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