class BBC::Cosmos::Tools::Commands::Stack

Attributes

api[RW]

Public Class Methods

new(args = [], local_options = {}, thor_config = {}) click to toggle source
Calls superclass method BBC::Cosmos::Tools::Commands::Base::new
# File lib/bbc/cosmos/tools/commands/stack.rb, line 19
def initialize(args = [], local_options = {}, thor_config = {})
  super(args, local_options, thor_config)

  @l = -> data, component, environment do
    if data["Parameters"] && data["Parameters"]["ImageId"]
      data["Parameters"]["ImageId"]["Default"] = latest_ami_for(component, environment)
    end
  end

  @api = BBC::Cosmos::Tools::API.new(config.app["api"], options[:key_path])
end

Public Instance Methods

create(component = nil, main_stack = "true") click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 79
def create(component = nil, main_stack = "true")
  say "#{banner}\n"

  components(options, component).each do |id|
    say get_key_value("\nComponent", id)

    type       = Tools::Types::Factory.create(id, options, config)
    data       = type.generate_data
    stack_data = Tools::Cloudformation::Generator.component_stack_details(config.resources, options, id)

    post_data = {
      "name_suffix"   => options[:stack],
      "service_stack" => !main_stack.match(/(true|t|yes|y|1)$/i).nil?,
      "template"      => data,
      "parameters"    => Tools::Cloudformation::Generator.parameters(data, main_stack)
    }

    post_data["aws_account_id"] = stack_data["account_id"] unless stack_data["account_id"].nil?
    post_data["region"] = stack_data["region"] unless stack_data["region"].nil?
    response = api.post sprintf(config.app["stack_create_endpoint"], options[:env], id), JSON.pretty_generate(post_data)

    if response.success?
      say "Stack creation for component '#{component}' successfully started", :green
    else
      api_error(response, false)
    end
  end
rescue Exception => e
  error e.message
end
delete(component) click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 199
def delete(component)
  say "#{banner(component)}\n"

  reply = yes?("Are you sure you want to delete the above stack?", :yellow)
  if reply || reply.nil?

    stack_name = "#{options[:env]}-#{component}-#{options[:stack]}"

    response = api.post sprintf(config.app["stack_delete"], options[:env], component, stack_name), JSON.pretty_generate({})

    if response.success?
      say "Stack delete for component '#{component}' successfully started", :green
    else
      api_error response
    end
  else
    error "Action cancelled"
  end
end
diff(component = nil) click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 157
def diff(component = nil)
  components(options, component).each do |id|
    stack_name = "#{options[:env]}-#{id}-#{options[:stack]}"
    response = api.get sprintf(config.app["stack_template"], options[:env], id, stack_name)

    ### Copy-pasted: extract this logic out
    type = Tools::Types::Factory.create(id, options, config)
    data = type.generate_data.tap do |d|
      @l.call(d, id, options[:env])
    end

    stack_meta = stack_data(component, stack_name, options[:env])

    if data["Parameters"]
      data["Parameters"] = deep_merge(
        stack_parameters(stack_meta["parameters"], data["Parameters"]),
        data["Parameters"]
      )

      params = Tools::Cloudformation::Generator.parameters(data, false)
    end

    post_data = {
      "template"   => data,
      "parameters" => params || {}
    }
    ###

    if response.success?
      original_template = JSON.parse(response.body)

      removed, added = original_template.easy_diff(data)

      say JSON.pretty_generate(removed), :red
      say JSON.pretty_generate(added), :green
    else
      api_error(response, false)
    end
  end
end
events(component = nil) click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 223
def events(component = nil)
  say "#{banner}\n"

  components(options, component).each do |id|
    say get_key_value("\nComponent", id)

    stack_name = "#{options[:env]}-#{id}-#{options[:stack]}"
    response = api.get sprintf(config.app["stack_events"], options[:env], id, stack_name)

    if response.success?
      events = JSON.parse(response.body)

      data = events.slice(0, (options[:limit])).map do |event|
        event.split(" ")
      end

      print_table(
        build_table(
          ["Event type", "CF type", "Name", "Status"],
          data
        )
      )
    else
      api_error(response, false)
    end
  end
end
generate(component) click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 67
def generate(component)
  say "#{banner(component)}\n"

  type = Tools::Types::Factory.create(component, options, config)
  data = type.generate_data true
  say data, :white
end
list(component = nil) click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 33
def list(component = nil)
  say "#{banner}\n"

  components(options, component).each do |id|
    response = api.get sprintf(config.app["stacks"], options[:env], id)
    say get_key_value("\nComponent", id)

    if response.success?

      data = JSON.parse(response.body).map do |stack|
        [
          stack["name"].split("-").last,
          stack["main_stack"],
          stack["updated_at"],
          stack["status"]
        ]
      end

      print_table(
        build_table(
          ["Name", "Main stack", "Updated at", "Status"],
          data
        )
      )

    else
      api_error(response, false)
    end
  end
end
update(component = nil) click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 114
def update(component = nil)
  say "#{banner}\n"

  components(options, component).each do |id|
    say get_key_value("\nComponent", id)

    stack_name = "#{options[:env]}-#{id}-#{options[:stack]}"

    type = Tools::Types::Factory.create(id, options, config)
    data = type.generate_data.tap do |d|
      @l.call(d, id, options[:env])
    end

    stack_meta = stack_data(component, stack_name, options[:env])

    if data["Parameters"]
      data["Parameters"] = deep_merge(
        stack_parameters(stack_meta["parameters"], data["Parameters"]),
        data["Parameters"]
      )

      params = Tools::Cloudformation::Generator.parameters(data, false)
    end

    post_data = {
      "template"   => data,
      "parameters" => params || {}
    }

    response = api.post sprintf(config.app["stack_update_endpoint"], options[:env], id, stack_name), JSON.pretty_generate(post_data)

    if response.success?
      say "Stack update for component '#{id}' successfully started", :green
    else
      api_error(response, false)
    end
  end
end

Private Instance Methods

deep_merge(merger, mergee) click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 272
def deep_merge(merger, mergee)
  merge_proc = proc { |_key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merge_proc) : v2 }
  merger.merge(mergee, &merge_proc)
end
latest_ami_for(component, env = "int") click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 266
def latest_ami_for(component, env = "int")
  response = api.get sprintf(config.app["deployments"], component, env)
  api_error response unless response.success?
  JSON.parse(response.body).first["image"]["ami_id"]
end
stack_data(component, stack_name, env = "int") click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 261
def stack_data(component, stack_name, env = "int")
  response = api.get sprintf(config.app["stack_details"], env, component, stack_name)
  JSON.parse(response.body)
end
stack_parameters(stack_data, new_params) click to toggle source
# File lib/bbc/cosmos/tools/commands/stack.rb, line 253
def stack_parameters(stack_data, new_params)
  stack_data.reduce({}) do |accum, (key, value)|
    accum.tap do |a|
      a[key] = { "Default" => value["value"] } if new_params.has_key? key
    end
  end
end