class PhusionPassenger::Config::RestartAppCommand

def self.create_option_parser(options)

def self.create_option_parser(options)
  OptionParser.new do |opts|
    nl = "\n" + ' ' * 37
    opts.banner =
      "Usage 1: passenger-config restart-app <APP PATH PREFIX> [OPTIONS]\n" +
      "Usage 2: passenger-config restart-app --name <APP GROUP NAME> [OPTIONS]"
    opts.separator ""
    opts.separator "  Restart an application. The syntax determines how the application that is to"
    opts.separator "  be restarted, will be selected."
    opts.separator ""
    opts.separator "  1. Selects all applications whose paths begin with the given prefix."
    opts.separator ""
    opts.separator "     Example: passenger-config restart-app /webapps"
    opts.separator "     Restarts all apps whose path begin with /webapps, such as /webapps/foo,"
    opts.separator "     /webapps/bar and /webapps123."
    opts.separator ""
    opts.separator "  2. Selects a specific application based on an exact match of its app group"
    opts.separator "     name."
    opts.separator ""
    opts.separator "     Example: passenger-config restart-app --name /webapps/foo"
    opts.separator "     Restarts only /webapps/foo, but not for example /webapps/foo/bar or"
    opts.separator "     /webapps/foo123."
    opts.separator ""
    opts.separator "Options:"
    opts.on("--name APP_GROUP_NAME", String, "The app group name to select") do |value|
      options[:app_group_name] = value
    end
    opts.on("--rolling-restart", "Perform a rolling restart instead of a#{nl}" +
      "regular restart (Enterprise only). The#{nl}" +
      "default is a blocking restart") do |value|
      if Config::Utils.is_enterprise?
        options[:rolling_restart] = true
      else
        abort "--rolling-restart is only available in #{PROGRAM_NAME} Enterprise: #{ENTERPRISE_URL}"
      end
    end
    opts.on("--ignore-app-not-running", "Exit successfully if the specified#{nl}" +
      "application is not currently running. The#{nl}" +
      "default is to exit with an error") do
      options[:ignore_app_not_running] = true
    end
    opts.on("--instance NAME", String, "The #{PROGRAM_NAME} instance to select") do |value|
      options[:instance] = value
    end
    opts.on("-h", "--help", "Show this help") do
      options[:help] = true
    end
  end
end

def abort_app_not_found(message)

def abort_app_not_found(message)
  if @options[:ignore_app_not_running]
    STDERR.puts(message)
    exit
  else
    abort(message)
  end
end

def help

def help
  puts @parser
end

def parse_options

def parse_options
  super
  case @argv.size
  when 0
    if !@options[:app_group_name] && !STDIN.tty?
      abort "Please pass either an app path prefix or an app group name. " +
        "See --help for more information."
    end
  when 1
    if @options[:app_group_name]
      abort "You've passed an app path prefix, but you cannot also pass an " +
        "app group name. Please use only either one of them. See --help " +
        "for more information."
    end
  else
    help
    abort
  end
end

def perform_restart

def perform_restart
  restart_method = @options[:rolling_restart] ? "rolling" : "blocking"
  @groups.each do |group|
    group_name = group.elements["name"].text
    puts "Restarting #{group_name}"
    request = Net::HTTP::Post.new("/pool/restart_app_group.json")
    request.basic_auth("admin", obtain_full_admin_password(@instance))
    request.content_type = "application/json"
    request.body = PhusionPassenger::Utils::JSON.generate(
      :name => group_name,
      :method => restart_method)
    response = @instance.http_request("agents.s/server_admin", request)
    if response.code.to_i / 100 == 2
      response.body
    else
      STDERR.puts "*** An error occured while communicating with the #{PROGRAM_NAME} server:"
      STDERR.puts response.body
      abort
    end
  end
end

def query_group_names

def query_group_names
  result = []
  query_pool_xml.elements.each("info/supergroups/supergroup/group") do |group|
    result << group.elements["name"].text
  end
  result << "Cancel"
  result
end

def query_pool_xml

def query_pool_xml
  request = Net::HTTP::Get.new("/pool.xml")
  request.basic_auth("ro_admin", obtain_read_only_admin_password(@instance))
  response = @instance.http_request("agents.s/server_admin", request)
  if response.code.to_i / 100 == 2
    REXML::Document.new(response.body)
  else
    STDERR.puts "*** An error occured while querying the #{PROGRAM_NAME} server:"
    STDERR.puts response.body
    abort
  end
end

def run

def run
  parse_options
  select_passenger_instance
  select_app_group_name
  perform_restart
end

def select_app_group_name

def select_app_group_name
  @groups = []
  if app_group_name = @options[:app_group_name]
    select_app_group_name_exact(app_group_name)
  elsif @argv.empty?
    # STDIN is guaranteed to be a TTY thanks to the check in #parse_options.
    select_app_group_name_interactively
  else
    select_app_group_name_by_app_root_regex(@argv.first)
  end
end

def select_app_group_name_by_app_root_regex(app_root)

def select_app_group_name_by_app_root_regex(app_root)
  regex = /^#{Regexp.escape(app_root)}/
  query_pool_xml.elements.each("info/supergroups/supergroup/group") do |group|
    if group.elements["app_root"].text =~ regex
      @groups << group
    end
  end
  if @groups.empty?
    abort_app_not_found "There are no #{PROGRAM_NAME}-served applications running " +
      "whose paths begin with '#{app_root}'."
  end
end

def select_app_group_name_exact(name)

def select_app_group_name_exact(name)
  query_pool_xml.elements.each("info/supergroups/supergroup/group") do |group|
    if group.elements["name"].text == name
      @groups << group
    end
  end
  if @groups.empty?
    abort_app_not_found "There is no #{PROGRAM_NAME}-served application running " +
      "with the app group name '#{name}'."
  end
end

def select_app_group_name_interactively

def select_app_group_name_interactively
  colors = PhusionPassenger::Utils::AnsiColors.new
  choices = query_group_names
  if choices.size == 1
    # No running apps
    abort_app_not_found "#{PROGRAM_NAME} is currently not serving any applications."
  end
  puts "Please select the application to restart."
  puts colors.ansi_colorize("<gray>Tip: re-run this command with --help to learn how to automate it.</gray>")
  puts colors.ansi_colorize("<dgray>If the menu doesn't display correctly, press '!'</dgray>")
  puts
  menu = PhusionPassenger::Utils::TerminalChoiceMenu.new(choices, :single_choice)
  begin
    index, name = menu.query
  rescue Interrupt
    abort
  end
  if index == choices.size - 1
    abort
  else
    puts
    select_app_group_name_exact(name)
  end
end