#!/usr/bin/env oo-ruby

require 'rubygems'
require 'getoptlong'

CTL_APP_COMMANDS = "(start|stop|force-stop|restart|status|destroy|force-destroy|remove-gear|remove-cartridge)"

def usage
  puts <<USAGE
== Synopsis

oo-admin-ctl-app: Control user applications

== Usage

oo-admin-ctl-app OPTIONS

Options:
-l|--login <login_name>
    Login with OpenShift access (required)
-a|--app     <application>
    Application name  (alphanumeric) (required)
-c|--command <command>
    #{CTL_APP_COMMANDS} (required)
-b|--bypass
    Ignore warnings
--gear_uuid
    Gear uuid to operate on
--cartridge
    Cartridge to operate on
-h|--help
    Show Usage info
USAGE
  exit 255
end

opts = GetoptLong.new(
    ["--login",            "-l", GetoptLong::REQUIRED_ARGUMENT],
    ["--app",              "-a", GetoptLong::REQUIRED_ARGUMENT],    
    ["--command",          "-c", GetoptLong::REQUIRED_ARGUMENT],
    ["--gear_uuid",              GetoptLong::REQUIRED_ARGUMENT],
    ["--cartridge",              GetoptLong::REQUIRED_ARGUMENT],
    ["--bypass",           "-b", GetoptLong::NO_ARGUMENT],    
    ["--help",             "-h", GetoptLong::NO_ARGUMENT]
)

args = {}
begin
  opts.each{ |k,v| args[k]=v }
rescue GetoptLong::Error => e
  usage
end

login    = args["--login"]
app_name = args["--app"]
command  = args['--command']
bypass   = args['--bypass']
gear_uuid = args['--gear_uuid']
cartridge = args['--cartridge']

if login.nil? or app_name.nil? or command.nil? or args["--help"]
  usage
end

require "/var/www/openshift/broker/config/environment"
# Disable analytics for admin scripts
Rails.configuration.analytics[:enabled] = false

user = nil
begin
  user = CloudUser.with(consistency: :eventual).find_by(login: login)
rescue Mongoid::Errors::DocumentNotFound
end
unless user
  puts "User #{login} not found."
  exit 1
end
app = Application.find(user, app_name)
unless app
  puts "Application #{app_name} for user #{login} not found."
  exit 1
end

def check_user_response
  begin
    agree = gets.chomp
    if agree != 'y'
      puts "\n"
      exit 217
    end
  rescue Interrupt
    puts "\n"
    exit 217
  end
end

reply = ResultIO.new
begin
  case command
  when "start"
    reply.append app.start
  when "stop"
    reply.append app.stop  
  when "force-stop"
    reply.append app.stop(nil, true)
  when "restart"
    reply.append app.restart  
  when "status"
    app.requires.each { |feature|
      reply.append app.status(feature)
    }
  when "force-destroy","destroy"
    unless bypass
      puts <<-WARNING
    !!!! WARNING !!!! WARNING !!!! WARNING !!!!
    You are about to destroy the #{app_name} application.
  
    This is NOT reversible, all remote data for this application will be removed.
    WARNING
  
      print "Do you want to destroy this application (y/n): "
      check_user_response
    end
    
    if command=="destroy"
      app.destroy_app
    elsif command=="force-destroy"
      puts "Force destroying application #{app.name}.. user's consumed_gear count may get skewed"
      # gather all gears and destroy, then delete app
      app.group_instances.each { |gi| 
        gi.gears.each { |g| 
          g.deregister_dns rescue nil
          g.destroy_gear rescue nil 
        } if gi.gears
      } if app.group_instances
      app.delete
    end
    reply.resultIO << "Successfully destroyed application: #{app.name}" if reply.resultIO.string.empty?
  when "remove-cartridge"
    unless cartridge
      puts "Cartridge is required to remove-dependency"
      exit 1
    end
    app.remove_features([cartridge])
  when "remove-gear"
    unless gear_uuid
      puts "Gear uuid is required to remove-gear"
      exit 1
    end

    group_instance = nil
    gear_id = nil
    found = false

    app.group_instances.each do |gi|
      gi.gears.each do |g|
        if g._id.to_s == gear_uuid or g.uuid == gear_uuid
          if g.host_singletons
            puts "Gear #{gear_uuid} hosts singleton components within its group instance. You cannot remove it."
            puts "You can either remove the cartridge or delete the application."
            exit 1
          else  
            group_instance = gi
            gear_id = g._id.to_s
            found = true
            break
          end
        end
      end
      break if found
    end

    unless found
      puts "Gear #{gear_uuid} not found in #{app.name}"
      exit 1
    end
    app.remove_gear(gear_id)
  else
    puts "Command must be one of: #{CTL_APP_COMMANDS}"
    usage
  end
rescue OpenShift::UserException => e
  puts e.message
  exit 1
end

puts "DEBUG OUTPUT:\n#{reply.debugIO.string}\n" unless reply.debugIO.string.empty?
puts "ERROR OUTPUT:\n#{reply.errorIO.string}\n" unless reply.errorIO.string.empty?
puts reply.resultIO.string.empty? ? "Success" : reply.resultIO.string
