#!/usr/bin/env oo-ruby

#--
# Copyright 2012 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#++

require 'rubygems'
require 'getoptlong'
require 'time'

def usage
  puts <<USAGE
== Synopsis

#{$0}: Check user applications and delete all pending_op_groups that were created before a given time.

User operations (start/stop/add cart/etc.) are queued per-app; occasionally one may get stuck, unable
to operate and holding up the queue so that no other operations can proceed on that application.
This tool removes stale operations from the application queue.

== Usage

#{$0} OPTIONS

Options:
-t|--time
    Age in hours (default 1) to delete; pending ops older than this will be deleted.
-u|--uuid
    Specific app's uuid where the pruning operation will be done - if not given all apps, domains and users will be screened
-h|--help
    Show Usage info
USAGE
  exit 255
end

args = {}
begin
  opts = GetoptLong.new(
    ["--time",             "-t", GetoptLong::REQUIRED_ARGUMENT],
    ["--uuid",             "-u", GetoptLong::REQUIRED_ARGUMENT],
    ["--help",             "-h", GetoptLong::NO_ARGUMENT]
  )
  opts.each{ |k,v| args[k]=v }
rescue GetoptLong::Error => e
  usage
end

usage if args["--help"]

require "#{ENV['OPENSHIFT_BROKER_DIR'] || '/var/www/openshift/broker'}/config/environment"

# Reset the logging options. We want DEBUG level stuff.
module Broker
  class Application < Rails::Application
    Mongoid.logger = Rails.logger
    Moped.logger = Rails.logger
    Mongoid.logger.level = Logger::DEBUG
    Moped.logger.level = Logger::DEBUG
  end
end

$app_count = 0
def clean_app(a)
  $app_count += 1
  begin
    Lock.run_in_app_lock(a) do
      a.run_jobs(nil, true)
    end

    a.reload
    a.pending_op_groups.each do |op|
      puts
      puts "Failed to clear op for app (#{a.uuid}) - #{op.inspect} "
    end 
  rescue Mongoid::Errors::DocumentNotFound => ex
    # ignore the exception if the application has been deleted
    raise ex unless Application.where(_id: a._id).count == 0
  end
end

$domain_count = 0
def clean_domain(d)
  $domain_count += 1
  d.reload
  dlist = d.pending_ops.select { |op| op._type.nil? }
  dlist.each { |op| op.delete }
  d.pending_ops.delete_if { |op| op.nil? }
  d.run_jobs rescue nil
  d.reload
  dlist = d.pending_ops.select { |op| op.completed? }
  dlist.each { |op| op.delete }
  d.pending_ops.each { |op|
    unless op.completed?
      puts "Failed to clear op for domain (#{d.namespace}) - #{op.inspect} "
      op.state = :queued if op.state == :init
    end
  }
  d.save!
end

$user_count = 0
def clean_user(u)
  $user_count += 1
  Lock.run_in_user_lock(u, 1800) do
    u.run_jobs(nil, true)
  end

  u.pending_op_groups.each do |op|
    puts
    puts "Failed to clear op for user (#{u.id}) - #{op.inspect} "
  end 
end

$team_count = 0
def clean_team(t)
  $team_count += 1
  t.reload
  dlist = t.pending_ops.select { |op| op._type.nil? }
  dlist.each { |op| op.delete }
  t.pending_ops.delete_if { |op| op.nil? }
  t.save
  t.run_jobs rescue nil
  t.reload
  dlist = t.pending_ops.select { |op| op.completed? }
  dlist.each { |op| puts "Clearing op for team (#{t.name}) - #{op.inspect} " and op.delete }
  t.pending_ops.each { |op|
    unless op.completed?
      puts "Failed to clear op for team (#{t.name}) - #{op.inspect} "
      # putting back to queued state if it is in init state
      op.state = :queued if op.state == :init
    end
  }
  t.save
end


hours = args["--time"] || 1
hours = hours.to_i
uuid = args["--uuid"]

if uuid.nil?
  time = (hours == 0) ? nil : (Time.now.utc - hours*60*60)
   
  app_query = {"pending_op_groups.0" => {"$exists" => true}}
  app_query["pending_op_groups.created_at"] = {"$lt" => time} if time
  Application.no_timeout.where(app_query).each { |a| 
    begin
      clean_app(a) 
    rescue Exception=>e
      puts e.message
      puts e.backtrace
    end
  }
   
  domain_query = {"pending_ops.0" => {"$exists" => true}}
  domain_query["pending_ops.created_at"] = {"$lt" => time} if time
  Domain.no_timeout.where(domain_query).each { |d| 
    begin
      clean_domain(d)
    rescue Exception=>e
      puts e.message
      puts e.backtrace
    end
  }
   
  team_query = {"pending_ops.0" => {"$exists" => true}}
  team_query["pending_ops.created_at"] = {"$lt" => time} if time
  Team.no_timeout.where(team_query).each { |t| 
    begin
      clean_team(t)
    rescue Exception=>e
      puts e.message
      puts e.backtrace
    end
  }
   
  user_query = {"pending_op_groups.0" => {"$exists" => true}}
  user_query["pending_op_groups.created_at"] = {"$lt" => time} if time
  CloudUser.no_timeout.where(user_query).each { |u| 
    begin
      clean_user(u)
    rescue Exception=>e
      puts e.message
      puts e.backtrace
    end
  }
else
  Application.where({"_id" => uuid, "pending_op_groups.0" => {"$exists" => true}}).each { |a| clean_app(a) }
end

puts "#{$app_count} applications were cleaned up. #{$user_count} users were cleaned up. #{$domain_count} domains were cleaned up. #{$team_count} teams were cleaned up."
exit 0
