class CFnDK::Stack
Attributes
capabilities[R]
depends[R]
enabled[R]
package[R]
parameter_input[R]
post_command[R]
pre_command[R]
region[R]
role_arn[R]
template_file[R]
timeout_in_minutes[R]
Public Class Methods
new(name, data, option, global_config, credentials)
click to toggle source
# File lib/cfndk/stack.rb, line 4 def initialize(name, data, option, global_config, credentials) @global_config = global_config @name = name @template_file = data['template_file'] || '' @parameter_input = data['parameter_input'] || '' @capabilities = data['capabilities'] || [] @depends = data['depends'] || [] @region = data['region'] || @global_config.region @role_arn = @global_config.role_arn @package = data['package'] || @global_config.package @pre_command = data['pre_command'] || nil @post_command = data['post_command'] || nil @enabled = true @enabled = false if data['enabled'] === false @timeout_in_minutes = data['timeout_in_minutes'] || @global_config.timeout_in_minutes @override_parameters = data['parameters'] || {} @option = option @client = Aws::CloudFormation::Client.new(credentials: credentials, region: @region) @s3_client = Aws::S3::Client.new(credentials: credentials, region: @region) @sts_client = Aws::STS::Client.new(credentials: credentials, region: @region) @tp = CFnDK::TemplatePackager.new(@template_file, @region, @package, @global_config, @s3_client, @sts_client) end
Public Instance Methods
available_change_set?()
click to toggle source
# File lib/cfndk/stack.rb, line 382 def available_change_set? resp = @client.describe_change_set( change_set_name: change_set_name, stack_name: name ) return true if resp.execution_status == 'AVAILABLE' false rescue Aws::CloudFormation::Errors::ChangeSetNotFound false end
change_set_name()
click to toggle source
# File lib/cfndk/stack.rb, line 503 def change_set_name [@name, @option[:change_set_uuid]].compact.join('-') end
create()
click to toggle source
# File lib/cfndk/stack.rb, line 27 def create return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled CFnDK.logger.info(('creating stack: ' + name).color(:green)) CFnDK.logger.debug('Name :' + name) CFnDK.logger.debug('Parametres :' + parameters.inspect) CFnDK.logger.debug('Capabilities:' + capabilities.inspect) CFnDK.logger.debug('Timeout :' + timeout_in_minutes.to_s) CFnDK.logger.debug('Region :' + region) tags = [ { key: 'origina_name', value: @name, }, ] tags.push( key: 'UUID', value: @option[:uuid] ) if @option[:uuid] hash = { stack_name: name, parameters: parameters, capabilities: capabilities, timeout_in_minutes: timeout_in_minutes, tags: tags, } hash[:role_arn] = @role_arn if @role_arn if @tp.large_template? hash[:template_url] = @tp.upload_template_file() else hash[:template_body] = @tp.template_body() end @client.create_stack( hash ) end
create_change_set()
click to toggle source
# File lib/cfndk/stack.rb, line 170 def create_change_set return nil if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled CFnDK.logger.info(('creating change set: ' + change_set_name).color(:green)) CFnDK.logger.debug('Parametres :' + parameters.inspect) CFnDK.logger.debug('Capabilities:' + capabilities.inspect) CFnDK.logger.debug('Region :' + region) tags = [ { key: 'origina_name', value: @name, }, ] tags.push( key: 'UUID', value: @option[:uuid] ) if @option[:uuid] tags.push( key: 'CHANGE_SET_UUID', value: @option[:change_set_uuid] ) if @option[:change_set_uuid] hash = { stack_name: name, parameters: parameters, capabilities: capabilities, change_set_name: change_set_name, change_set_type: exits? ? 'UPDATE' : 'CREATE', tags: tags, } hash[:role_arn] = @role_arn if @role_arn if @tp.large_template? hash[:template_url] = @tp.upload_template_file() else hash[:template_body] = @tp.template_body() end @client.create_change_set( hash ) @name rescue Aws::CloudFormation::Errors::ValidationError => ex if review_in_progress? CFnDK.logger.warn("failed create change set because the stack on REVIEW_IN_PROGRESS already exist : #{change_set_name}".color(:orange)) nil else CFnDK.logger.error("failed create change set: #{change_set_name}".color(:red)) raise ex end end
created?()
click to toggle source
# File lib/cfndk/stack.rb, line 362 def created? resp = @client.describe_stacks( stack_name: name ) return false if resp.stacks[0].stack_status == 'REVIEW_IN_PROGRESS' true rescue Aws::CloudFormation::Errors::ValidationError false end
delete_change_set()
click to toggle source
# File lib/cfndk/stack.rb, line 262 def delete_change_set return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled CFnDK.logger.info(('deleting change set: ' + change_set_name).color(:green)) @client.delete_change_set( stack_name: name, change_set_name: change_set_name ) CFnDK.logger.info(('deleted change set: ' + change_set_name).color(:green)) end
destroy()
click to toggle source
# File lib/cfndk/stack.rb, line 136 def destroy return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled if exits? CFnDK.logger.info(('deleting stack: ' + name).color(:green)) CFnDK.logger.debug('Name :' + name) CFnDK.logger.debug('Region :' + region) hash = { stack_name: name, } hash[:role_arn] = @role_arn if @role_arn @client.delete_stack( hash ) else CFnDK.logger.info(('do not delete stack: ' + name).color(:red)) end end
execute_change_set()
click to toggle source
# File lib/cfndk/stack.rb, line 245 def execute_change_set return nil if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled if available_change_set? CFnDK.logger.info(('executing change set: ' + change_set_name).color(:green)) @client.execute_change_set( stack_name: name, change_set_name: change_set_name ) CFnDK.logger.info(('execute change set: ' + change_set_name).color(:green)) @name else CFnDK.logger.warn("failed execute change set because this change set is not AVAILABLE: #{change_set_name}".color(:orange)) nil end end
exits?()
click to toggle source
# File lib/cfndk/stack.rb, line 353 def exits? @client.describe_stacks( stack_name: name ) true rescue Aws::CloudFormation::Errors::ValidationError false end
name()
click to toggle source
# File lib/cfndk/stack.rb, line 499 def name [@name, @option[:uuid]].compact.join('-') end
parameters()
click to toggle source
# File lib/cfndk/stack.rb, line 507 def parameters json = JSON.load(open(@parameter_input).read) json['Parameters'].map do |item| next if item.empty? { parameter_key: item['ParameterKey'], parameter_value: eval_override_parameter(item['ParameterKey'], item['ParameterValue']), } end.compact end
post_command_execute()
click to toggle source
# File lib/cfndk/stack.rb, line 532 def post_command_execute return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled if @post_command CFnDK.logger.info(('execute post command: ' + @post_command).color(:green)) IO.popen(@post_command, :err => [:child, :out]) do |io| io.each_line do |line| CFnDK.logger.info((line).color(:green)) end end raise 'post command is error. status: ' + $?.exitstatus.to_s + ' command: ' + @post_command if $?.exitstatus != 0 end end
pre_command_execute()
click to toggle source
# File lib/cfndk/stack.rb, line 518 def pre_command_execute return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled if @pre_command CFnDK.logger.info(('execute pre command: ' + @pre_command).color(:green)) IO.popen(@pre_command, :err => [:child, :out]) do |io| io.each_line do |line| CFnDK.logger.info((line).color(:green)) end end raise 'pre command is error. status: ' + $?.exitstatus.to_s + ' command: ' + @pre_command if $?.exitstatus != 0 end end
report()
click to toggle source
# File lib/cfndk/stack.rb, line 393 def report return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled CFnDK.logger.info('*****************************************************'.color(:green)) CFnDK.logger.info(('stack: ' + name).color(:green)) CFnDK.logger.info('*****************************************************'.color(:green)) CFnDK.logger.info('') begin resp = @client.describe_stacks( stack_name: name ).stacks[0] CFnDK.logger.info('Status: '.color(:green) + colored_status(resp.stack_status)) CFnDK.logger.info('Reason: '.color(:green) + resp.stack_status_reason) if resp.stack_status_reason if @option[:types].instance_of?(Array) && @option[:types].include?('tag') CFnDK.logger.info('Tags:'.color(:green)) tags_rows = resp.tags.map do |item| [ item.key, item.value, ] end unless tags_rows.empty? table = Terminal::Table.new headings: %w(Key Value), rows: tags_rows CFnDK.logger.info table end end if @option[:types].instance_of?(Array) && @option[:types].include?('parameter') CFnDK.logger.info('Parameters:'.color(:green)) parameter_rows = resp.parameters.map do |item| [ item.parameter_key, item.parameter_value, item.use_previous_value, item.resolved_value, ] end unless parameter_rows.empty? table = Terminal::Table.new headings: ['Key', 'Value', 'Use Previous Value', 'Resolved Value'], rows: parameter_rows CFnDK.logger.info table end end if @option[:types].instance_of?(Array) && @option[:types].include?('output') CFnDK.logger.info('Outputs:'.color(:green)) output_rows = resp.outputs.map do |item| [ item.output_key, item.output_value, item.export_name, item.description, ] end unless output_rows.empty? table = Terminal::Table.new headings: ['Key', 'Value', 'Export Name', 'Description'], rows: output_rows CFnDK.logger.info table end end rescue Aws::CloudFormation::Errors::ValidationError => ex CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red) end if @option[:types].instance_of?(Array) && @option[:types].include?('resource') begin CFnDK.logger.info('Resources:'.color(:green)) rows = @client.describe_stack_resources( stack_name: name ).stack_resources.map do |item| [ item.logical_resource_id, item.physical_resource_id, item.resource_type, item.timestamp, colored_status(item.resource_status), item.resource_status_reason, item.description, ] end unless rows.empty? table = Terminal::Table.new headings: %w(Logical Physical Type Timestamp Status Reason Desc), rows: rows CFnDK.logger.info table end rescue Aws::CloudFormation::Errors::ValidationError => ex CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red) end end if @option[:types].instance_of?(Array) && @option[:types].include?('event') CFnDK.logger.info('Events:'.color(:green)) begin rows = @client.describe_stack_events( stack_name: name ).stack_events.map do |item| [ item.resource_type, item.timestamp, colored_status(item.resource_status), item.resource_status_reason, ] end unless rows.empty? table = Terminal::Table.new headings: %w(Type Time Status Reason), rows: rows CFnDK.logger.info table end rescue Aws::CloudFormation::Errors::ValidationError => ex CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red) end end end
report_change_set()
click to toggle source
# File lib/cfndk/stack.rb, line 273 def report_change_set return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled CFnDK.logger.info('*****************************************************'.color(:green)) CFnDK.logger.info(('change set: ' + change_set_name).color(:green)) CFnDK.logger.info('*****************************************************'.color(:green)) CFnDK.logger.info('') resp = @client.describe_change_set( change_set_name: change_set_name, stack_name: name ) CFnDK.logger.info('Execution Status: '.color(:green) + colored_status(resp.execution_status)) CFnDK.logger.info('Status: '.color(:green) + colored_status(resp.status)) CFnDK.logger.info('Reason: '.color(:green) + resp.status_reason) if resp.status_reason if @option[:types].instance_of?(Array) && @option[:types].include?('tag') CFnDK.logger.info('Tags:'.color(:green)) tags_rows = resp.tags.map do |item| [ item.key, item.value, ] end unless tags_rows.empty? table = Terminal::Table.new headings: %w(Key Value), rows: tags_rows CFnDK.logger.info table end end if @option[:types].instance_of?(Array) && @option[:types].include?('parameter') CFnDK.logger.info('Parameters:'.color(:green)) parameter_rows = resp.parameters.map do |item| [ item.parameter_key, item.parameter_value, item.use_previous_value, item.resolved_value, ] end unless parameter_rows.empty? table = Terminal::Table.new headings: ['Key', 'Value', 'Use Previous Value', 'Resolved Value'], rows: parameter_rows CFnDK.logger.info table end end if @option[:types].instance_of?(Array) && @option[:types].include?('changes') CFnDK.logger.info('Changes:'.color(:green)) changes_rows = resp.changes.map do |item| [ item.resource_change.action, item.resource_change.logical_resource_id, item.resource_change.physical_resource_id, item.resource_change.resource_type, item.resource_change.replacement, ] end unless changes_rows.empty? table = Terminal::Table.new headings: %w(Action Logical Physical Type Replacement), rows: changes_rows CFnDK.logger.info table end end rescue Aws::CloudFormation::Errors::ValidationError => ex CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red) rescue Aws::CloudFormation::Errors::ChangeSetNotFound => ex CFnDK.logger.warn "#{ex.class}: #{ex.message}".color(:red) end
review_in_progress?()
click to toggle source
# File lib/cfndk/stack.rb, line 372 def review_in_progress? resp = @client.describe_stacks( stack_name: name ) return true if resp.stacks[0].stack_status == 'REVIEW_IN_PROGRESS' false rescue Aws::CloudFormation::Errors::ValidationError false end
update()
click to toggle source
# File lib/cfndk/stack.rb, line 86 def update return false if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled CFnDK.logger.info(('updating stack: ' + name).color(:green)) CFnDK.logger.debug('Name :' + name) CFnDK.logger.debug('Parametres :' + parameters.inspect) CFnDK.logger.debug('Capabilities:' + capabilities.inspect) CFnDK.logger.debug('Timeout :' + timeout_in_minutes.to_s) CFnDK.logger.debug('Region :' + region) begin hash = { stack_name: name, parameters: parameters, capabilities: capabilities, } hash[:role_arn] = @role_arn if @role_arn if @tp.large_template? hash[:template_url] = @tp.upload_template_file() else hash[:template_body] = @tp.template_body() end @client.update_stack( hash ) true rescue Aws::CloudFormation::Errors::ValidationError => ex case ex.message when 'No updates are to be performed.' CFnDK.logger.warn "#{ex.message}: #{name}".color(:red) false else raise ex end end end
validate()
click to toggle source
# File lib/cfndk/stack.rb, line 337 def validate return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled CFnDK.logger.info(('validate stack: ' + name).color(:green)) CFnDK.logger.debug('Name :' + @name) hash = {} if @tp.large_template? hash[:template_url] = @tp.upload_template_file() else hash[:template_body] = @tp.template_body() end @client.validate_template( hash ) end
wait_until_create()
click to toggle source
# File lib/cfndk/stack.rb, line 65 def wait_until_create return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled CFnDK.logger.info(('waiting create stack: ' + name).color(:green)) begin @client.wait_until( :stack_create_complete, stack_name: name ) do |w| w.max_attempts = 360 w.delay = 10 end CFnDK.logger.info(('created stack: ' + name).color(:green)) rescue Aws::Waiters::Errors::FailureStateError => ex CFnDK.logger.error "#{ex.class}: #{ex.message}".color(:red) @option[:type] = %w(tag output parameter resource event) report raise ex end end
wait_until_create_change_set()
click to toggle source
# File lib/cfndk/stack.rb, line 219 def wait_until_create_change_set return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled return unless exits? CFnDK.logger.info(('waiting create change set: ' + change_set_name).color(:green)) @client.wait_until( :change_set_create_complete, stack_name: name, change_set_name: change_set_name ) do |w| w.max_attempts = 360 w.delay = 10 end CFnDK.logger.info("created change set: #{change_set_name}".color(:green)) rescue Aws::Waiters::Errors::FailureStateError => ex case ex.message when 'stopped waiting, encountered a failure state' unless available_change_set? delete_change_set CFnDK.logger.warn("failed create change set because this change set is UNAVAILABLE: #{change_set_name}".color(:orange)) return end end raise ex end
wait_until_destroy()
click to toggle source
# File lib/cfndk/stack.rb, line 155 def wait_until_destroy return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled return unless exits? CFnDK.logger.info(('waiting delete stack: ' + name).color(:green)) @client.wait_until( :stack_delete_complete, stack_name: name ) do |w| w.max_attempts = 360 w.delay = 10 end CFnDK.logger.info(('deleted stack: ' + name).color(:green)) end
wait_until_update()
click to toggle source
# File lib/cfndk/stack.rb, line 122 def wait_until_update return if @option[:stack_names].instance_of?(Array) && !@option[:stack_names].include?(@name) return unless @enabled CFnDK.logger.info(('waiting update stack: ' + name).color(:green)) @client.wait_until( :stack_update_complete, stack_name: name ) do |w| w.max_attempts = 360 w.delay = 10 end CFnDK.logger.info(('updated stack: ' + name).color(:green)) end
Private Instance Methods
colored_status(str)
click to toggle source
# File lib/cfndk/stack.rb, line 548 def colored_status(str) case str when 'CREATE_FAILED' then str.color :red when 'ROLLBACK_IN_PROGRESS' then str.color :red when 'ROLLBACK_COMPLETE' then str.color :red when 'CREATE_COMPLETE' then str.color :green when 'DELETE_COMPLETE' then str.color :gray else str.color :orange end end
eval_override_parameter(k, v)
click to toggle source
# File lib/cfndk/stack.rb, line 565 def eval_override_parameter(k, v) if @override_parameters[k] CFnDK::ErbString.new(@override_parameters[k], @option).value else v end end