lib/cfer/cli.rb in cfer-0.5.0.pre.rc1 vs lib/cfer/cli.rb in cfer-0.5.0.pre.rc2
- old
+ new
@@ -1,141 +1,150 @@
-require 'thor'
+require 'cri'
require 'rainbow'
require 'table_print'
module Cfer
- class Cli < Thor
- map '-v' => :version, '--version' => :version
+ module Cli
+ CFER_CLI = Cri::Command.define do
+ name 'cfer'
+ description 'Toolkit and Ruby DSL for automating infrastructure using AWS CloudFormation'
+ flag nil, 'verbose', 'Runs Cfer with debug output enabled'
- namespace 'cfer'
- class_option :verbose, type: :boolean, default: false
- class_option :profile, type: :string, aliases: :p, desc: 'The AWS profile to use from your credentials file'
- class_option :region, type: :string, aliases: :r, desc: 'The AWS region to use'
- class_option :pretty_print, type: :boolean, default: :true, desc: 'Render JSON in a more human-friendly format'
+ optional :p, 'profile', 'The AWS profile to use from your credentials file'
+ optional :r, 'region', 'The AWS region to use'
- def self.template_options
- method_option :parameters,
- type: :hash,
- desc: 'The CloudFormation parameters to pass to the stack',
- default: {}
- method_option :parameter_file,
- type: :string,
- desc: 'A YAML or JSON file with CloudFormation parameters to pass to the stack'
- method_option :parameter_environment,
- type: :string,
- desc: 'If parameter_file is set, will merge the subkey of this into the parameter list.'
- end
+ optional nil, 'output-format', 'The output format to use when printing a stack [table|json]'
- def self.stack_options
- method_option :output_format,
- type: :string,
- desc: 'The output format of the stack [table|json]',
- default: 'table'
+ optional nil, 'parameter', 'Sets a parameter to pass into the stack (format: `name:value`)', multiple: true
+ optional nil, 'parameter-file', 'A YAML or JSON file with CloudFormation parameters to pass to the stack'
+ optional nil, 'parameter-environment', 'If parameter_file is set, will merge the subkey of this into the parameter list.'
+
+ flag :v, 'version', 'show the current version of cfer' do |value, cmd|
+ puts Cfer::VERSION
+ exit 0
+ end
+
+ flag :h, 'help', 'show help for this command' do |value, cmd|
+ puts cmd.help
+ exit 0
+ end
end
- desc 'converge [OPTIONS] <stack-name>', 'Create or update a cloudformation stack according to the template'
- #method_option :git_lock,
- # type: :boolean,
- # default: true,
- # desc: 'When enabled, Cfer will not converge a stack in a dirty git tree'
+ CFER_CLI.define_command do
+ name 'converge'
+ usage 'converge [OPTIONS] <stack-name> [param=value ...]'
+ summary 'Create or update a cloudformation stack according to the template'
- method_option :on_failure,
- type: :string,
- desc: 'The action to take if the stack creation fails'
- method_option :follow,
- aliases: :f,
- type: :boolean,
- default: true,
- desc: 'Follow stack events on standard output while the changes are made.'
- method_option :number,
- type: :numeric,
- default: 1,
- desc: 'Prints the last (n) stack events.'
- method_option :template,
- aliases: :t,
- type: :string,
- desc: 'Override the stack filename (defaults to <stack-name>.rb)'
- method_option :stack_policy,
- aliases: :s,
- type: :string,
- desc: 'Set a new stack policy on create or update of the stack [file|url|json]'
- method_option :stack_policy_during_update,
- aliases: :u,
- type: :string,
- desc: 'Set a temporary overriding stack policy during an update [file|url|json]'
- method_option :timeout,
- type: :numeric,
- desc: 'The timeout (in minutes) before the stack operation aborts'
- method_option :s3_path,
- type: :string,
- desc: 'Specifies an S3 path in case the stack is created with a URL.'
- method_option :force_s3,
- type: :boolean,
- default: false,
- desc: 'Forces Cfer to upload the template to S3 and pass CloudFormation a URL.'
- method_option :change,
- type: :string,
- desc: 'Issues updates as a Cfn change set.'
- method_option :change_description,
- type: :string,
- desc: 'The description of this Cfn change'
+ optional :t, 'template', 'Override the stack filename (defaults to <stack-name>.rb)'
+ optional nil, 'on-failure', 'The action to take if the stack creation fails'
+ optional nil, 'timeout', 'The timeout (in minutes) before the stack operation aborts'
+ #flag nil, 'git-lock', 'When enabled, Cfer will not converge a stack in a dirty git tree'
- template_options
- stack_options
- def converge(stack_name)
- Cfer.converge! stack_name, options
+ optional :s, 'stack-policy', 'Set a new stack policy on create or update of the stack [file|url|json]'
+ optional :u, 'stack-policy-during-update', 'Set a temporary overriding stack policy during an update [file|url|json]'
+
+ optional nil, 'change', 'Issues updates as a Cfn change set.'
+ optional nil, 'change-description', 'The description of this Cfn change'
+
+ optional nil, 's3-path', 'Specifies an S3 path in case the stack is created with a URL.'
+ flag nil, 'force-s3', 'Forces Cfer to upload the template to S3 and pass CloudFormation a URL.'
+
+ run do |options, args, cmd|
+ Cfer::Cli.fixup_options(options)
+ params = {}
+ options[:number] = 0
+ options[:follow] = true
+ #options[:git_lock] = true if options[:git_lock].nil?
+
+ Cfer::Cli.extract_parameters(params, args).each do |arg|
+ Cfer.converge! arg, options.merge(parameters: params)
+ end
+ end
end
- desc 'describe <stack>', 'Fetches and prints information about a CloudFormation'
- stack_options
- def describe(stack_name)
- Cfer.describe! stack_name, options
+ CFER_CLI.define_command do
+ name 'generate'
+ usage 'generate [OPTIONS] <template.rb> [param=value ...]'
+ summary 'Generates a CloudFormation template by evaluating a Cfer template'
+
+ flag nil, 'minified', 'Minifies the JSON when printing output.'
+
+ run do |options, args, cmd|
+ Cfer::Cli.fixup_options(options)
+ params = {}
+ options[:pretty_print] = !options[:minified]
+
+ Cfer::Cli.extract_parameters(params, args).each do |arg|
+ Cfer.generate! arg, options.merge(parameters: params)
+ end
+ end
end
- desc 'delete <stack>', 'Deletes a CloudFormation stack'
- stack_options
- def delete(stack_name)
- Cfer.delete! stack_name, options
+ CFER_CLI.define_command do
+ name 'tail'
+ usage 'tail <stack>'
+ summary 'Follows stack events on standard output as they occur'
+
+ flag :f, 'follow', 'Follow stack events on standard output while the changes are made.'
+ optional :n, 'number', 'Prints the last (n) stack events.', type: :number
+
+ run do |options, args, cmd|
+ Cfer::Cli.fixup_options(options)
+ args.each do |arg|
+ Cfer.tail! arg, options
+ end
+ end
end
- desc 'tail <stack>', 'Follows stack events on standard output as they occur'
- method_option :follow,
- aliases: :f,
- type: :boolean,
- default: false,
- desc: 'Follow stack events on standard output while the changes are made.'
- method_option :number,
- aliases: :n,
- type: :numeric,
- default: 10,
- desc: 'Prints the last (n) stack events.'
- stack_options
- def tail(stack_name)
- Cfer.tail! stack_name, options
+ CFER_CLI.define_command do
+ name 'estimate'
+ usage 'estimate [OPTIONS] <template.rb>'
+ summary 'Prints a link to the Amazon cost caculator estimating the cost of the resulting CloudFormation stack'
+
+ run do |options, args, cmd|
+ Cfer::Cli.fixup_options(options)
+ args.each do |arg|
+ Cfer.estimate! arg, options
+ end
+ end
end
- desc 'generate [OPTIONS] <template.rb>', 'Generates a CloudFormation template by evaluating a Cfer template'
- long_desc <<-LONGDESC
- Generates a CloudFormation template by evaluating a Cfer template.
- LONGDESC
- template_options
- def generate(tmpl)
- Cfer.generate! tmpl, options
+ CFER_CLI.define_command do
+ name 'describe'
+ usage 'describe <stack>'
+ summary 'Fetches and prints information about a CloudFormation'
+
+ run do |options, args, cmd|
+ Cfer::Cli.fixup_options(options)
+ options[:pretty_print] ||= true
+ args.each do |arg|
+ Cfer.describe! arg, options
+ end
+ end
end
- desc 'estimate [OPTIONS] <template.rb>', 'Prints a link to the Amazon cost caculator estimating the cost of the resulting CloudFormation stack'
- long_desc <<-LONGDESC
- LONGDESC
- template_options
- def estimate(tmpl)
- Cfer.estimate! tmpl, options
+ CFER_CLI.define_command do
+ name 'delete'
+ usage 'delete <stack>'
+ summary 'Deletes a CloudFormation stack'
+
+ run do |options, args, cmd|
+ Cfer::Cli.fixup_options(options)
+ options[:number] = 0
+ options[:follow] = true
+ args.each do |arg|
+ Cfer.delete! arg, options
+ end
+ end
end
+ CFER_CLI.add_command Cri::Command.new_basic_help
+
def self.main(args)
Cfer::LOGGER.debug "Cfer version #{Cfer::VERSION}"
begin
- Cli.start(args)
+ CFER_CLI.run(args)
rescue Aws::Errors::NoSuchProfileError => e
Cfer::LOGGER.error "#{e.message}. Specify a valid profile with the --profile option."
exit 1
rescue Aws::Errors::MissingRegionError => e
Cfer::LOGGER.error "Missing region. Specify a valid AWS region with the --region option, or use the AWS_REGION environment variable."
@@ -160,19 +169,34 @@
end
exit 1
end
end
- desc 'version', 'Prints the current version of Cfer'
- def version
- puts Cfer::VERSION
+ PARAM_REGEX=/(?<name>.+?)=(?<value>.+)/
+ def self.extract_parameters(params, args)
+ args.reject do |arg|
+ if match = PARAM_REGEX.match(arg)
+ name = match[:name]
+ value = match[:value]
+ Cfer::LOGGER.debug "Extracting parameter #{name}: #{value}"
+ params[name] = value
+ end
+ end
end
- private
-
- def cfn(opts = {})
- @cfn ||= opts
+ # Convert options of the form `:'some-option'` into `:some_option`.
+ # Cfer internally uses the latter format, while Cri options must be specified as the former.
+ # This approach is better than changing the names of all the options in the CLI.
+ def self.fixup_options(opts)
+ opts.keys.map(&:to_s).each do |k|
+ old_k = k.to_sym
+ new_k = k.gsub('-', '_').to_sym
+ val = opts[old_k]
+ opts[new_k] = (Integer(val) rescue Float(val) rescue val)
+ opts.delete(old_k) if old_k != new_k
+ end
end
+
private
def self.format_backtrace(bt)
"Backtrace: #{bt.join("\n from ")}"
end
def self.exit_on_failure?