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 +4 -4
- data/README.md +148 -48
- data/lib/cfn_flow.rb +7 -36
- data/lib/cfn_flow/cached_stack.rb +32 -0
- data/lib/cfn_flow/stack_params.rb +71 -0
- data/lib/cfn_flow/version.rb +1 -1
- data/spec/cfn_flow/cached_stack_spec.rb +71 -0
- data/spec/cfn_flow/stack_params_spec.rb +114 -0
- data/spec/cfn_flow_spec.rb +3 -24
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d70dd0718f3ff8dd77315c1478efa29bde5b9411
|
4
|
+
data.tar.gz: a15a7d6ee25f1d91908d7d437cde77443f8e1a24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
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
|
12
|
-
2. *Optimize for onboarding.* The workflow should be simple to learn &
|
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
|
-
##
|
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
|
55
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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/
|
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
|
-
####
|
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
|
-
####
|
303
|
+
#### List stacks for your service or environment
|
233
304
|
|
234
|
-
|
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
|
-
####
|
244
|
-
|
245
|
-
Deletes a stack.
|
319
|
+
#### Inspect a stack
|
246
320
|
|
247
321
|
```
|
248
|
-
|
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
|
-
####
|
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 `--
|
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
|
-
####
|
381
|
+
#### Validate templates
|
293
382
|
|
294
383
|
```
|
295
|
-
|
296
|
-
|
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
|
-
####
|
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.
|
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
|
data/lib/cfn_flow.rb
CHANGED
@@ -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
|
-
|
44
|
-
|
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
|
data/lib/cfn_flow/version.rb
CHANGED
@@ -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
|
data/spec/cfn_flow_spec.rb
CHANGED
@@ -50,22 +50,9 @@ describe 'CfnFlow' do
|
|
50
50
|
error.message.must_match 'No stack defined'
|
51
51
|
end
|
52
52
|
|
53
|
-
it('
|
54
|
-
|
55
|
-
subject.
|
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.
|
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-
|
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:
|
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
|