summaryrefslogtreecommitdiff
path: root/scripts/gitaly_test.rb
blob: 2262870eb96f276c7c6dddcb5b1ee6012f7d8ca2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# frozen_string_literal: true

# This file contains environment settings for gitaly when it's running
# as part of the gitlab-ce/ee test suite.
#
# Please be careful when modifying this file. Your changes must work
# both for local development rspec runs, and in CI.

require 'securerandom'
require 'socket'
require 'logger'

module GitalyTest
  LOGGER = begin
             default_name = ENV['CI'] ? 'DEBUG' : 'WARN'
             level_name = ENV['GITLAB_TESTING_LOG_LEVEL']&.upcase
             level = Logger.const_get(level_name || default_name, true) # rubocop: disable Gitlab/ConstGetInheritFalse
             Logger.new(STDOUT, level: level, formatter: ->(_, _, _, msg) { msg })
           end

  def tmp_tests_gitaly_dir
    File.expand_path('../tmp/tests/gitaly', __dir__)
  end

  def tmp_tests_gitlab_shell_dir
    File.expand_path('../tmp/tests/gitlab-shell', __dir__)
  end

  def rails_gitlab_shell_secret
    File.expand_path('../.gitlab_shell_secret', __dir__)
  end

  def gemfile
    File.join(tmp_tests_gitaly_dir, 'ruby', 'Gemfile')
  end

  def gitlab_shell_secret_file
    File.join(tmp_tests_gitlab_shell_dir, '.gitlab_shell_secret')
  end

  def env
    env_hash = {
      'HOME' => File.expand_path('tmp/tests'),
      'GEM_PATH' => Gem.path.join(':'),
      'BUNDLE_APP_CONFIG' => File.join(File.dirname(gemfile), '.bundle/config'),
      'BUNDLE_FLAGS' => "--jobs=4 --retry=3",
      'BUNDLE_INSTALL_FLAGS' => nil,
      'BUNDLE_GEMFILE' => gemfile,
      'RUBYOPT' => nil,

      # Git hooks can't run during tests as the internal API is not running.
      'GITALY_TESTING_NO_GIT_HOOKS' => "1"
    }

    if ENV['CI']
      bundle_path = File.expand_path('../vendor/gitaly-ruby', __dir__)
      env_hash['BUNDLE_FLAGS'] += " --path=#{bundle_path}"
    end

    env_hash
  end

  def config_path(service)
    case service
    when :gitaly
      File.join(tmp_tests_gitaly_dir, 'config.toml')
    when :gitaly2
      File.join(tmp_tests_gitaly_dir, 'gitaly2.config.toml')
    when :praefect
      File.join(tmp_tests_gitaly_dir, 'praefect.config.toml')
    end
  end

  def service_binary(service)
    case service
    when :gitaly, :gitaly2
      'gitaly'
    when :praefect
      'praefect'
    end
  end

  def install_gitaly_gems
    system(env, "make #{tmp_tests_gitaly_dir}/.ruby-bundle", chdir: tmp_tests_gitaly_dir) # rubocop:disable GitlabSecurity/SystemCommandInjection
  end

  def build_gitaly
    system(env, 'make', chdir: tmp_tests_gitaly_dir) # rubocop:disable GitlabSecurity/SystemCommandInjection
  end

  def start_gitaly
    start(:gitaly)
  end

  def start_gitaly2
    start(:gitaly2)
  end

  def start_praefect
    start(:praefect)
  end

  def start(service)
    args = ["#{tmp_tests_gitaly_dir}/#{service_binary(service)}"]
    args.push("-config") if service == :praefect
    args.push(config_path(service))
    pid = spawn(env, *args, [:out, :err] => "log/#{service}-test.log")

    begin
      try_connect!(service)
    rescue
      Process.kill('TERM', pid)
      raise
    end

    pid
  end

  # Taken from Gitlab::Shell.generate_and_link_secret_token
  def ensure_gitlab_shell_secret!
    secret_file = rails_gitlab_shell_secret
    shell_link = gitlab_shell_secret_file

    unless File.size?(secret_file)
      File.write(secret_file, SecureRandom.hex(16))
    end

    unless File.exist?(shell_link)
      FileUtils.ln_s(secret_file, shell_link)
    end
  end

  def check_gitaly_config!
    LOGGER.debug "Checking gitaly-ruby Gemfile...\n"

    unless File.exist?(gemfile)
      message = "#{gemfile} does not exist."
      message += "\n\nThis might have happened if the CI artifacts for this build were destroyed." if ENV['CI']
      abort message
    end

    LOGGER.debug "Checking gitaly-ruby bundle...\n"
    out = ENV['CI'] ? STDOUT : '/dev/null'
    abort 'bundle check failed' unless system(env, 'bundle', 'check', out: out, chdir: File.dirname(gemfile))
  end

  def read_socket_path(service)
    # This code needs to work in an environment where we cannot use bundler,
    # so we cannot easily use the toml-rb gem. This ad-hoc parser should be
    # good enough.
    config_text = IO.read(config_path(service))

    config_text.lines.each do |line|
      match_data = line.match(/^\s*socket_path\s*=\s*"([^"]*)"$/)

      return match_data[1] if match_data
    end

    raise "failed to find socket_path in #{config_path(service)}"
  end

  def try_connect!(service)
    LOGGER.debug "Trying to connect to #{service}: "
    timeout = 20
    delay = 0.1
    socket = read_socket_path(service)

    Integer(timeout / delay).times do
      UNIXSocket.new(socket)
      LOGGER.debug " OK\n"

      return
    rescue Errno::ENOENT, Errno::ECONNREFUSED
      LOGGER.debug '.'
      sleep delay
    end

    LOGGER.warn " FAILED to connect to #{service}\n"

    raise "could not connect to #{socket}"
  end
end