module Gitlab
  module QA
    module Scenario
      module Test
        module Integration
          class GitalyCluster < Scenario::Template
            attr_reader :gitlab_name, :spec_suite

            def initialize
              @gitlab_name = 'gitlab-gitaly-ha'
              @primary_node_name = 'gitaly1'
              @secondary_node_name = 'gitaly2'
              @tertiary_node_name = 'gitaly3'
              @praefect_node_name = 'praefect'
              @database = 'postgres'
              @spec_suite = 'Test::Integration::GitalyHA'
              @network = 'test'
            end

            # rubocop:disable Metrics/AbcSize
            def perform(release, *rspec_args)
              gitaly_primary_node = gitaly(@primary_node_name, release)
              gitaly_secondary_node = gitaly(@secondary_node_name, release)
              gitaly_tertiary_node = gitaly(@tertiary_node_name, release)

              sql_node = Component::PostgreSQL.new.tap do |sql|
                sql.name = @database
                sql.network = @network
                sql.instance_no_teardown do
                  sql.run_psql '-d template1 -c "CREATE DATABASE praefect_production OWNER postgres"'
                end
              end

              praefect_node = Component::Gitlab.new.tap do |praefect|
                praefect.release = QA::Release.new(release)
                praefect.name = @praefect_node_name
                praefect.network = @network
                praefect.skip_availability_check = true

                praefect.omnibus_config = praefect_omnibus_configuration

                praefect.instance_no_teardown
              end

              Component::Gitlab.perform do |gitlab|
                gitlab.release = QA::Release.new(release)
                gitlab.name = gitlab_name
                gitlab.network = @network

                gitlab.omnibus_config = gitlab_omnibus_configuration
                gitlab.instance do
                  puts "Running Gitaly HA specs!"

                  Component::Specs.perform do |specs|
                    specs.suite = spec_suite
                    specs.release = gitlab.release
                    specs.network = gitlab.network
                    specs.args = [gitlab.address, *rspec_args]
                  end
                end
              end
            ensure
              praefect_node&.teardown
              sql_node&.teardown
              gitaly_primary_node&.teardown
              gitaly_secondary_node&.teardown
              gitaly_tertiary_node&.teardown
            end
            # rubocop:enable Metrics/AbcSize

            private

            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 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['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['postgres_queue_enabled'] = true;
                praefect['failover_enabled'] = true;
                praefect['virtual_storages'] = {
                  'default' => {
                    '#{@primary_node_name}' => {
                      'address' => 'tcp://#{@primary_node_name}.#{@network}:8075',
                      'token'   => 'PRAEFECT_INTERNAL_TOKEN',
                      'primary' => true
                    },
                    '#{@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 gitaly_omnibus_configuration
              <<~OMNIBUS
                #{disable_other_services}
                prometheus['enable'] = 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
              <<~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['listen_addr'] = '0.0.0.0:8075';
                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 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.omnibus_config = gitaly_omnibus_configuration
                gitaly.instance_no_teardown
              end
            end
          end
        end
      end
    end
  end
end