class Object

Constants

Version

Public Instance Methods

add_encryption_password(template) click to toggle source
# File lib/kumogata/client.rb, line 577
def add_encryption_password(template)
  if @options.encrypt_parameters? and not @options.skip_send_password?
    template['Parameters'] ||= {}

    template['Parameters'][Kumogata::ENCRYPTION_PASSWORD] = {
      'Type'   => 'String',
      'NoEcho' => 'true',
    }
  end
end
add_encryption_password_for_validation(template) click to toggle source
# File lib/kumogata/client.rb, line 588
def add_encryption_password_for_validation(template)
  template['Parameters'] ||= {}

  template['Parameters'][Kumogata::ENCRYPTION_PASSWORD] ||= {
    'Type' => 'String',
    'Default' => "(#{Kumogata::ENCRYPTION_PASSWORD})",
  }
end
add_parameters(hash) click to toggle source
# File lib/kumogata/client.rb, line 540
def add_parameters(hash)
  if @options.parameters? and not @options.parameters.empty?
    parameters = {}

    enc_params = @options.encrypt_parameters
    passwd = @options.encryption_password || Kumogata::Crypt.mkpasswd(16)

    @options.parameters.each do |key, value|
      if enc_params and (enc_params.include?('*') or enc_params.include?(key))
        value = Kumogata::Crypt.encrypt(passwd, value)
      end

      parameters[key] = value
    end

    if @options.encrypt_parameters? and not @options.skip_send_password?
      parameters[Kumogata::ENCRYPTION_PASSWORD] = passwd.encode64
    end

    hash[:parameters] = parameters
  end
end
build_create_options() click to toggle source
# File lib/kumogata/client.rb, line 517
def build_create_options
  opts = {}
  add_parameters(opts)

  [:capabilities, :disable_rollback, :notify, :timeout,
   :stack_policy_body, :stack_policy_url].each do |k|
    opts[k] = @options[k] if @options[k]
  end

  return opts
end
build_update_options(template) click to toggle source
# File lib/kumogata/client.rb, line 529
def build_update_options(template)
  opts = {:template => template}
  add_parameters(opts)

  [:capabilities, :stack_policy_body, :stack_policy_url].each do |k|
    opts[k] = @options[k] if @options[k]
  end

  return opts
end
create_event_log(stack) click to toggle source
# File lib/kumogata/client.rb, line 506
def create_event_log(stack)
  event_log = {}

  events_for(stack).sort_by {|i| i['Timestamp'] }.each do |event|
    event_id = event['EventId']
    event_log[event_id] = event
  end

  return event_log
end
create_stack(template, stack_name) click to toggle source
# File lib/kumogata/client.rb, line 332
def create_stack(template, stack_name)
  unless stack_name
    user_host = Kumogata::Utils.get_user_host

    stack_name = ['kumogata']
    stack_name << user_host if user_host
    stack_name << UUIDTools::UUID.timestamp_create

    stack_name = stack_name.join('-')
    stack_name.gsub!(/[^-a-zA-Z0-9]+/, '-')
  end

  Kumogata.logger.info("Creating stack: #{stack_name}".cyan)
  stack = @cloud_formation.stacks.create(stack_name,
                                         JSON.pretty_generate(template),
                                         build_create_options)

  return if @options.detach?

  event_log = {}

  unless while_in_progress(stack, 'CREATE_COMPLETE', event_log)
    errmsgs = ['Create failed']
    errmsgs << stack_name
    errmsgs << stack.status_reason if stack.status_reason
    raise errmsgs.join(': ')
  end

  outputs = outputs_for(stack)
  summaries = resource_summaries_for(stack)

  if @options.delete_stack?
    delete_stack(stack_name)
  end

  output_result(stack_name, outputs, summaries)

  return outputs
end
define_template_func(scope, path_or_url) click to toggle source
# File lib/kumogata/client.rb, line 297
  def define_template_func(scope, path_or_url)
    scope.instance_eval(<<-EOS)
      def _include(file, args = {})
        path = file.dup

        unless path =~ %r|\\A/| or path =~ %r|\\A\\w+://|
          path = File.expand_path(File.join(File.dirname(#{path_or_url.inspect}), path))
        end

        open(path) {|f| instance_eval(f.read) }
      end

      def _path(path, value = nil, &block)
        if block
          value = Dslh::ScopeBlock.nest(binding, 'block')
        end

        @__hash__[path] = value
      end

      def _outputs_filter(&block)
        @__hash__[:_outputs_filter] = block
      end

      def _post(options = {}, &block)
        commands = Dslh::ScopeBlock.nest(binding, 'block')

        @__hash__[:_post] = {
          :options  => options,
          :commands => commands,
        }
      end
    EOS
  end
delete_stack(stack_name) click to toggle source
# File lib/kumogata/client.rb, line 396
def delete_stack(stack_name)
  stack = @cloud_formation.stacks[stack_name]
  stack.status

  Kumogata.logger.info("Deleting stack: #{stack_name}".red)
  event_log = create_event_log(stack)
  stack.delete

  return if @options.detach?

  completed = false

  begin
    completed = while_in_progress(stack, 'DELETE_COMPLETE', event_log)
  rescue AWS::CloudFormation::Errors::ValidationError
    # Handle `Stack does not exist`
    completed = true
    Kumogata.logger.info('Success')
  end

  unless completed
    errmsgs = ['Delete failed']
    errmsgs << stack_name
    errmsgs << stack.status_reason if stack.status_reason
    raise errmsgs.join(': ')
  end
end
describe_events(stack_name) click to toggle source
# File lib/kumogata/client.rb, line 446
def describe_events(stack_name)
  AWS.memoize do
    stack = @cloud_formation.stacks[stack_name]
    stack.status
    events_for(stack)
  end
end
describe_outputs(stack_name) click to toggle source
# File lib/kumogata/client.rb, line 454
def describe_outputs(stack_name)
  AWS.memoize do
    stack = @cloud_formation.stacks[stack_name]
    stack.status
    outputs_for(stack)
  end
end
describe_resources(stack_name) click to toggle source
# File lib/kumogata/client.rb, line 462
def describe_resources(stack_name)
  AWS.memoize do
    stack = @cloud_formation.stacks[stack_name]
    stack.status
    resource_summaries_for(stack)
  end
end
describe_stacks(stack_name) click to toggle source
# File lib/kumogata/client.rb, line 424
def describe_stacks(stack_name)
  AWS.memoize do
    stacks = @cloud_formation.stacks
    stacks = stacks.select {|i| i.name == stack_name } if stack_name

    stacks.map do |stack|
      {
        'StackName'    => stack.name,
        'CreationTime' => stack.creation_time,
        'StackStatus'  => stack.status,
        'Description'  => stack.description,
      }
    end
  end
end
devaluate_template(template) click to toggle source
# File lib/kumogata/client.rb, line 262
def devaluate_template(template)
  exclude_key = proc do |k|
    k = k.to_s.gsub(':', '_')
    k = k.to_s.gsub('::', '__')
    k !~ /\A[_a-z]\w+\Z/i and k !~ %r|\A/\S*\Z|
  end

  key_conv = proc do |k|
    k = k.to_s

    if k =~ %r|\A/\S*\Z|
      proc do |v, nested|
        if nested
          "_path(#{k.inspect}) #{v}"
        else
          "_path #{k.inspect}, #{v}"
        end
      end
    else
      k.gsub(':', '_')
      k.gsub('::', '__')
    end
  end

  value_conv = proc do |v|
    if v.kind_of?(String) and v =~ /\A(?:0|[1-9]\d*)\Z/
      v.to_i
    else
      v
    end
  end

  Dslh.deval(template, :key_conv => key_conv, :value_conv => value_conv, :exclude_key => exclude_key)
end
evaluate_after_trigger(template) click to toggle source
# File lib/kumogata/client.rb, line 257
def evaluate_after_trigger(template)
  triggers = template.delete('_after')
  return {} unless triggers
end
evaluate_template(template, path_or_url) click to toggle source
# File lib/kumogata/client.rb, line 223
def evaluate_template(template, path_or_url)
  key_converter = proc do |key|
    key = key.to_s
    unless @options.skip_replace_underscore?
      key.gsub!('_', ':')
      key.gsub!('__', '::')
    end
    key
  end

  value_converter = proc do |v|
    case v
    when Hash, Array
      v
    else
      v.to_s
    end
  end

  template = Dslh.eval(template.read, {
    :key_conv   => key_converter,
    :value_conv => value_converter,
    :scope_hook => proc {|scope|
      define_template_func(scope, path_or_url)
    },
    :filename   => path_or_url,
  })

  @outputs_filter.fetch!(template)
  @post_processing.fetch!(template)

  return template
end
events_for(stack) click to toggle source
# File lib/kumogata/client.rb, line 611
def events_for(stack)
  stack.events.map do |event|
    event_hash = {}

    [
      :event_id,
      :logical_resource_id,
      :physical_resource_id,
      :resource_properties,
      :resource_status,
      :resource_status_reason,
      :resource_type,
      :stack_id,
      :stack_name,
      :timestamp,
    ].each do |k|
      event_hash[Kumogata::Utils.camelize(k)] = event.send(k)
    end

    event_hash
  end
end
export_template(stack_name) click to toggle source
# File lib/kumogata/client.rb, line 440
def export_template(stack_name)
  stack = @cloud_formation.stacks[stack_name]
  stack.status
  JSON.parse(stack.template)
end
guess_format(path_or_url) click to toggle source
# File lib/kumogata/client.rb, line 204
def guess_format(path_or_url)
  case File.extname(path_or_url)
  when '.rb'
    :ruby
  when '.json'
    :json
  when '.yml', '.yaml'
    :yaml
  when '.js'
    :js
  when '.coffee'
    :coffee
  when '.json5'
    :json5
  else
    :json
  end
end
output_result(stack_name, outputs, summaries) click to toggle source
# File lib/kumogata/client.rb, line 663
  def output_result(stack_name, outputs, summaries)
    puts <<-EOS

Stack Resource Summaries:
#{JSON.pretty_generate(summaries).colorize_as(:json)}

Outputs:
#{JSON.pretty_generate(outputs).colorize_as(:json)}
EOS

    if @options.result_log?
      puts <<-EOS

(Save to `#{@options.result_log}`)
      EOS

      open(@options.result_log, 'wb') do |f|
        f.puts JSON.pretty_generate({
          'StackName' => stack_name,
          'StackResourceSummaries' => summaries,
          'Outputs' => outputs,
        })
      end
    end
  end
outputs_for(stack) click to toggle source
# File lib/kumogata/client.rb, line 634
def outputs_for(stack)
  outputs_hash = {}

  stack.outputs.each do |output|
    outputs_hash[output.key] = output.value
  end

  return outputs_hash
end
print_event_log(stack, event_log) click to toggle source
resource_summaries_for(stack) click to toggle source
# File lib/kumogata/client.rb, line 644
def resource_summaries_for(stack)
  stack.resource_summaries.map do |summary|
    summary_hash = {}

    [
      :logical_resource_id,
      :physical_resource_id,
      :resource_type,
      :resource_status,
      :resource_status_reason,
      :last_updated_timestamp
    ].each do |k|
      summary_hash[Kumogata::Utils.camelize(k)] = summary[k]
    end

    summary_hash
  end
end
update_deletion_policy(template, options = {}) click to toggle source
# File lib/kumogata/client.rb, line 563
def update_deletion_policy(template, options = {})
  if @options.delete_stack? or @options.deletion_policy_retain?
    template['Resources'].each do |k, v|
      next if /\AAWS::CloudFormation::/ =~ v['Type']
      v['DeletionPolicy'] ||= 'Retain'

      if options[:update_metadate]
        v['Metadata'] ||= {}
        v['Metadata']['DeletionPolicyUpdateKeyForKumogata'] = "DeletionPolicyUpdateValueForKumogata#{Time.now.to_i}"
      end
    end
  end
end
update_stack(template, stack_name) click to toggle source
# File lib/kumogata/client.rb, line 372
def update_stack(template, stack_name)
  stack = @cloud_formation.stacks[stack_name]
  stack.status

  Kumogata.logger.info("Updating stack: #{stack_name}".green)
  event_log = create_event_log(stack)
  stack.update(build_update_options(JSON.pretty_generate(template)))

  return if @options.detach?

  unless while_in_progress(stack, 'UPDATE_COMPLETE', event_log)
    errmsgs = ['Update failed']
    errmsgs << stack_name
    errmsgs << stack.status_reason if stack.status_reason
    raise errmsgs.join(': ')
  end

  outputs = outputs_for(stack)
  summaries = resource_summaries_for(stack)
  output_result(stack_name, outputs, summaries)

  return outputs
end
validate_stack_name(stack_name) click to toggle source
# File lib/kumogata/client.rb, line 689
def validate_stack_name(stack_name)
  return unless stack_name

  unless /\A[a-zA-Z][-a-zA-Z0-9]*\Z/i =~ stack_name
    raise "1 validation error detected: Value '#{stack_name}' at 'stackName' failed to satisfy constraint: Member must satisfy regular expression pattern: [a-zA-Z][-a-zA-Z0-9]*"
  end
end
validate_template(template) click to toggle source
# File lib/kumogata/client.rb, line 597
def validate_template(template)
  result = @cloud_formation.validate_template(template.to_json)

  if result[:code]
    raise result.values_at(:code, :message).join(': ')
  end

  Kumogata.logger.info('Template validated successfully'.green)

  if @options.verbose
    Kumogata.logger.info(JSON.pretty_generate(JSON.parse(result.to_json)).colorize_as(:json))
  end
end
while_in_progress(stack, complete_status, event_log) click to toggle source
# File lib/kumogata/client.rb, line 470
def while_in_progress(stack, complete_status, event_log)
  # XXX: Status does not change if you have been memoized.
  #      Should be forcibly disabled memoization?
  while stack.status =~ /_IN_PROGRESS\Z/
    print_event_log(stack, event_log)
    sleep 1
  end

  print_event_log(stack, event_log)
  completed = (stack.status == complete_status)
  Kumogata.logger.info(completed ? 'Success' : 'Failure')
  return completed
end