cfer 0.5.0.pre.rc1 → 0.5.0.pre.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +9 -60
- data/bin/cfer-dbg +1 -16
- data/bin/console +4 -9
- data/cfer.gemspec +1 -1
- data/lib/cfer.rb +52 -84
- data/lib/cfer/block.rb +19 -1
- data/lib/cfer/cfn/client.rb +3 -11
- data/lib/cfer/cli.rb +145 -121
- data/lib/cfer/config.rb +47 -0
- data/lib/cfer/console.rb +21 -0
- data/lib/cfer/core/stack.rb +1 -5
- data/lib/cfer/util/error.rb +3 -0
- data/lib/cfer/version.rb +1 -1
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '03594ef49c992c1131effb85d44014b6fabc7356'
|
4
|
+
data.tar.gz: 10026ae4c9616d6ba4cfb61068c1861211dd680f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcd8edc9504e962937b7735432871d6c38dcbda94c602389dccd7b140e0faca0508f25219d125488f530f94daece3ffc65f2438092e6bfe271ff6a29ca9e3044
|
7
|
+
data.tar.gz: a0bf003d630ae1137880ffb62a29190fe6695baf1b1de12f90a543c8e27371dbfd49267fb3e663603ed75cfd29e76c7924920ba2cb4278eb38c86df0d2557949
|
data/CHANGELOG.md
CHANGED
@@ -2,10 +2,15 @@
|
|
2
2
|
|
3
3
|
## 0.5.0
|
4
4
|
|
5
|
+
### **BREAKING CHANGES**
|
6
|
+
* `--parameters <name>:<value>` option is removed from CLI. Use `name=value` instead.
|
7
|
+
For example: `cfer generate stack.rb parameter_name=parameter_value`
|
8
|
+
|
5
9
|
### Enhancements
|
6
10
|
* Adds support for Pre-build and Post-build hooks for resources and stacks.
|
7
11
|
* Adds `json-to-cfer` script to automatically convert json templates to Cfer DSL code.
|
8
12
|
* Adds support for directly converging JSON files.
|
13
|
+
* Replaces [Thor](https://github.com/erikhuda/thor) with [Cri](https://github.com/ddfreyne/cri) as a CLI option parser.
|
9
14
|
* Relaxes some version constraints to make it easier to integrate with older Rails projects.
|
10
15
|
* Pulled stack validation out into an extension using post-build hooks.
|
11
16
|
* Adds some extension methods to improve usability of certain resources.
|
@@ -13,6 +18,7 @@
|
|
13
18
|
* Supports reading ruby template from stdin by specifying the filename `-`
|
14
19
|
* Adds exponential backoff to `tail` command.
|
15
20
|
* `--on-failure` flag is now case insensitive.
|
21
|
+
* Removes `--pretty-print` as a global option and adds `--minified` to the `generate` command.
|
16
22
|
* Various test improvements.
|
17
23
|
|
18
24
|
### Bugfixes
|
data/README.md
CHANGED
@@ -41,7 +41,7 @@ To quickly see Cfer in action, try converging the example stacks:
|
|
41
41
|
|
42
42
|
```bash
|
43
43
|
cfer converge vpc -t examples/vpc.rb --profile [YOUR-PROFILE] --region [YOUR-REGION]
|
44
|
-
cfer converge instance -t examples/instance.rb --profile [YOUR-PROFILE] --region [YOUR-REGION]
|
44
|
+
cfer converge instance -t examples/instance.rb --profile [YOUR-PROFILE] --region [YOUR-REGION] KeyName=[YOUR-EC2-SSH-KEY]
|
45
45
|
```
|
46
46
|
|
47
47
|
You should see something like this:
|
@@ -50,65 +50,14 @@ You should see something like this:
|
|
50
50
|
|
51
51
|
### Command line
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
* `--profile <profile>`: The AWS profile to use (from your `~/.aws/credentials` file)
|
62
|
-
* `--region <region>`: The AWS region to use
|
63
|
-
* `--verbose`: Also print debugging messages
|
64
|
-
|
65
|
-
#### `generate <template.rb>`
|
66
|
-
|
67
|
-
The `generate` subcommand evaluates the given Ruby script and prints the CloudFormation stack JSON to standard output.
|
68
|
-
|
69
|
-
The following options may be used with the `generate` command:
|
70
|
-
|
71
|
-
* `--no-pretty-print`: Print minified JSON
|
72
|
-
|
73
|
-
#### `converge <stack-name>`
|
74
|
-
|
75
|
-
Creates or updates a CloudFormation stack according to the specified template.
|
76
|
-
|
77
|
-
The following options may be used with the `converge` command:
|
78
|
-
|
79
|
-
* `--follow` (`-f`): Follows stack events on standard output as the create/update process takes place.
|
80
|
-
* `--stack-file <template.rb>`: Reads this file from the filesystem, rather than the default `<stack-name>.rb`
|
81
|
-
* `--parameters <Key1>:<Value1> <Key2>:<Value2> ...`: Specifies input parameters, which will be available to Ruby in the `parameters` hash, or to CloudFormation by using the `Fn::ref` function
|
82
|
-
* `--parameter-file <params_file.[yaml|json]`: Specifies input parameters from a YAML or JSON file
|
83
|
-
* `--parameter-environment <env_name>`: Requires `--parameter-file`. Merges the specified key in the YAML or JSON file into the root of the parameter file before passing it into the Cfer stack, i.e. to provide different constants for different AWS environments. The priority for parameters is, in ascending order, **stack default**, **file**, **environment**, and **command line**.
|
84
|
-
* `--on-failure <DELETE|ROLLBACK|DO_NOTHING>`: Specifies the action to take when a stack creation fails. Has no effect if the stack already exists and is being updated.
|
85
|
-
* `--stack-policy <filename|URL|JSON string>` (`-s`): Stack policy to apply to the stack in order to control updates; takes a local filename containing the policy, a URL to an S3 object, or a raw JSON string.
|
86
|
-
* `--stack-policy-during-update <filename|URL|JSON string>` (`-u`): Stack policy as in `--stack-policy` option above, but applied as a temporary override to the permanent policy during stack update.
|
87
|
-
* `--s3-path <S3_PATH>`: Path to an S3 bucket location where the template will be stored. This is required if the template output exceeds [51,200 bytes](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cloudformation-limits.html).
|
88
|
-
* `--force-s3`: Forces Cfer to upload the template to S3, even if it's small enough to be uploaded directly to the cloudformation API.
|
89
|
-
* `--change <CHANGE_NAME>`: Creates a [CloudFormation Change Set](https://aws.amazon.com/blogs/aws/new-change-sets-for-aws-cloudformation/), rather than immediately updating the stack.
|
90
|
-
|
91
|
-
#### `tail <stack-name>`
|
92
|
-
|
93
|
-
Prints the latest `n` stack events, and optionally follows events while a stack is converging.
|
94
|
-
|
95
|
-
The following options may be used with the `tail` command:
|
96
|
-
|
97
|
-
* `--follow` (`-f`): Follows stack events on standard output as the create/update process takes place.
|
98
|
-
* `--number` (`-n`): Print the last `n` stack events.
|
99
|
-
|
100
|
-
#### `remove <stack-name>`
|
101
|
-
|
102
|
-
Removes or deletes an existing CloudFormation stack.
|
103
|
-
|
104
|
-
```bash
|
105
|
-
cfer remove vpc --profile [YOUR-PROFILE] --region [YOUR-REGION]
|
106
|
-
```
|
107
|
-
|
108
|
-
This can also be done in the following way with the awscli tools, but now eliminates the need to install that package.
|
109
|
-
```bash
|
110
|
-
aws cloudformation delete-stack --stack-name vpc
|
111
|
-
```
|
53
|
+
COMMANDS
|
54
|
+
converge Create or update a cloudformation stack according to the template
|
55
|
+
delete Deletes a CloudFormation stack
|
56
|
+
describe Fetches and prints information about a CloudFormation
|
57
|
+
estimate Prints a link to the Amazon cost caculator estimating the cost of the resulting CloudFormation stack
|
58
|
+
generate Generates a CloudFormation template by evaluating a Cfer template
|
59
|
+
help show help
|
60
|
+
tail Follows stack events on standard output as they occur
|
112
61
|
|
113
62
|
### Template Anatomy
|
114
63
|
|
data/bin/cfer-dbg
CHANGED
@@ -2,22 +2,7 @@
|
|
2
2
|
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
3
|
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
4
4
|
|
5
|
-
require '
|
6
|
-
require 'pry-rescue'
|
7
|
-
|
8
|
-
module Cfer
|
9
|
-
DEBUG = true
|
10
|
-
end
|
11
|
-
|
12
|
-
require 'cfer'
|
13
|
-
|
14
|
-
Cfer::LOGGER.level = Logger::DEBUG
|
15
|
-
|
16
|
-
Cfer::LOGGER.fatal "Showing FATAL logs"
|
17
|
-
Cfer::LOGGER.error "Showing ERROR logs"
|
18
|
-
Cfer::LOGGER.warn "Showing WARN logs"
|
19
|
-
Cfer::LOGGER.info "Showing INFO logs"
|
20
|
-
Cfer::LOGGER.debug "Showing DEBUG logs"
|
5
|
+
require 'cfer/console'
|
21
6
|
|
22
7
|
Pry::rescue {
|
23
8
|
Cfer::Cli::main(ARGV)
|
data/bin/console
CHANGED
@@ -1,14 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
3
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
2
4
|
|
3
5
|
require "bundler/setup"
|
4
|
-
require
|
6
|
+
require 'cfer/console'
|
5
7
|
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "pry"
|
14
8
|
Pry.start
|
9
|
+
|
data/cfer.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
24
|
spec.add_runtime_dependency 'docile', '~> 1.1'
|
25
|
-
spec.add_runtime_dependency '
|
25
|
+
spec.add_runtime_dependency 'cri', '~> 2.7'
|
26
26
|
spec.add_runtime_dependency 'activesupport', '>= 3'
|
27
27
|
spec.add_runtime_dependency 'aws-sdk', '~> 2.2'
|
28
28
|
spec.add_runtime_dependency 'aws-sdk-resources', '~> 2.2'
|
data/lib/cfer.rb
CHANGED
@@ -14,6 +14,8 @@ end
|
|
14
14
|
|
15
15
|
# Contains the core Cfer logic
|
16
16
|
module Cfer
|
17
|
+
DEBUG = false unless defined? DEBUG
|
18
|
+
|
17
19
|
# Code relating to working with Amazon CloudFormation
|
18
20
|
module Cfn
|
19
21
|
end
|
@@ -62,13 +64,15 @@ module Cfer
|
|
62
64
|
cfn = options[:aws_options] || {}
|
63
65
|
|
64
66
|
cfn_stack = options[:cfer_client] || Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name))
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
)
|
67
|
+
raise Cfer::Util::CferError, "No such template file: #{tmpl}" unless File.exists?(tmpl)
|
68
|
+
stack =
|
69
|
+
options[:cfer_stack] ||
|
70
|
+
Cfer::stack_from_file(tmpl,
|
71
|
+
options.merge(
|
72
|
+
client: cfn_stack,
|
73
|
+
parameters: generate_final_parameters(options)
|
74
|
+
)
|
75
|
+
)
|
72
76
|
|
73
77
|
begin
|
74
78
|
operation = stack.converge!(options)
|
@@ -91,7 +95,8 @@ module Cfer
|
|
91
95
|
end
|
92
96
|
end
|
93
97
|
end
|
94
|
-
|
98
|
+
# This is allowed to fail, particularly if we decided to roll back
|
99
|
+
describe! stack_name, options rescue nil
|
95
100
|
rescue Aws::CloudFormation::Errors::ValidationError => e
|
96
101
|
Cfer::LOGGER.info "CFN validation error: #{e.message}"
|
97
102
|
end
|
@@ -109,7 +114,7 @@ module Cfer
|
|
109
114
|
if cfer_version
|
110
115
|
cfer_version_str = [cfer_version["major"], cfer_version["minor"], cfer_version["patch"]].join '.'
|
111
116
|
cfer_version_str << '-' << cfer_version["pre"] unless cfer_version["pre"].nil?
|
112
|
-
cfer_version_str << '+' << cfer_version["
|
117
|
+
cfer_version_str << '+' << cfer_version["build"] unless cfer_version["build"].nil?
|
113
118
|
end
|
114
119
|
|
115
120
|
Cfer::LOGGER.debug "Describe stack: #{cfn_stack}"
|
@@ -160,6 +165,7 @@ module Cfer
|
|
160
165
|
cfn = options[:aws_options] || {}
|
161
166
|
|
162
167
|
cfn_stack = options[:cfer_client] || Cfer::Cfn::Client.new(cfn)
|
168
|
+
raise Cfer::Util::CferError, "No such template file: #{tmpl}" unless File.exists?(tmpl)
|
163
169
|
stack = options[:cfer_stack] || Cfer::stack_from_file(tmpl,
|
164
170
|
options.merge(client: cfn_stack, parameters: generate_final_parameters(options))).to_h
|
165
171
|
puts render_json(stack, options)
|
@@ -179,7 +185,7 @@ module Cfer
|
|
179
185
|
config(options)
|
180
186
|
cfn = options[:aws_options] || {}
|
181
187
|
cfn_stack = options[:cfer_client] || cfn_stack = Cfer::Cfn::Client.new(cfn.merge(stack_name: stack_name))
|
182
|
-
cfn_stack.delete_stack(stack_name)
|
188
|
+
cfn_stack.delete_stack(stack_name: stack_name)
|
183
189
|
|
184
190
|
if options[:follow]
|
185
191
|
tail! stack_name, options.merge(cfer_client: cfn_stack)
|
@@ -209,12 +215,8 @@ module Cfer
|
|
209
215
|
return stack_from_stdin(options) if file == '-'
|
210
216
|
|
211
217
|
s = Cfer::Core::Stack.new(options)
|
212
|
-
|
213
|
-
s.
|
214
|
-
else
|
215
|
-
templatize_errors(file) do
|
216
|
-
s.build_from_file file
|
217
|
-
end
|
218
|
+
templatize_errors(file) do
|
219
|
+
s.build_from_file file
|
218
220
|
end
|
219
221
|
s
|
220
222
|
end
|
@@ -242,32 +244,28 @@ module Cfer
|
|
242
244
|
end
|
243
245
|
|
244
246
|
def generate_final_parameters(options)
|
245
|
-
raise "parameter-environment set but parameter_file not set" \
|
247
|
+
raise Cfer::Util::CferError, "parameter-environment set but parameter_file not set" \
|
246
248
|
if options[:parameter_environment] && options[:parameter_file].nil?
|
247
249
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
YAML.load_file(options[:parameter_file])
|
254
|
-
when '.json'
|
255
|
-
JSON.parse(IO.read(options[:parameter_file]))
|
256
|
-
else
|
257
|
-
raise "Unrecognized parameter file format: #{File.extname(options[:parameter_file])}"
|
258
|
-
end
|
259
|
-
else
|
260
|
-
{}
|
261
|
-
end
|
250
|
+
final_params = HashWithIndifferentAccess.new
|
251
|
+
|
252
|
+
final_params.deep_merge! Cfer::Config.new(cfer: options) \
|
253
|
+
.build_from_file(options[:parameter_file]) \
|
254
|
+
.to_h if options[:parameter_file]
|
262
255
|
|
263
256
|
if options[:parameter_environment]
|
264
|
-
raise "no key '#{options[:parameter_environment]}' found in parameters file." \
|
265
|
-
unless
|
257
|
+
raise Cfer::Util::CferError, "no key '#{options[:parameter_environment]}' found in parameters file." \
|
258
|
+
unless final_params.key?(options[:parameter_environment])
|
259
|
+
|
260
|
+
Cfer::LOGGER.debug "Merging in environment key #{options[:parameter_environment]}"
|
266
261
|
|
267
|
-
|
262
|
+
final_params.deep_merge!(final_params[options[:parameter_environment]])
|
268
263
|
end
|
269
264
|
|
270
|
-
|
265
|
+
final_params.deep_merge!(options[:parameters] || {})
|
266
|
+
|
267
|
+
Cfer::LOGGER.debug "Final parameters: #{final_params}"
|
268
|
+
final_params
|
271
269
|
end
|
272
270
|
|
273
271
|
def render_json(obj, options = {})
|
@@ -298,53 +296,24 @@ module Cfer
|
|
298
296
|
|
299
297
|
|
300
298
|
COLORS_MAP = {
|
301
|
-
'CREATE_IN_PROGRESS' => {
|
302
|
-
|
303
|
-
},
|
304
|
-
|
305
|
-
|
306
|
-
},
|
307
|
-
'
|
308
|
-
|
309
|
-
},
|
310
|
-
|
311
|
-
'
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
'
|
316
|
-
|
317
|
-
|
318
|
-
}
|
319
|
-
'UPDATE_FAILED' => {
|
320
|
-
color: :red,
|
321
|
-
finished: true
|
322
|
-
},
|
323
|
-
|
324
|
-
'CREATE_COMPLETE' => {
|
325
|
-
color: :green,
|
326
|
-
finished: true
|
327
|
-
},
|
328
|
-
'DELETE_COMPLETE' => {
|
329
|
-
color: :green,
|
330
|
-
finished: true
|
331
|
-
},
|
332
|
-
'UPDATE_COMPLETE' => {
|
333
|
-
color: :green,
|
334
|
-
finished: true
|
335
|
-
},
|
336
|
-
|
337
|
-
'DELETE_SKIPPED' => {
|
338
|
-
color: :yellow
|
339
|
-
},
|
340
|
-
|
341
|
-
'ROLLBACK_IN_PROGRESS' => {
|
342
|
-
color: :red
|
343
|
-
},
|
344
|
-
'ROLLBACK_COMPLETE' => {
|
345
|
-
color: :red,
|
346
|
-
finished: true
|
347
|
-
}
|
299
|
+
'CREATE_IN_PROGRESS' => { color: :yellow },
|
300
|
+
'DELETE_IN_PROGRESS' => { color: :yellow },
|
301
|
+
'UPDATE_IN_PROGRESS' => { color: :green },
|
302
|
+
|
303
|
+
'CREATE_FAILED' => { color: :red, finished: true },
|
304
|
+
'DELETE_FAILED' => { color: :red, finished: true },
|
305
|
+
'UPDATE_FAILED' => { color: :red, finished: true },
|
306
|
+
|
307
|
+
'CREATE_COMPLETE' => { color: :green, finished: true },
|
308
|
+
'DELETE_COMPLETE' => { color: :green, finished: true },
|
309
|
+
'UPDATE_COMPLETE' => { color: :green, finished: true },
|
310
|
+
|
311
|
+
'DELETE_SKIPPED' => { color: :yellow },
|
312
|
+
|
313
|
+
'ROLLBACK_IN_PROGRESS' => { color: :red },
|
314
|
+
|
315
|
+
'UPDATE_ROLLBACK_COMPLETE' => { color: :red, finished: true },
|
316
|
+
'ROLLBACK_COMPLETE' => { color: :red, finished: true }
|
348
317
|
}
|
349
318
|
|
350
319
|
def color_map(str)
|
@@ -361,7 +330,6 @@ module Cfer
|
|
361
330
|
end
|
362
331
|
end
|
363
332
|
|
364
|
-
Dir["#{File.dirname(__FILE__)}/cfer
|
365
|
-
Dir["#{File.dirname(__FILE__)}/cfer/**/*.rb"].each { |f| require(f) }
|
333
|
+
Dir["#{File.dirname(__FILE__)}/cfer/**/*.rb"].each { |f| require(f) unless f.ends_with?('console.rb') }
|
366
334
|
Dir["#{File.dirname(__FILE__)}/cferext/**/*.rb"].each { |f| require(f) }
|
367
335
|
|
data/lib/cfer/block.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'docile'
|
2
|
+
require 'json'
|
3
|
+
require 'yaml'
|
2
4
|
|
3
5
|
module Cfer
|
4
6
|
# Represents the base class of a Cfer DSL
|
@@ -27,7 +29,23 @@ module Cfer
|
|
27
29
|
# @param args [Array<Object>] (see: #build_from_block)
|
28
30
|
# @param file [File] The Ruby script to evaluate
|
29
31
|
def build_from_file(*args, file)
|
30
|
-
|
32
|
+
build_from_block(*args) do
|
33
|
+
include_file(file)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def include_file(file)
|
38
|
+
Preconditions.check(file).is_not_nil
|
39
|
+
raise Cfer::Util::FileDoesNotExistError, "#{file} does not exist." unless File.exists?(file)
|
40
|
+
|
41
|
+
case File.extname(file)
|
42
|
+
when '.json'
|
43
|
+
deep_merge! JSON.parse(IO.read(file))
|
44
|
+
when '.yml', '.yaml'
|
45
|
+
deep_merge! YAML.load_file(file)
|
46
|
+
else
|
47
|
+
instance_eval File.read(file), file
|
48
|
+
end
|
31
49
|
end
|
32
50
|
|
33
51
|
# Executed just before the DSL is evaluated
|
data/lib/cfer/cfn/client.rb
CHANGED
@@ -24,17 +24,6 @@ module Cfer::Cfn
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
def delete_stack(stack_name)
|
29
|
-
begin
|
30
|
-
@cfn.delete_stack({
|
31
|
-
stack_name: stack_name, # required
|
32
|
-
})
|
33
|
-
rescue Aws::CloudFormation::Errors
|
34
|
-
raise CferError, "Stack delete #{stack_name}"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
27
|
def responds_to?(method)
|
39
28
|
@cfn.responds_to? method
|
40
29
|
end
|
@@ -196,6 +185,9 @@ module Cfer::Cfn
|
|
196
185
|
q.unshift fetched_event
|
197
186
|
end
|
198
187
|
end
|
188
|
+
rescue Aws::CloudFormation::Errors::Throttling
|
189
|
+
Cfer::LOGGER.debug "AWS SDK is being throttled..."
|
190
|
+
# Keep going though.
|
199
191
|
rescue Aws::CloudFormation::Errors::ValidationError
|
200
192
|
running = false
|
201
193
|
end
|
data/lib/cfer/cli.rb
CHANGED
@@ -1,139 +1,148 @@
|
|
1
|
-
require '
|
1
|
+
require 'cri'
|
2
2
|
require 'rainbow'
|
3
3
|
require 'table_print'
|
4
4
|
|
5
5
|
module Cfer
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
desc: 'If parameter_file is set, will merge the subkey of this into the parameter list.'
|
26
|
-
end
|
6
|
+
module Cli
|
7
|
+
CFER_CLI = Cri::Command.define do
|
8
|
+
name 'cfer'
|
9
|
+
description 'Toolkit and Ruby DSL for automating infrastructure using AWS CloudFormation'
|
10
|
+
flag nil, 'verbose', 'Runs Cfer with debug output enabled'
|
11
|
+
|
12
|
+
optional :p, 'profile', 'The AWS profile to use from your credentials file'
|
13
|
+
optional :r, 'region', 'The AWS region to use'
|
14
|
+
|
15
|
+
optional nil, 'output-format', 'The output format to use when printing a stack [table|json]'
|
16
|
+
|
17
|
+
optional nil, 'parameter', 'Sets a parameter to pass into the stack (format: `name:value`)', multiple: true
|
18
|
+
optional nil, 'parameter-file', 'A YAML or JSON file with CloudFormation parameters to pass to the stack'
|
19
|
+
optional nil, 'parameter-environment', 'If parameter_file is set, will merge the subkey of this into the parameter list.'
|
20
|
+
|
21
|
+
flag :v, 'version', 'show the current version of cfer' do |value, cmd|
|
22
|
+
puts Cfer::VERSION
|
23
|
+
exit 0
|
24
|
+
end
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
default: 'table'
|
26
|
+
flag :h, 'help', 'show help for this command' do |value, cmd|
|
27
|
+
puts cmd.help
|
28
|
+
exit 0
|
29
|
+
end
|
33
30
|
end
|
34
31
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
method_option :timeout,
|
66
|
-
type: :numeric,
|
67
|
-
desc: 'The timeout (in minutes) before the stack operation aborts'
|
68
|
-
method_option :s3_path,
|
69
|
-
type: :string,
|
70
|
-
desc: 'Specifies an S3 path in case the stack is created with a URL.'
|
71
|
-
method_option :force_s3,
|
72
|
-
type: :boolean,
|
73
|
-
default: false,
|
74
|
-
desc: 'Forces Cfer to upload the template to S3 and pass CloudFormation a URL.'
|
75
|
-
method_option :change,
|
76
|
-
type: :string,
|
77
|
-
desc: 'Issues updates as a Cfn change set.'
|
78
|
-
method_option :change_description,
|
79
|
-
type: :string,
|
80
|
-
desc: 'The description of this Cfn change'
|
81
|
-
|
82
|
-
template_options
|
83
|
-
stack_options
|
84
|
-
def converge(stack_name)
|
85
|
-
Cfer.converge! stack_name, options
|
32
|
+
CFER_CLI.define_command do
|
33
|
+
name 'converge'
|
34
|
+
usage 'converge [OPTIONS] <stack-name> [param=value ...]'
|
35
|
+
summary 'Create or update a cloudformation stack according to the template'
|
36
|
+
|
37
|
+
optional :t, 'template', 'Override the stack filename (defaults to <stack-name>.rb)'
|
38
|
+
optional nil, 'on-failure', 'The action to take if the stack creation fails'
|
39
|
+
optional nil, 'timeout', 'The timeout (in minutes) before the stack operation aborts'
|
40
|
+
#flag nil, 'git-lock', 'When enabled, Cfer will not converge a stack in a dirty git tree'
|
41
|
+
|
42
|
+
optional :s, 'stack-policy', 'Set a new stack policy on create or update of the stack [file|url|json]'
|
43
|
+
optional :u, 'stack-policy-during-update', 'Set a temporary overriding stack policy during an update [file|url|json]'
|
44
|
+
|
45
|
+
optional nil, 'change', 'Issues updates as a Cfn change set.'
|
46
|
+
optional nil, 'change-description', 'The description of this Cfn change'
|
47
|
+
|
48
|
+
optional nil, 's3-path', 'Specifies an S3 path in case the stack is created with a URL.'
|
49
|
+
flag nil, 'force-s3', 'Forces Cfer to upload the template to S3 and pass CloudFormation a URL.'
|
50
|
+
|
51
|
+
run do |options, args, cmd|
|
52
|
+
Cfer::Cli.fixup_options(options)
|
53
|
+
params = {}
|
54
|
+
options[:number] = 0
|
55
|
+
options[:follow] = true
|
56
|
+
#options[:git_lock] = true if options[:git_lock].nil?
|
57
|
+
|
58
|
+
Cfer::Cli.extract_parameters(params, args).each do |arg|
|
59
|
+
Cfer.converge! arg, options.merge(parameters: params)
|
60
|
+
end
|
61
|
+
end
|
86
62
|
end
|
87
63
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
Cfer
|
64
|
+
CFER_CLI.define_command do
|
65
|
+
name 'generate'
|
66
|
+
usage 'generate [OPTIONS] <template.rb> [param=value ...]'
|
67
|
+
summary 'Generates a CloudFormation template by evaluating a Cfer template'
|
68
|
+
|
69
|
+
flag nil, 'minified', 'Minifies the JSON when printing output.'
|
70
|
+
|
71
|
+
run do |options, args, cmd|
|
72
|
+
Cfer::Cli.fixup_options(options)
|
73
|
+
params = {}
|
74
|
+
options[:pretty_print] = !options[:minified]
|
75
|
+
|
76
|
+
Cfer::Cli.extract_parameters(params, args).each do |arg|
|
77
|
+
Cfer.generate! arg, options.merge(parameters: params)
|
78
|
+
end
|
79
|
+
end
|
92
80
|
end
|
93
81
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
82
|
+
CFER_CLI.define_command do
|
83
|
+
name 'tail'
|
84
|
+
usage 'tail <stack>'
|
85
|
+
summary 'Follows stack events on standard output as they occur'
|
86
|
+
|
87
|
+
flag :f, 'follow', 'Follow stack events on standard output while the changes are made.'
|
88
|
+
optional :n, 'number', 'Prints the last (n) stack events.', type: :number
|
89
|
+
|
90
|
+
run do |options, args, cmd|
|
91
|
+
Cfer::Cli.fixup_options(options)
|
92
|
+
args.each do |arg|
|
93
|
+
Cfer.tail! arg, options
|
94
|
+
end
|
95
|
+
end
|
98
96
|
end
|
99
97
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
stack_options
|
112
|
-
def tail(stack_name)
|
113
|
-
Cfer.tail! stack_name, options
|
98
|
+
CFER_CLI.define_command do
|
99
|
+
name 'estimate'
|
100
|
+
usage 'estimate [OPTIONS] <template.rb>'
|
101
|
+
summary 'Prints a link to the Amazon cost caculator estimating the cost of the resulting CloudFormation stack'
|
102
|
+
|
103
|
+
run do |options, args, cmd|
|
104
|
+
Cfer::Cli.fixup_options(options)
|
105
|
+
args.each do |arg|
|
106
|
+
Cfer.estimate! arg, options
|
107
|
+
end
|
108
|
+
end
|
114
109
|
end
|
115
110
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
111
|
+
CFER_CLI.define_command do
|
112
|
+
name 'describe'
|
113
|
+
usage 'describe <stack>'
|
114
|
+
summary 'Fetches and prints information about a CloudFormation'
|
115
|
+
|
116
|
+
run do |options, args, cmd|
|
117
|
+
Cfer::Cli.fixup_options(options)
|
118
|
+
options[:pretty_print] ||= true
|
119
|
+
args.each do |arg|
|
120
|
+
Cfer.describe! arg, options
|
121
|
+
end
|
122
|
+
end
|
123
123
|
end
|
124
124
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
125
|
+
CFER_CLI.define_command do
|
126
|
+
name 'delete'
|
127
|
+
usage 'delete <stack>'
|
128
|
+
summary 'Deletes a CloudFormation stack'
|
129
|
+
|
130
|
+
run do |options, args, cmd|
|
131
|
+
Cfer::Cli.fixup_options(options)
|
132
|
+
options[:number] = 0
|
133
|
+
options[:follow] = true
|
134
|
+
args.each do |arg|
|
135
|
+
Cfer.delete! arg, options
|
136
|
+
end
|
137
|
+
end
|
131
138
|
end
|
132
139
|
|
140
|
+
CFER_CLI.add_command Cri::Command.new_basic_help
|
141
|
+
|
133
142
|
def self.main(args)
|
134
143
|
Cfer::LOGGER.debug "Cfer version #{Cfer::VERSION}"
|
135
144
|
begin
|
136
|
-
|
145
|
+
CFER_CLI.run(args)
|
137
146
|
rescue Aws::Errors::NoSuchProfileError => e
|
138
147
|
Cfer::LOGGER.error "#{e.message}. Specify a valid profile with the --profile option."
|
139
148
|
exit 1
|
@@ -162,16 +171,31 @@ module Cfer
|
|
162
171
|
end
|
163
172
|
end
|
164
173
|
|
165
|
-
|
166
|
-
def
|
167
|
-
|
174
|
+
PARAM_REGEX=/(?<name>.+?)=(?<value>.+)/
|
175
|
+
def self.extract_parameters(params, args)
|
176
|
+
args.reject do |arg|
|
177
|
+
if match = PARAM_REGEX.match(arg)
|
178
|
+
name = match[:name]
|
179
|
+
value = match[:value]
|
180
|
+
Cfer::LOGGER.debug "Extracting parameter #{name}: #{value}"
|
181
|
+
params[name] = value
|
182
|
+
end
|
183
|
+
end
|
168
184
|
end
|
169
185
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
186
|
+
# Convert options of the form `:'some-option'` into `:some_option`.
|
187
|
+
# Cfer internally uses the latter format, while Cri options must be specified as the former.
|
188
|
+
# This approach is better than changing the names of all the options in the CLI.
|
189
|
+
def self.fixup_options(opts)
|
190
|
+
opts.keys.map(&:to_s).each do |k|
|
191
|
+
old_k = k.to_sym
|
192
|
+
new_k = k.gsub('-', '_').to_sym
|
193
|
+
val = opts[old_k]
|
194
|
+
opts[new_k] = (Integer(val) rescue Float(val) rescue val)
|
195
|
+
opts.delete(old_k) if old_k != new_k
|
196
|
+
end
|
174
197
|
end
|
198
|
+
|
175
199
|
private
|
176
200
|
def self.format_backtrace(bt)
|
177
201
|
"Backtrace: #{bt.join("\n from ")}"
|
data/lib/cfer/config.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Cfer
|
2
|
+
class Config < BlockHash
|
3
|
+
def initialize(file = nil, options = nil, &block)
|
4
|
+
@config_file = file
|
5
|
+
deep_merge! options if options
|
6
|
+
instance_eval &block if block
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(method_sym, *arguments, &block)
|
10
|
+
key = camelize_property(method_sym)
|
11
|
+
case arguments.size
|
12
|
+
when 0
|
13
|
+
if block
|
14
|
+
Config.new(@config_file, nil, &block)
|
15
|
+
else
|
16
|
+
val = self[key]
|
17
|
+
val =
|
18
|
+
case val
|
19
|
+
when Hash, nil
|
20
|
+
Config.new(nil, val)
|
21
|
+
else
|
22
|
+
val
|
23
|
+
end
|
24
|
+
properties key => val
|
25
|
+
val
|
26
|
+
end
|
27
|
+
else
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Includes config code from one or more files, and evals it in the context of this stack.
|
33
|
+
# Filenames are relative to the file containing the invocation of this method.
|
34
|
+
def include_config(*files)
|
35
|
+
include_base = File.dirname(@config_file) if @config_file
|
36
|
+
files.each do |file|
|
37
|
+
path = File.join(include_base, file) if include_base
|
38
|
+
include_file(path || file)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def non_proxied_methods
|
44
|
+
[]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/cfer/console.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "pry"
|
2
|
+
require "pry-rescue"
|
3
|
+
|
4
|
+
module Cfer
|
5
|
+
DEBUG = true
|
6
|
+
end
|
7
|
+
|
8
|
+
require "cfer"
|
9
|
+
|
10
|
+
Cfer::LOGGER.level = Logger::DEBUG
|
11
|
+
|
12
|
+
Cfer::LOGGER.fatal "Showing FATAL logs"
|
13
|
+
Cfer::LOGGER.error "Showing ERROR logs"
|
14
|
+
Cfer::LOGGER.warn "Showing WARN logs"
|
15
|
+
Cfer::LOGGER.info "Showing INFO logs"
|
16
|
+
Cfer::LOGGER.debug "Showing DEBUG logs"
|
17
|
+
|
18
|
+
def cfer(*args)
|
19
|
+
Cfer::Cli::main(args)
|
20
|
+
end
|
21
|
+
|
data/lib/cfer/core/stack.rb
CHANGED
@@ -169,11 +169,7 @@ module Cfer::Core
|
|
169
169
|
include_base = options[:include_base] || File.dirname(caller.first.split(/:\d/,2).first)
|
170
170
|
files.each do |file|
|
171
171
|
path = File.join(include_base, file)
|
172
|
-
|
173
|
-
self.deep_merge! JSON.parse(File.read(path))
|
174
|
-
else
|
175
|
-
instance_eval(File.read(path), path)
|
176
|
-
end
|
172
|
+
include_file(path)
|
177
173
|
end
|
178
174
|
end
|
179
175
|
|
data/lib/cfer/util/error.rb
CHANGED
data/lib/cfer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.0.pre.
|
4
|
+
version: 0.5.0.pre.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Edwards
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docile
|
@@ -25,19 +25,19 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: cri
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: '2.7'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: '2.7'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: activesupport
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -229,6 +229,8 @@ files:
|
|
229
229
|
- lib/cfer/cfn/cfer_credentials_provider.rb
|
230
230
|
- lib/cfer/cfn/client.rb
|
231
231
|
- lib/cfer/cli.rb
|
232
|
+
- lib/cfer/config.rb
|
233
|
+
- lib/cfer/console.rb
|
232
234
|
- lib/cfer/core/client.rb
|
233
235
|
- lib/cfer/core/functions.rb
|
234
236
|
- lib/cfer/core/hooks.rb
|