#!/usr/bin/env oo-ruby

require 'rubygems'
require 'getoptlong'

def usage
  puts <<USAGE
== Synopsis

#{File.basename $0}: Control user settings.

== Notes

  *** WARNING *** WARNING *** WARNING *** WARNING ***

  DO NOT USE THIS SCRIPT TO MODIFY A LOT OF USERS AT ONCE!

  *** WARNING *** WARNING *** WARNING *** WARNING ***

== Usage

#{File.basename $0} OPTIONS

Options:
  -l|--login <login_name>
    Login with OpenShift access (required)
  --setmaxdomains <number>
    Set the maximum number of domains a user is allowed to use
  --setmaxgears <number>
    Set the maximum number of gears a user is allowed to use
  --setmaxtrackedstorage <number>
    Set the maximum additional storage per gear that will be tracked for a user
  --setmaxuntrackedstorage <number>
    Set the maximum additional storage per gear that will be untracked for a user
  --setconsumedgears <number>
    Set the number of gears a user has consumed (use carefully)
  --listsubaccounts
    List the subaccounts that have been created under this parent account (login)
  --addsubaccount <subaccount login>
    The sub account to add to the login parent account
  --removesubaccount <subaccount login>
    The sub account to remove from the login parent account
  --allowsubaccounts (true|false)
    Add / Remove the capability to manage sub accounts
  --allowplanupgrade (true|false)
    Add / Remove the capability to upgrade your plan.
  --allowprivatesslcertificates (true|false)
    Add / Remove the capability to add private SSL certificates
  --addgearsize <gearsize>
    Add gearsize to the capability for this login user
  --removegearsize <gearsize>
    Remove gearsize from the capability for this login user
  --inheritgearsizes (true|false)
    Allow / Disallow inheritance of login user gearsizes capability to sub accounts
  --allowha (true|false)
    Allow / Disallow High Availability capability to the user
  -h|--help
    Show Usage info

Examples:
  List the current user settings with:
    #{File.basename $0} -l bob@redhat.com

  Set the maximum number of gears a user is allowed to use with:
    #{File.basename $0} -l bob@redhat.com --setmaxgears 10
USAGE
  exit 255
end

class String
  def to_b()
    return true if self.to_s.strip =~ /^(true|t|yes|y|1)$/i
    return false
  end
end

def set_max_domains(user, maxdomains)
  if user.max_domains == maxdomains
    puts "User already has max_domains set to #{maxdomains}"
    return
  end

  print "Setting max_domains to #{maxdomains}... "
  user.max_domains = maxdomains
  if user.save
    puts "Done."
  else
    puts "An error occurred saving the user."
    puts "Errors: #{user.errors.messages}"
    exit 6
  end
end

def set_max_gears(user, maxgears)
  if user.max_gears == maxgears
    puts "User already has max_gears set to #{maxgears}"
    return
  end

  print "Setting max_gears to #{maxgears}... "
  user.max_gears = maxgears
  if user.save
    puts "Done."
  else
    puts "An error occurred saving the user."
    puts "Errors: #{user.errors.messages}"
    exit 6
  end
end

def set_max_tracked_storage(user, maxtrackedstorage)
  if user.max_tracked_additional_storage == maxtrackedstorage
    puts "User already has max_tracked_additional_storage set to #{maxtrackedstorage}"
    return
  end

  print "Setting max_tracked_addtl_storage_per_gear to #{maxtrackedstorage}... "
  user.max_tracked_additional_storage = maxtrackedstorage
  if user.save
    puts "Done."
  else
    puts "An error occurred saving the user."
    puts "Errors: #{user.errors.messages}"
    exit 6
  end
end

def set_max_untracked_storage(user, maxuntrackedstorage)
  if user.max_untracked_additional_storage == maxuntrackedstorage
    puts "User already has max_untracked_additional_storage set to #{maxuntrackedstorage}"
    return
  end

  print "Setting max_untracked_addtl_storage_per_gear to #{maxuntrackedstorage}... "
  user.max_untracked_additional_storage = maxuntrackedstorage
  if user.save
    puts "Done."
  else
    puts "An error occurred saving the user."
    puts "Errors: #{user.errors.messages}"
    exit 6
  end
end

def set_consumed_gears(user, consumedgears)
  old_value = user.consumed_gears
  if user.consumed_gears == consumedgears
    puts "User already has consumed_gears set to #{user.consumed_gears}"
    return
  end

  print "Setting consumed_gears to #{consumedgears}... "
  result = CloudUser.where(login: user.login, consumed_gears: old_value).find_and_modify({ "$set" => { "consumed_gears" => consumedgears } } )
  if result.nil?
    puts "User's consumed_gear count changed between read and write, try again."
    return
  end
  puts "Done."
end

def allow_sub_accounts(user, allow)
  if user.subaccounts == allow
    puts "User already has allowsubaccounts set to #{allow}"
    return
  end

  print "Setting subaccounts capability to #{allow} for user #{user.login}... "
  user.subaccounts = allow
  if user.save
    puts "Done."
  else
    puts "An error occurred modifying the user capabilities."
    puts "Errors: #{user.errors.messages}"
    exit 6
  end
end

def allow_private_ssl_certificates(user, allow)
  if user.private_ssl_certificates == allow
    puts "User already has allow private_ssl_certificates set to #{allow}"
    return
  end

  print "Setting private_ssl_certificates capability to #{allow} for user #{user.login}... "
  user.private_ssl_certificates = allow
  if user.save
    puts "Done."
  else
    puts "An error occurred modifying the user capabilities."
    puts "Errors: #{user.errors.messages}"
    exit 6
  end
end

def allow_plan_upgrade(user, allow)
  if user.plan_upgrade_enabled == allow
    puts "User already has plan_upgrade_enabled set to #{allow}"
    return
  end

  print "Setting plan_upgraded_enabled capability to #{allow} for user #{user.login}... "
  user.plan_upgrade_enabled = allow
  if user.save
    puts "Done."
  else
    puts "An error occurred modifying the user capabilities."
    puts "Errors: #{user.errors.messages}"
    exit 6
  end
end

def add_sub_account(user, subaccount_login)
  unless user.subaccounts
    puts "User #{user.login} does not have the capability to manage sub accounts"
    return
  end

  begin
    child_user = CloudUser::find_by_identity(subaccount_login)
  rescue Mongoid::Errors::DocumentNotFound
    child_user = nil
  end
    
  if not child_user.nil?
    if child_user.parent_user_id == user._id
      puts "Error: Subaccount for '#{subaccount_login}' already exists under #{user.login}"
    elsif not child_user.parent_user_id.nil?
      parent_user = CloudUser.with(consistency: :eventual).find_by(_id: child_user.parent_user_id)
      puts "Error: Subaccount for '#{subaccount_login}' already exists under #{parent_user.login}"
    else
      puts "Error: User '#{subaccount_login}' already exists"
    end
    exit 5
  end
    
  print "Adding subaccount for #{subaccount_login} under #{user.login}... "
  child_user = CloudUser.new(login: subaccount_login, parent_user_id: user._id)
  if child_user.save
    puts "Done."
  else
    puts "An error occurred adding the sub account #{subaccount_login}."
    puts "Errors: #{child_user.errors.messages}"
    exit 6
  end
  Lock.create_lock(child_user)
end

def remove_sub_account(user, subaccount_login)
  unless user.subaccounts
    puts "User #{user.login} does not have the capability to manage sub accounts"
    return
  end

  begin
    child_user = CloudUser::find_by_identity(subaccount_login)
  rescue Mongoid::Errors::DocumentNotFound
    puts "Error: Sub Account User '#{subaccount_login}' not found"
    exit 5
  end
    
  if child_user.parent_user_id.nil? || (child_user.parent_user_id != user._id)
    puts "Error: User '#{subaccount_login}' is not a sub account of #{user.login}"
    exit 5
  end
    
  print "Removing subaccount for #{child_user.login} under #{user.login}... "
  begin
    child_user.force_delete
  rescue Exception => e
    puts "An error occurred removing the sub account for #{subaccount_login} : #{e.message}"
    exit 6
  end
  puts "Done."
end

def add_gear_size(user, gear_size)
  print "Adding gear size #{gear_size} for user #{user.login}... "

  begin
    user.add_gear_size(gear_size)
  rescue Exception=>e
    puts e.message
    exit 1
  end
  puts "Done."
end

def remove_gear_size(user, gear_size)
  print "Removing gear size #{gear_size} for user #{user.login}... "

  begin
    user.remove_gear_size(gear_size)
  rescue Exception => e
    puts e.message
    exit 6
  end
  puts "Done."
end

def inherit_on_subaccounts(user, allow, capability, cap_name)
  if user.inherit_on_subaccounts.include?(capability) == allow
    puts "User already has #{cap_name} inheritance set to #{allow}"
    return
  end

  print "Setting #{cap_name} inheritance to #{allow} for user #{user.login}... "
  if allow
    user.add_capability_inherit_on_subaccounts(capability)
  else
    user.remove_capability_inherit_on_subaccounts(capability)
  end
    
  if user.save
    puts "Done."
  else
    puts "An error occurred modifying the user capabilities."
    puts "Errors: #{user.errors.messages}"
    exit 6
  end
end

def allow_ha(user, allow)
  unless Rails.configuration.openshift[:allow_ha_applications]
    puts "Error: Platform does not allow HA applications"
    exit 5
  end
  if user.ha == allow
    puts "User already has HA set to #{allow}"
    return
  end

  print "Setting HA capability to #{allow} for user #{user.login}... "
  user.ha = allow
  if user.save
    puts "Done."
  else
    puts "An error occurred modifying the user capabilities."
    puts "Errors: #{user.errors.messages}"
    exit 6
  end
end

opts = GetoptLong.new(
    ["--login",          "-l", GetoptLong::REQUIRED_ARGUMENT],
    ["--setmaxdomains",    GetoptLong::REQUIRED_ARGUMENT],
    ["--setmaxgears",      GetoptLong::REQUIRED_ARGUMENT],
    ["--setmaxtrackedstorage", GetoptLong::REQUIRED_ARGUMENT],
    ["--setmaxuntrackedstorage", GetoptLong::REQUIRED_ARGUMENT],
    ["--setconsumedgears", GetoptLong::REQUIRED_ARGUMENT],
    ["--listsubaccounts",  GetoptLong::NO_ARGUMENT],
    ["--addsubaccount",    GetoptLong::REQUIRED_ARGUMENT],
    ["--removesubaccount", GetoptLong::REQUIRED_ARGUMENT],
    ["--allowsubaccounts", GetoptLong::REQUIRED_ARGUMENT],
    ["--allowprivatesslcertificates", GetoptLong::REQUIRED_ARGUMENT],
    ["--allowplanupgrade", GetoptLong::REQUIRED_ARGUMENT],
    ["--addgearsize",      GetoptLong::REQUIRED_ARGUMENT],
    ["--removegearsize",   GetoptLong::REQUIRED_ARGUMENT],
    ["--inheritgearsizes", GetoptLong::REQUIRED_ARGUMENT],
    ["--allowha",          GetoptLong::REQUIRED_ARGUMENT],
    ["--help",             "-h", GetoptLong::NO_ARGUMENT]
)

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

login = args["--login"]
allowsubaccounts = args["--allowsubaccounts"].to_b if args["--allowsubaccounts"]
allowprivatesslcertificates = args["--allowprivatesslcertificates"].to_b if args["--allowprivatesslcertificates"]
inheritgearsizes = args["--inheritgearsizes"].to_b if args["--inheritgearsizes"]
allowha = args["--allowha"].to_b if args["--allowha"]

if args["--setmaxdomains"]
  unless args["--setmaxdomains"] =~ /^[0-9]+$/
    puts "ERROR: Max domains must be a positive integer"
    exit 1
  end
  maxdomains = args["--setmaxdomains"].to_i
end

if args["--setmaxgears"]
  unless args["--setmaxgears"] =~ /^[0-9]+$/
    puts "ERROR: Max gears must be a positive integer"
    exit 1
  end
  maxgears = args["--setmaxgears"].to_i
end

if args["--setmaxtrackedstorage"]
  unless args["--setmaxtrackedstorage"] =~ /^[0-9]+$/
    puts "ERROR: Max tracked storage must be a positive integer"
    exit 1
  end
  maxtrackedstorage = args["--setmaxtrackedstorage"].to_i
end

if args["--setmaxuntrackedstorage"]
  unless args["--setmaxuntrackedstorage"] =~ /^[0-9]+$/
    puts "ERROR: Max untracked storage must be a positive integer"
    exit 1
  end
  maxuntrackedstorage = args["--setmaxuntrackedstorage"].to_i
end

if args["--setconsumedgears"]
  unless args["--setconsumedgears"] =~ /^[0-9]+$/
    puts "ERROR: Consumed gears must be a positive integer"
    exit 1
  end
  consumedgears = args["--setconsumedgears"].to_i
end

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

account_to_add = args["--addsubaccount"]
account_to_remove = args["--removesubaccount"]
gear_size_to_add = args["--addgearsize"]
gear_size_to_remove = args["--removegearsize"]

# this require is here to not load the environment simply to display help
require '/var/www/openshift/broker/config/environment'
# Disable analytics for admin scripts
Rails.configuration.analytics[:enabled] = false
puts
puts

begin
  user = CloudUser.find_by_identity(login)
rescue Mongoid::Errors::DocumentNotFound
  puts "Error: User '#{login}' not found"
  exit 5
end

changed_user = false
subaccount_list = []

unless maxdomains.nil?
  set_max_domains(user, maxdomains)
  changed_user = true
end

unless maxgears.nil?
  set_max_gears(user, maxgears)
  changed_user = true
end

unless maxtrackedstorage.nil?
  set_max_tracked_storage(user,maxtrackedstorage)
  changed_user = true
end

unless maxuntrackedstorage.nil?
  set_max_untracked_storage(user,maxuntrackedstorage)
  changed_user = true
end

unless consumedgears.nil?
  set_consumed_gears(user, consumedgears)
  changed_user = true
end

unless allowsubaccounts.nil?
  allow_sub_accounts(user, allowsubaccounts)
  changed_user = true
end

unless args["--allowplanupgrade"].nil?
  allow_plan_upgrade(user, args["--allowplanupgrade"].to_b)
end

unless allowprivatesslcertificates.nil?
  allow_private_ssl_certificates(user, allowprivatesslcertificates)
  changed_user = true
end

unless account_to_add.nil?
  add_sub_account(user, account_to_add)
end

unless account_to_remove.nil?
  remove_sub_account(user, account_to_remove)
end

unless gear_size_to_add.nil?
  add_gear_size(user, gear_size_to_add)
  changed_user = true
end

unless gear_size_to_remove.nil?
  remove_gear_size(user, gear_size_to_remove)
  changed_user = true
end

unless inheritgearsizes.nil?
  inherit_on_subaccounts(user, inheritgearsizes, 'gear_sizes', 'gearsizes')
  changed_user = true
end

unless allowha.nil?
  allow_ha(user, allowha)
  changed_user = true
end

if args["--listsubaccounts"]
  subaccount_list = CloudUser.where(parent_user_id: user._id) 
end

if changed_user
  # reload user with new settings
  user = CloudUser.find_by_identity(login)
  puts
end

# print out the user's current settings
puts "User #{user.login}:"
puts "                            plan: #{user.plan_id}"
puts "                consumed domains: #{user.domains.count}"
puts "                     max domains: #{user.max_domains}"
puts "                  consumed gears: #{user.consumed_gears}"
puts "                       max gears: #{user.max_gears}"
puts "    max tracked storage per gear: #{user.max_tracked_additional_storage}"
puts "  max untracked storage per gear: #{user.max_untracked_additional_storage}"
puts "            plan upgrade enabled: #{user.plan_upgrade_enabled}" if user.capabilities.has_key?('plan_upgrade_enabled')
puts "                      gear sizes: #{user.allowed_gear_sizes.join(', ')}"
puts "            sub accounts allowed: #{user.subaccounts}"
puts "private SSL certificates allowed: #{user.private_ssl_certificates}"
puts "              inherit gear sizes: #{user.inherit_on_subaccounts.include?('gear_sizes')}"
puts "                      HA allowed: #{user.ha}"
puts 
if args["--listsubaccounts"] and (not subaccount_list.nil?) and (not subaccount_list.empty?)
  puts "Sub Accounts: #{subaccount_list.length}"
  subaccount_list.each do |subaccount|
    puts "     #{subaccount.login}"
  end
  puts
end
