#!/usr/bin/env ruby

$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
$stdout.sync = true

require 'gpt_common'
require 'open3'
require 'optimist'
require 'rainbow'
require 'tty-spinner'

@opts = Optimist.options do
  banner "Usage: run-gitlab-docker [options]"
  banner "\nRuns a GitLab Docker image and waits for it to become healthy. Image must be based on official GitLab Images."
  banner "\nOptions:"
  opt :image, "Image to use for Gitlab Docker Container", short: :none, type: :string, required: true
  opt :name, "Name to use for Gitlab Docker Container", short: :none, type: :string, default: "gitlab"
  opt :hostname, "Address to use that resolves to Gitlab Docker Container", short: :none, type: :string, default: "docker"
  opt :project_id, "ID or URL-encoded path of a project on the GitLab image to perform checks.", short: :none, type: :string, default: "qa-perf-testing%2Fgitlabhq"
  opt :help, 'Show help message'
end

raise "Docker not installed. Exiting..." unless Open3.capture2e("docker version" + ';')[1]

spinner = TTY::Spinner.new("[:spinner] Starting GitLab Docker")
spinner.auto_spin

puts "Pulling docker image '#{@opts[:image]}'"
Open3.capture2e("docker pull #{@opts[:image]}")
image_id = Open3.capture2e("docker images -q #{@opts[:image]}")[0].strip

puts "Starting docker image '#{@opts[:image]}' ('#{image_id}') and waiting until healthy"
gd_out, gd_status = Open3.capture2e("docker run -d --publish 80:80 --publish 443:443 --publish 2222:22 --name #{@opts[:name]} --hostname #{@opts[:hostname]} --env GITLAB_OMNIBUS_CONFIG=\"gitlab_rails['monitoring_whitelist'] = ['0.0.0.0/0']; gitlab_rails['gitlab_shell_ssh_port'] = 2222\" #{@opts[:image]}")
raise "docker issue:\n#{gd_out}" unless gd_status.success?

spinner.success

start_time = Time.now
spinner = TTY::Spinner.new("[:spinner] Waiting for GitLab Container to be ready")
spinner.auto_spin
begin
  retries ||= 0
  sleep 5

  docker_healthcheck = Open3.capture2e("docker inspect -f {{.State.Health.Status}} #{@opts[:name]}")[0].strip == 'healthy'

  gitlab_healthcheck = GPTCommon.make_http_request(method: 'get', url: "http://#{@opts[:hostname]}/-/liveness", fail_on_error: false).status.success?

  homepage = GPTCommon.make_http_request(method: 'get', url: "http://#{@opts[:hostname]}", fail_on_error: false)
  homepage_healthcheck = !homepage.body.to_s.include?("Deploy in progress") && homepage.status.success?

  api_healthcheck = true
  10.times do
    api_healthcheck &&= GPTCommon.make_http_request(method: 'get', url: "http://#{@opts[:hostname]}/api/v4/groups", fail_on_error: false).status.success?
    api_healthcheck &&= GPTCommon.make_http_request(method: 'get', url: "http://#{@opts[:hostname]}/api/v4/version", headers: { 'PRIVATE-TOKEN': ENV['ACCESS_TOKEN'] }, fail_on_error: false).status.success? if ENV['ACCESS_TOKEN']
    sleep 1
  end

  raise "One or more healthchecks failed (Docker: #{docker_healthcheck}, GitLab: #{gitlab_healthcheck}, Homepage: #{homepage_healthcheck}, API: #{api_healthcheck})" unless docker_healthcheck && gitlab_healthcheck && homepage_healthcheck && api_healthcheck
rescue RuntimeError, HTTP::ConnectionError => e
  sleep 5
  retries += 1
  if retries > 120
    puts Open3.capture2e("docker logs #{@opts[:name]}")[0].strip
    raise e
  end

  retry
rescue Interrupt
  warn Rainbow("Caught the interrupt. Stopping and removing Docker container.").yellow
  Open3.capture2e("docker rm -f #{@opts[:name]}")
  exit
end
spinner.success

spinner = TTY::Spinner.new("[:spinner] Performing GitLab Docker warmup")
spinner.auto_spin
%W[api/v4/projects api/v4/projects/#{@opts[:project_id]}/merge_requests api/v4/projects/#{@opts[:project_id]}/issues
  api/v4/projects/#{@opts[:project_id]}/repository/branches api/v4/projects/#{@opts[:project_id]}/repository/commits].each do |warmup_path|
  GPTCommon.make_http_request(method: 'get', url: "http://#{@opts[:hostname]}/#{warmup_path}", fail_on_error: false)
end
spinner.success

end_time = Time.now
run_time = (end_time - start_time).round(2)

puts "GitLab Container (image '#{@opts[:image]}') running successfully after #{run_time}s"
