class CfnFlow::CLI
Public Class Methods
exit_on_failure?()
click to toggle source
# File lib/cfn_flow/cli.rb, line 4 def self.exit_on_failure? CfnFlow.exit_on_failure? end
Public Instance Methods
delete(name)
click to toggle source
# File lib/cfn_flow/cli.rb, line 194 def delete(name) stack = find_stack_in_service(name) if options[:force] || yes?("Are you sure you want to shut down #{name}?", :red) stack.delete say "Deleted stack #{name}" say "Polling for events..." invoke :events, [stack.name], ['--poll'] end end
deploy(environment)
click to toggle source
# File lib/cfn_flow/cli.rb, line 54 def deploy(environment) # Export environment as an env var so it can be interpolated in config ENV['CFN_FLOW_ENVIRONMENT'] = environment begin params = CfnFlow.stack_params(environment) stack = CfnFlow.cfn_resource.create_stack(params) rescue Aws::CloudFormation::Errors::ValidationError => e raise Thor::Error.new(e.message) end say "Launching stack #{stack.name}" # Invoke events say "Polling for events..." invoke :events, [stack.name], ['--poll'] say "Stack Outputs:" invoke :show, [stack.name], ['--format=outputs-table'] # Optionally cleanup other stacks in this environment if options[:cleanup] puts "Finding stacks to clean up" list_stacks_in_service.select {|s| s.name != stack.name && \ s.tags.any? {|tag| tag.key == 'CfnFlowEnvironment' && tag.value == environment } }.map(&:name).each do |name| delete(name) end end end
deploy ENVIRONMENT()
click to toggle source
Stack methods
# File lib/cfn_flow/cli.rb, line 52 desc 'deploy ENVIRONMENT', 'Launch a stack'
events(name)
click to toggle source
# File lib/cfn_flow/cli.rb, line 171 def events(name) stack = find_stack_in_service(name) say EventPresenter.header unless options['no-header'] EventPresenter.present(stack.events) {|p| say p } if options[:poll] # Display events until we're COMPLETE/FAILED delay = (ENV['CFN_FLOW_EVENT_POLL_INTERVAL'] || 2).to_i begin stack.wait_until(max_attempts: -1, delay: delay) do |s| EventPresenter.present(s.events) {|p| say p } # Wait until the stack status ends with _FAILED or _COMPLETE s.stack_status.match(/_(FAILED|COMPLETE)$/) end rescue Aws::CloudFormation::Errors::ValidationError # The stack was deleted. Keep on trucking. end end end
list(environment=nil)
click to toggle source
# File lib/cfn_flow/cli.rb, line 123 def list(environment=nil) stacks = list_stacks_in_service if environment stacks.select! do |stack| stack.tags.any? {|tag| tag.key == 'CfnFlowEnvironment' && tag.value == environment } end end return if stacks.empty? table_header = options['no-header'] ? [] : [['NAME', 'ENVIRONMENT', 'STATUS', 'CREATED']] table_data = stacks.map do |s| env_tag = s.tags.detect {|tag| tag.key == 'CfnFlowEnvironment'} env = env_tag ? env_tag.value : 'NONE' [ s.name, env, s.stack_status, s.creation_time ] end print_table(table_header + table_data) end
publish(*templates)
click to toggle source
# File lib/cfn_flow/cli.rb, line 33 def publish(*templates) if templates.empty? raise Thor::RequiredArgumentMissingError.new('You must specify a template to publish') end validate(*templates) release = publish_release templates.each do |path| t = Template.new(path) say "Publishing #{t.local_path} to #{t.url(release)}" t.upload(release) end end
show(name)
click to toggle source
# File lib/cfn_flow/cli.rb, line 146 def show(name) formatters = { 'json' => ->(stack) { say MultiJson.dump(stack.data.to_hash, pretty: true) }, 'yaml' => ->(stack) { say stack.data.to_hash.to_yaml }, 'outputs-table' => ->(stack) do outputs = stack.outputs.to_a if outputs.any? table_header = [['KEY', 'VALUE', 'DESCRIPTION']] table_data = outputs.map do |s| [ s.output_key, s.output_value, s.description ] end print_table(table_header + table_data) else say "No stack outputs to show." end end } stack = find_stack_in_service(name) formatters[options[:format]].call(stack) end
update(environment, name)
click to toggle source
# File lib/cfn_flow/cli.rb, line 87 def update(environment, name) # Export environment as an env var so it can be interpolated in config ENV['CFN_FLOW_ENVIRONMENT'] = environment stack = find_stack_in_service(name) # Check that environment matches unless stack.tags.any?{|tag| tag.key == 'CfnFlowEnvironment' && tag.value == environment } raise Thor::Error.new "Stack #{name} is not tagged for environment #{environment}" end begin params = CfnFlow.stack_params(environment) params.delete(:tags) # No allowed for Stack#update stack.update(params) rescue Aws::CloudFormation::Errors::ValidationError => e raise Thor::Error.new(e.message) end say "Updating stack #{stack.name}" # NB: there's a potential race condition where polling for events would # see the last complete state before the stack has a chance to begin updating. # Consider putting a sleep, wait_for an UPDATE_IN_PROGRESS state beforehand, # or look for events newer than the last event before updating. # Invoke events say "Polling for events..." invoke :events, [stack.name], ['--poll'] say "Stack Outputs:" invoke :show, [stack.name], ['--format=outputs-table'] end
validate(*templates)
click to toggle source
# File lib/cfn_flow/cli.rb, line 12 def validate(*templates) if templates.empty? raise Thor::RequiredArgumentMissingError.new('You must specify a template to validate') end templates.map{|path| Template.new(path) }.each do |template| say "Validating #{template.local_path}... " template.validate! say 'valid.', :green end rescue Aws::CloudFormation::Errors::ValidationError => e raise Thor::Error.new("Invalid template. Message: #{e.message}") rescue CfnFlow::Template::Error => e raise Thor::Error.new("Error loading template. (#{e.class}) Message: #{e.message}") end
validate TEMPLATE [...]()
click to toggle source
Template
methods
# File lib/cfn_flow/cli.rb, line 11 desc 'validate TEMPLATE [...]', 'Validates templates'
version()
click to toggle source
Version command
# File lib/cfn_flow/cli.rb, line 207 desc "version", "Prints the version information"
Private Instance Methods
find_stack_in_service(name)
click to toggle source
# File lib/cfn_flow/cli.rb, line 214 def find_stack_in_service(name) stack = CfnFlow.cfn_resource.stack(name).load unless stack.tags.any? {|tag| tag.key == 'CfnFlowService' && tag.value == CfnFlow.service } raise Thor::Error.new "Stack #{name} is not tagged for service #{CfnFlow.service}" end stack rescue Aws::CloudFormation::Errors::ValidationError => e # Handle missing stacks: 'Stack with id blah does not exist' raise Thor::Error.new(e.message) end
list_stacks_in_service()
click to toggle source
# File lib/cfn_flow/cli.rb, line 225 def list_stacks_in_service CfnFlow.cfn_resource.stacks.select do |stack| stack.tags.any? {|tag| tag.key == 'CfnFlowService' && tag.value == CfnFlow.service } end end
publish_release()
click to toggle source
# File lib/cfn_flow/cli.rb, line 231 def publish_release # Add the release or dev name to the prefix if options[:release] release = options[:release] == true ? CfnFlow::Git.sha : options[:release] 'release/' + release elsif options['dev-name'] 'dev/' + options['dev-name'] elsif ENV['CFN_FLOW_DEV_NAME'] 'dev/' + ENV['CFN_FLOW_DEV_NAME'] else raise Thor::Error.new("Must specify --release or --dev-name; or set CFN_FLOW_DEV_NAME env var") end end