class Gitlab::QA::Scenario::Test::Integration::GitalyCluster

def disable_other_services

def disable_other_services
  <<~OMNIBUS
    postgresql['enable'] = false;
    redis['enable'] = false;
    nginx['enable'] = false;
    prometheus['enable'] = false;
    grafana['enable'] = false;
    puma['enable'] = false;
    sidekiq['enable'] = false;
    gitlab_workhorse['enable'] = false;
    gitlab_rails['rake_cache_clear'] = false;
    gitlab_rails['auto_migrate'] = false;
  OMNIBUS
end

def gitaly(name, release)

def gitaly(name, release)
  Component::Gitlab.new.tap do |gitaly|
    gitaly.release = QA::Release.new(release)
    gitaly.name = name
    gitaly.network = @network
    gitaly.skip_availability_check = true
    gitaly.seed_admin_token = false
    gitaly.omnibus_configuration << gitaly_omnibus_configuration
    gitaly.instance(skip_teardown: true)
  end
end

def gitaly_omnibus_configuration

def gitaly_omnibus_configuration
  <<~OMNIBUS
    #{disable_other_services.sub(/(prometheus\['enable'\]) = false/, '\1 = true')}
    prometheus_monitoring['enable'] = false;
    gitaly['enable'] = true;
    gitaly['listen_addr'] = '0.0.0.0:8075';
    gitaly['prometheus_listen_addr'] = '0.0.0.0:9236';
    gitaly['auth_token'] = 'PRAEFECT_INTERNAL_TOKEN';
    gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN';
    gitlab_rails['internal_api_url'] = 'http://#{@gitlab_name}.#{@network}';
    git_data_dirs({
      '#{@primary_node_name}' => {
        'path' => '/var/opt/gitlab/git-data'
      },
      '#{@secondary_node_name}' => {
        'path' => '/var/opt/gitlab/git-data'
      },
      '#{@tertiary_node_name}' => {
        'path' => '/var/opt/gitlab/git-data'
      }
    });
  OMNIBUS
end

def gitlab_omnibus_configuration

def gitlab_omnibus_configuration
  <<~OMNIBUS
    external_url 'http://#{@gitlab_name}.#{@network}';
    git_data_dirs({
      'default' => {
        'gitaly_address' => 'tcp://#{@praefect_node_name}.#{@network}:2305',
        'gitaly_token' => 'PRAEFECT_EXTERNAL_TOKEN'
      }
    });
    gitaly['enable'] = false;
    gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN';
    prometheus['scrape_configs'] = [
      {
        'job_name' => 'praefect',
        'static_configs' => [
          'targets' => [
            '#{@praefect_node_name}.#{@network}:9652'
          ]
        ]
      },
      {
        'job_name' => 'praefect-gitaly',
        'static_configs' => [
          'targets' => [
            '#{@primary_node_name}.#{@network}:9236',
            '#{@secondary_node_name}.#{@network}:9236',
            '#{@tertiary_node_name}.#{@network}:9236'
          ]
        ]
      }
    ];
    grafana['disable_login_form'] = false;
    grafana['admin_password'] = 'GRAFANA_ADMIN_PASSWORD';
  OMNIBUS
end

def initialize

def initialize
  @gitlab_name = 'gitlab-gitaly-cluster'
  @primary_node_name = 'gitaly1'
  @secondary_node_name = 'gitaly2'
  @tertiary_node_name = 'gitaly3'
  @praefect_node_name = 'praefect'
  @database = 'postgres'
  @spec_suite = 'Test::Instance::All'
  @network = 'test'
  @env = {}
  @tag = 'gitaly_cluster'
end

def perform(release, *rspec_args)

def perform(release, *rspec_args)
  # The postgres container starts in seconds so not essential to parallelize it
  # This also ensure that the docker network is created here, avoiding any potential race conditions later
  #  if the gitaly-cluster and GitLab containers attempt to create a network in parallel
  @sql_node = postgres
  gitaly_cluster = Thread.new do
    Thread.current.abort_on_exception = true
    start_gitaly_cluster(release)
  end
  Component::Gitlab.perform do |gitlab|
    gitlab.release = QA::Release.new(release)
    gitlab.name = gitlab_name
    gitlab.network = @network
    gitlab.omnibus_configuration << gitlab_omnibus_configuration
    gitlab.instance do
      # Wait for gitaly cluster to finish booting, before attempting to run specs
      gitaly_cluster.join
      Runtime::Logger.info('Running Gitaly Cluster specs!')
      if @tag
        rspec_args << "--" unless rspec_args.include?('--')
        rspec_args << "--tag" << @tag
      end
      Component::Specs.perform do |specs|
        specs.suite = spec_suite
        specs.release = gitlab.release
        specs.network = gitlab.network
        specs.args = [gitlab.address, *rspec_args]
        specs.env = @env
      end
    end
  end
ensure
  @praefect_node&.teardown
  @sql_node&.teardown
  @gitaly_primary_node&.teardown
  @gitaly_secondary_node&.teardown
  @gitaly_tertiary_node&.teardown
end

def postgres

def postgres
  Component::PostgreSQL.new.tap do |sql|
    sql.name = @database
    sql.network = @network
    sql.instance(skip_teardown: true) do
      sql.run_psql '-d template1 -c "CREATE DATABASE praefect_production OWNER postgres"'
    end
  end
end

def praefect(name, release)

def praefect(name, release)
  Component::Gitlab.new.tap do |praefect|
    praefect.release = QA::Release.new(release)
    praefect.name = name
    praefect.network = @network
    praefect.skip_availability_check = true
    praefect.seed_admin_token = false
    praefect.omnibus_configuration << praefect_omnibus_configuration
    praefect.instance(skip_teardown: true)
  end
end

def praefect_omnibus_configuration

def praefect_omnibus_configuration
  <<~OMNIBUS
    #{disable_other_services}
    gitaly['enable'] = false;
    praefect['enable'] = true;
    praefect['listen_addr'] = '0.0.0.0:2305';
    praefect['prometheus_listen_addr'] = '0.0.0.0:9652';
    praefect['auth_token'] = 'PRAEFECT_EXTERNAL_TOKEN';
    praefect['reconciliation_scheduling_interval'] = '10s';
    praefect['database_host'] = '#{@database}.#{@network}';
    praefect['database_user'] = 'postgres';
    praefect['database_port'] = 5432;
    praefect['database_password'] = 'SQL_PASSWORD';
    praefect['database_dbname'] = 'praefect_production';
    praefect['database_sslmode'] = 'disable';
    praefect['database_direct_host'] = '#{@database}.#{@network}';
    praefect['database_direct_port'] = 5432;
    praefect['virtual_storages'] = {
      'default' => {
        'nodes' => {
          '#{@primary_node_name}' => {
            'address' => 'tcp://#{@primary_node_name}.#{@network}:8075',
            'token'   => 'PRAEFECT_INTERNAL_TOKEN'
          },
          '#{@secondary_node_name}' => {
            'address' => 'tcp://#{@secondary_node_name}.#{@network}:8075',
            'token'   => 'PRAEFECT_INTERNAL_TOKEN'
          },
          '#{@tertiary_node_name}' => {
            'address' => 'tcp://#{@tertiary_node_name}.#{@network}:8075',
            'token'   => 'PRAEFECT_INTERNAL_TOKEN'
          }
        }
      }
    };
  OMNIBUS
end

def start_gitaly_cluster(release)

def start_gitaly_cluster(release)
  Runtime::Logger.info("Starting Gitaly Cluster")
  @gitaly_primary_node = gitaly(@primary_node_name, release)
  @gitaly_secondary_node = gitaly(@secondary_node_name, release)
  @gitaly_tertiary_node = gitaly(@tertiary_node_name, release)
  @praefect_node = praefect(@praefect_node_name, release)
  Runtime::Logger.info("Gitaly Cluster Ready")
end