#!/usr/bin/env ruby
#
# POC ECS service deployment script.
#
# * Bumps task definitions
# * Notifies slack

require 'optparse'
require 'yaml'
require 'json'
require 'tempfile'
require 'hashie'
require 'pp'
require 'aws-sdk'
require 'slack-notifier'

options = Hashie::Mash.new({
  cluster: 'default',
  env: 'staging',
  region: 'us-east-1',
  profile: 'default',
  version: %x[git rev-parse --short HEAD].chomp
})

OptionParser.new do |opts|
  opts.banner = "Usage: ecs-deploy [-b] [service]"

  opts.on("-e", "--env ENVIRONMENT", "defaults to 'staging'") do |v|
    options[:env] = v
  end

  opts.on("-c", "--cluster CLUSTER_NAME", "ECS Cluster, defaults to 'default'") do |v|
    options[:cluster] = v
  end

  opts.on("-p", "--profile PROFILE_NAME", "AWS credentials profile") do |v|
    options[:profile] = v
  end
end.parse!

# Read environment configuration file from project
config = Hashie::Mash.new(YAML.load(File.read("deploy.yml"))[options.env] || {})

# Backward compat for single-service deploy configurations.
if config.service
  config.services = [config.dup]
  config.services[0].name = config.services[0].delete(:service)
  config.cluster = config.services[0].delete(:cluster)
end

config = Hashie::Mash.new(config)

options = options.merge(config)
options.service ||= ARGV.shift

if !options.service
  options.service = options.services.collect{|s| s[:name]}
  puts "No service name given, deploying [#{options.service.join(', ')}]"
else
  options.service = [options.service]
end

def system!(*args)
  system(*args)
  exit 1 if $? != 0
end

ecs = Aws::ECS::Client.new(
  region: options.region,
  profile: options.profile
)

begin
  services = ecs.describe_services(
    cluster: options.cluster,
    services: options.service
  )

  for service_options in config.services do
    service = services.services.find{ |s| s.service_name == service_options.name }

    if !service
      puts "Service '#{service_options.name}' doesn't exist and won't be created. Terraform should create the service and all infrastructure for you."
      next
    end

    # Bump TD
    cur_td = ecs.describe_task_definition(task_definition: service[:task_definition]).task_definition.to_h
    image = service_options[:image] || cur_td[:container_definitions][0][:image]
    puts "Image: #{image}\t revision: #{cur_td[:revision]}"

    td = cur_td.dup
    [:task_definition_arn, :revision, :requires_attributes, :status].each{|k| td.delete(k)}
    td[:container_definitions][0][:image] = image

    new_td = ecs.register_task_definition(td).task_definition.to_h

    # Note: you can't update the ELB for a service, you need to delete/recreate
    # the service.
    if service && service.status != "INACTIVE"
      ecs.update_service(
        cluster: options.cluster,
        service: service_options.name,
        task_definition: [service_options.name, new_td[:revision]].join(':')
      )

      msg = "service "
      if service_options.hostname
        msg << "*<http://#{service_options.hostname}|#{service_options.name}>*"
      else
        msg << "*#{service_options.name}*"
      end
      msg << " (*#{options.env}*) has been updated:\n  task: `#{cur_td[:revision]}` -> `#{new_td[:revision]}`\n  image: `#{cur_td[:container_definitions][0][:image]}` -> `#{image}`"

      if service_options[:slack_webhook] && service_options[:slack_channel]
        @slack = Slack::Notifier.new service_options[:slack_webhook],
          channel: service_options[:slack_channel],
          username: 'ecs-deploy'
      end

      if @slack
        # msg = "<!channel> #{msg}" if alert
        @slack.ping(msg)
      end
    end
  end
ensure
end
