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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d9d002adc64d106f800cb61545ae2a62644cdf6
4
- data.tar.gz: e6c08b6a6f4d011d23f2b0567f65febb1ebf1a8b
3
+ metadata.gz: '03594ef49c992c1131effb85d44014b6fabc7356'
4
+ data.tar.gz: 10026ae4c9616d6ba4cfb61068c1861211dd680f
5
5
  SHA512:
6
- metadata.gz: 6eb2bd6ddf3bc719faaeb11361662bbd7ac938b5ac2d4e37583e144b6cbdf7a59aa43c585f48f1117d2ace34be4a85d9f38a5b237f338231d84e03b9ce75aac5
7
- data.tar.gz: d342f24e5c9329cee1088aed994555be198efea702c063e6a62ef5f5b20843c7319c6c055fe92dc6f00ba821694545e3420507ece940e083d1fc25009ad07431
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] --parameters KeyName:[YOUR-EC2-SSH-KEY]
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
- Commands:
54
- cfer converge [OPTIONS] <stack-name> # Creates or updates a cloudformation stack according to the template
55
- cfer generate [OPTIONS] <template.rb> # Generates a CloudFormation template by evaluating a Cfer template
56
- cfer help [COMMAND] # Describe available commands or one specific command
57
- cfer tail <stack-name> # Follows stack events on standard output as they occur
58
-
59
- #### Global options
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 'pry'
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 "cfer"
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 'thor', '~> 0.19.1'
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
- stack = options[:cfer_stack] ||
66
- Cfer::stack_from_file(tmpl,
67
- options.merge(
68
- client: cfn_stack,
69
- parameters: generate_final_parameters(options)
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
- describe! stack_name, options rescue nil # It's fine if we can't do this.
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["pre"] unless cfer_version["pre"].nil?
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
- if file.ends_with?('.json')
213
- s.deep_merge! JSON.parse(File.read(file))
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
- file_params =
249
- if options[:parameter_file]
250
- case File.extname(options[:parameter_file])
251
- when '.yaml'
252
- require 'yaml'
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 file_params.key?(options[:parameter_environment])
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
- file_params = file_params.deep_merge(file_params[options[:parameter_environment]])
262
+ final_params.deep_merge!(final_params[options[:parameter_environment]])
268
263
  end
269
264
 
270
- file_params.deep_merge(options[:parameters] || {})
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
- color: :yellow
303
- },
304
- 'DELETE_IN_PROGRESS' => {
305
- color: :yellow
306
- },
307
- 'UPDATE_IN_PROGRESS' => {
308
- color: :green
309
- },
310
-
311
- 'CREATE_FAILED' => {
312
- color: :red,
313
- finished: true
314
- },
315
- 'DELETE_FAILED' => {
316
- color: :red,
317
- finished: true
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/*.rb"].each { |f| require(f) }
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
- build_from_string File.read(file), file
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
@@ -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 'thor'
1
+ require 'cri'
2
2
  require 'rainbow'
3
3
  require 'table_print'
4
4
 
5
5
  module Cfer
6
- class Cli < Thor
7
- map '-v' => :version, '--version' => :version
8
-
9
- namespace 'cfer'
10
- class_option :verbose, type: :boolean, default: false
11
- class_option :profile, type: :string, aliases: :p, desc: 'The AWS profile to use from your credentials file'
12
- class_option :region, type: :string, aliases: :r, desc: 'The AWS region to use'
13
- class_option :pretty_print, type: :boolean, default: :true, desc: 'Render JSON in a more human-friendly format'
14
-
15
- def self.template_options
16
- method_option :parameters,
17
- type: :hash,
18
- desc: 'The CloudFormation parameters to pass to the stack',
19
- default: {}
20
- method_option :parameter_file,
21
- type: :string,
22
- desc: 'A YAML or JSON file with CloudFormation parameters to pass to the stack'
23
- method_option :parameter_environment,
24
- type: :string,
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
- def self.stack_options
29
- method_option :output_format,
30
- type: :string,
31
- desc: 'The output format of the stack [table|json]',
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
- desc 'converge [OPTIONS] <stack-name>', 'Create or update a cloudformation stack according to the template'
36
- #method_option :git_lock,
37
- # type: :boolean,
38
- # default: true,
39
- # desc: 'When enabled, Cfer will not converge a stack in a dirty git tree'
40
-
41
- method_option :on_failure,
42
- type: :string,
43
- desc: 'The action to take if the stack creation fails'
44
- method_option :follow,
45
- aliases: :f,
46
- type: :boolean,
47
- default: true,
48
- desc: 'Follow stack events on standard output while the changes are made.'
49
- method_option :number,
50
- type: :numeric,
51
- default: 1,
52
- desc: 'Prints the last (n) stack events.'
53
- method_option :template,
54
- aliases: :t,
55
- type: :string,
56
- desc: 'Override the stack filename (defaults to <stack-name>.rb)'
57
- method_option :stack_policy,
58
- aliases: :s,
59
- type: :string,
60
- desc: 'Set a new stack policy on create or update of the stack [file|url|json]'
61
- method_option :stack_policy_during_update,
62
- aliases: :u,
63
- type: :string,
64
- desc: 'Set a temporary overriding stack policy during an update [file|url|json]'
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
- desc 'describe <stack>', 'Fetches and prints information about a CloudFormation'
89
- stack_options
90
- def describe(stack_name)
91
- Cfer.describe! stack_name, options
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
- desc 'delete <stack>', 'Deletes a CloudFormation stack'
95
- stack_options
96
- def delete(stack_name)
97
- Cfer.delete! stack_name, options
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
- desc 'tail <stack>', 'Follows stack events on standard output as they occur'
101
- method_option :follow,
102
- aliases: :f,
103
- type: :boolean,
104
- default: false,
105
- desc: 'Follow stack events on standard output while the changes are made.'
106
- method_option :number,
107
- aliases: :n,
108
- type: :numeric,
109
- default: 10,
110
- desc: 'Prints the last (n) stack events.'
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
- desc 'generate [OPTIONS] <template.rb>', 'Generates a CloudFormation template by evaluating a Cfer template'
117
- long_desc <<-LONGDESC
118
- Generates a CloudFormation template by evaluating a Cfer template.
119
- LONGDESC
120
- template_options
121
- def generate(tmpl)
122
- Cfer.generate! tmpl, options
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
- desc 'estimate [OPTIONS] <template.rb>', 'Prints a link to the Amazon cost caculator estimating the cost of the resulting CloudFormation stack'
126
- long_desc <<-LONGDESC
127
- LONGDESC
128
- template_options
129
- def estimate(tmpl)
130
- Cfer.estimate! tmpl, options
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
- Cli.start(args)
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
- desc 'version', 'Prints the current version of Cfer'
166
- def version
167
- puts Cfer::VERSION
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
- private
171
-
172
- def cfn(opts = {})
173
- @cfn ||= opts
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 ")}"
@@ -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
@@ -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
+
@@ -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
- if path.ends_with?('.json')
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
 
@@ -17,6 +17,9 @@ module Cfer::Util
17
17
  class StackDoesNotExistError < CferError
18
18
  end
19
19
 
20
+ class FileDoesNotExistError < CferError
21
+ end
22
+
20
23
  class TemplateError < CferError
21
24
  attr_reader :template_backtrace
22
25
 
data/lib/cfer/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Cfer
2
- VERSION = "0.5.0-rc1"
2
+ VERSION = "0.5.0-rc2"
3
3
 
4
4
  begin
5
5
  require 'semantic'
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.rc1
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-08-29 00:00:00.000000000 Z
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: thor
28
+ name: cri
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.19.1
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: 0.19.1
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