gitlab-qa 5.13.6 → 5.16.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,22 +6,9 @@ require 'securerandom'
6
6
  module Gitlab
7
7
  module QA
8
8
  module Component
9
- class SAML
10
- include Scenario::Actable
11
-
12
- SAML_IMAGE = 'jamedjo/test-saml-idp'.freeze
13
- SAML_IMAGE_TAG = 'latest'.freeze
14
-
15
- attr_reader :docker
16
- attr_accessor :volumes, :network, :environment
17
- attr_writer :name
18
-
19
- def initialize
20
- @docker = Docker::Engine.new
21
- @environment = {}
22
- @volumes = {}
23
- @network_aliases = []
24
- end
9
+ class SAML < Base
10
+ DOCKER_IMAGE = 'jamedjo/test-saml-idp'.freeze
11
+ DOCKER_IMAGE_TAG = 'latest'.freeze
25
12
 
26
13
  def set_entity_id(entity_id)
27
14
  @environment['SIMPLESAMLPHP_SP_ENTITY_ID'] = entity_id
@@ -31,18 +18,10 @@ module Gitlab
31
18
  @environment['SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE'] = assertion_con_service
32
19
  end
33
20
 
34
- def add_network_alias(name)
35
- @network_aliases.push(name)
36
- end
37
-
38
21
  def name
39
22
  @name ||= "saml-qa-idp"
40
23
  end
41
24
 
42
- def hostname
43
- "#{name}.#{network}"
44
- end
45
-
46
25
  def group_name
47
26
  @group_name ||= "saml_sso_group-#{SecureRandom.hex(4)}"
48
27
  end
@@ -50,25 +29,12 @@ module Gitlab
50
29
  def instance
51
30
  raise 'Please provide a block!' unless block_given?
52
31
 
53
- prepare
54
- start
55
-
56
- yield self
57
- ensure
58
- teardown
59
- end
60
-
61
- def prepare
62
- pull
63
-
64
- return if @docker.network_exists?(network)
65
-
66
- @docker.network_create(network)
32
+ super
67
33
  end
68
34
 
69
35
  # rubocop:disable Metrics/AbcSize
70
36
  def start
71
- docker.run(SAML_IMAGE, SAML_IMAGE_TAG) do |command|
37
+ docker.run(image, tag) do |command|
72
38
  command << '-d '
73
39
  command << "--name #{name}"
74
40
  command << "--net #{network}"
@@ -91,21 +57,6 @@ module Gitlab
91
57
  end
92
58
  # rubocop:enable Metrics/AbcSize
93
59
 
94
- def restart
95
- @docker.restart(name)
96
- end
97
-
98
- def teardown
99
- raise 'Invalid instance name!' unless name
100
-
101
- @docker.stop(name)
102
- @docker.remove(name)
103
- end
104
-
105
- def pull
106
- @docker.pull(SAML_IMAGE, SAML_IMAGE_TAG)
107
- end
108
-
109
60
  def set_sandbox_name(sandbox_name)
110
61
  ::Gitlab::QA::Runtime::Env.gitlab_sandbox_name = sandbox_name
111
62
  end
@@ -8,7 +8,7 @@ module Gitlab
8
8
  # the `qa/` directory located in GitLab CE / EE repositories.
9
9
  #
10
10
  class Specs < Scenario::Template
11
- attr_accessor :suite, :release, :network, :args, :volumes, :env
11
+ attr_accessor :suite, :release, :network, :args, :volumes, :env, :runner_network
12
12
 
13
13
  def initialize
14
14
  @docker = Docker::Engine.new
@@ -17,18 +17,11 @@ module Gitlab
17
17
  end
18
18
 
19
19
  def perform # rubocop:disable Metrics/AbcSize
20
+ return puts "Skipping tests." if skip_tests?
21
+
20
22
  raise ArgumentError unless [suite, release].all?
21
23
 
22
- if release.dev_gitlab_org?
23
- Docker::Command.execute(
24
- [
25
- 'login',
26
- '--username gitlab-qa-bot',
27
- %(--password "#{Runtime::Env.dev_access_token_variable}"),
28
- QA::Release::DEV_REGISTRY
29
- ]
30
- )
31
- end
24
+ @docker.login(**release.login_params) if release.login_params
32
25
 
33
26
  puts "Running test suite `#{suite}` for #{release.project_name}"
34
27
 
@@ -51,6 +44,12 @@ module Gitlab
51
44
  command.name(name)
52
45
  end
53
46
  end
47
+
48
+ private
49
+
50
+ def skip_tests?
51
+ Runtime::Scenario.attributes.include?(:run_tests) && !Runtime::Scenario.run_tests
52
+ end
54
53
  end
55
54
  end
56
55
  end
@@ -4,8 +4,9 @@ module Gitlab
4
4
  class Command
5
5
  attr_reader :args
6
6
 
7
- def initialize(cmd = nil)
7
+ def initialize(cmd = nil, mask_secrets: nil)
8
8
  @args = Array(cmd)
9
+ @mask_secrets = Array(mask_secrets)
9
10
  end
10
11
 
11
12
  def <<(*args)
@@ -28,6 +29,17 @@ module Gitlab
28
29
  "docker #{@args.join(' ')}"
29
30
  end
30
31
 
32
+ # Returns a masked string form of a Command
33
+ #
34
+ # @example
35
+ # Command.new('a docker command', mask_secrets: 'command').mask_secrets #=> 'a docker *****'
36
+ # Command.new('a docker command', mask_secrets: %w[docker command]).mask_secrets #=> 'a ***** *****'
37
+ #
38
+ # @return [String] The masked command string
39
+ def mask_secrets
40
+ @mask_secrets.each_with_object(to_s) { |secret, s| s.gsub!(secret, '*****') }
41
+ end
42
+
31
43
  def ==(other)
32
44
  to_s == other.to_s
33
45
  end
@@ -36,8 +48,8 @@ module Gitlab
36
48
  Docker::Shellout.new(self).execute!(&block)
37
49
  end
38
50
 
39
- def self.execute(cmd, &block)
40
- new(cmd).execute!(&block)
51
+ def self.execute(cmd, mask_secrets: nil, &block)
52
+ new(cmd, mask_secrets: mask_secrets).execute!(&block)
41
53
  end
42
54
  end
43
55
  end
@@ -3,11 +3,16 @@ module Gitlab
3
3
  module Docker
4
4
  class Engine
5
5
  DOCKER_HOST = ENV['DOCKER_HOST'] || 'http://localhost'
6
+ PRIVILEGED_COMMANDS = [/^iptables.*/].freeze
6
7
 
7
8
  def hostname
8
9
  URI(DOCKER_HOST).host
9
10
  end
10
11
 
12
+ def login(username:, password:, registry:)
13
+ Docker::Command.execute(%(login --username "#{username}" --password "#{password}" #{registry}), mask_secrets: password)
14
+ end
15
+
11
16
  def pull(image, tag)
12
17
  Docker::Command.execute("pull #{image}:#{tag}")
13
18
  end
@@ -23,8 +28,18 @@ module Gitlab
23
28
  end
24
29
  end
25
30
 
31
+ def privileged_command?(command)
32
+ PRIVILEGED_COMMANDS.each do |privileged_regex|
33
+ return true if command.match(privileged_regex)
34
+ end
35
+
36
+ false
37
+ end
38
+
26
39
  def exec(name, command)
27
- Docker::Command.execute("exec #{name} bash -c '#{command}'")
40
+ cmd = ['exec']
41
+ cmd << '--privileged' if privileged_command?(command)
42
+ Docker::Command.execute("#{cmd.join(' ')} #{name} bash -c '#{command}'")
28
43
  end
29
44
 
30
45
  def read_file(image, tag, path, &block)
@@ -63,6 +78,14 @@ module Gitlab
63
78
  def port(name, port)
64
79
  Docker::Command.execute("port #{name} #{port}/tcp")
65
80
  end
81
+
82
+ def running?(name)
83
+ Docker::Command.execute("ps -f name=#{name}").include?(name)
84
+ end
85
+
86
+ def ps(name = nil)
87
+ Docker::Command.execute(['ps', name].compact.join(' '))
88
+ end
66
89
  end
67
90
  end
68
91
  end
@@ -10,7 +10,7 @@ module Gitlab
10
10
  @command = command
11
11
  @output = []
12
12
 
13
- puts "Docker shell command: `#{@command}`"
13
+ puts "Docker shell command: `#{@command.mask_secrets}`"
14
14
  end
15
15
 
16
16
  def execute!
@@ -28,7 +28,7 @@ module Gitlab
28
28
  end
29
29
 
30
30
  if wait.value.exited? && wait.value.exitstatus.nonzero?
31
- raise StatusError, "Docker command `#{@command}` failed!"
31
+ raise StatusError, "Docker command `#{@command.mask_secrets}` failed!"
32
32
  end
33
33
  end
34
34
 
@@ -135,6 +135,35 @@ module Gitlab
135
135
  end
136
136
  end
137
137
 
138
+ def login_params
139
+ return if Runtime::Env.skip_pull?
140
+
141
+ if dev_gitlab_org?
142
+ Runtime::Env.require_qa_dev_access_token!
143
+
144
+ {
145
+ username: Runtime::Env.gitlab_dev_username,
146
+ password: Runtime::Env.dev_access_token_variable,
147
+ registry: DEV_REGISTRY
148
+ }
149
+ elsif omnibus_mirror?
150
+ username, password = if Runtime::Env.ci_job_token && Runtime::Env.ci_pipeline_source == 'pipeline'
151
+ ['gitlab-ci-token', Runtime::Env.ci_job_token]
152
+ elsif Runtime::Env.qa_container_registry_access_token
153
+ [Runtime::Env.gitlab_username, Runtime::Env.qa_container_registry_access_token]
154
+ else
155
+ Runtime::Env.require_qa_access_token!
156
+
157
+ [Runtime::Env.gitlab_username, Runtime::Env.qa_access_token]
158
+ end
159
+ {
160
+ username: username,
161
+ password: password,
162
+ registry: COM_REGISTRY
163
+ }
164
+ end
165
+ end
166
+
138
167
  def dev_gitlab_org?
139
168
  image.start_with?(DEV_REGISTRY)
140
169
  end
@@ -147,6 +176,10 @@ module Gitlab
147
176
  canonical? || release.match?(CUSTOM_GITLAB_IMAGE_REGEX)
148
177
  end
149
178
 
179
+ def api_project_name
180
+ project_name.gsub('ce', 'foss').gsub('-ee', '')
181
+ end
182
+
150
183
  private
151
184
 
152
185
  def canonical?
@@ -4,22 +4,20 @@ module Gitlab
4
4
  module QA
5
5
  # rubocop:disable Metrics/AbcSize
6
6
  class Runner
7
- # These options are implemented in the QA framework (i.e., in the CE/EE codebase)
8
- # They're included here so that gitlab-qa treats them as valid options
9
- PASS_THROUGH_OPTS = [
10
- ['--address URL', 'Address of the instance to test'],
11
- ['--enable-feature FEATURE_FLAG', 'Enable a feature before running tests'],
12
- ['--mattermost-address URL', 'Address of the Mattermost server'],
13
- ['--parallel', 'Execute tests in parallel'],
14
- ['--loop', 'Execute tests in a loop']
15
- ].freeze
16
-
17
7
  def self.run(args)
18
- options = OptionParser.new do |opts|
8
+ Runtime::Scenario.define(:teardown, true)
9
+ Runtime::Scenario.define(:run_tests, true)
10
+
11
+ @options = OptionParser.new do |opts|
19
12
  opts.banner = 'Usage: gitlab-qa [options] Scenario URL [[--] path] [rspec_options]'
20
13
 
21
- PASS_THROUGH_OPTS.each do |opt|
22
- opts.on(*opt)
14
+ opts.on('--no-teardown', 'Skip teardown of containers after the scenario completes.') do
15
+ Runtime::Scenario.define(:teardown, false)
16
+ end
17
+
18
+ opts.on('--no-tests', 'Orchestrates the docker containers but does not run the tests. Implies --no-teardown') do
19
+ Runtime::Scenario.define(:run_tests, false)
20
+ Runtime::Scenario.define(:teardown, false)
23
21
  end
24
22
 
25
23
  opts.on_tail('-v', '--version', 'Show the version') do
@@ -33,19 +31,29 @@ module Gitlab
33
31
  exit
34
32
  end
35
33
 
36
- opts.parse(args)
34
+ begin
35
+ opts.parse(args)
36
+ rescue OptionParser::InvalidOption
37
+ # Ignore invalid options and options that are passed through to the tests
38
+ end
37
39
  end
38
40
 
41
+ args.reject! { |arg| gitlab_qa_options.include?(arg) }
42
+
39
43
  if args.size >= 1
40
44
  Scenario
41
45
  .const_get(args.shift)
42
46
  .perform(*args)
43
47
  else
44
- puts options
48
+ puts @options
45
49
  exit 1
46
50
  end
47
51
  end
48
52
  # rubocop:enable Metrics/AbcSize
53
+
54
+ def self.gitlab_qa_options
55
+ @gitlab_qa_options ||= @options.top.list.map(&:long).flatten
56
+ end
49
57
  end
50
58
  end
51
59
  end
@@ -38,6 +38,7 @@ module Gitlab
38
38
  'SIGNUP_DISABLED' => :signup_disabled,
39
39
  'QA_ADDITIONAL_REPOSITORY_STORAGE' => :qa_additional_repository_storage,
40
40
  'QA_PRAEFECT_REPOSITORY_STORAGE' => :qa_praefect_repository_storage,
41
+ 'QA_GITALY_NON_CLUSTER_STORAGE' => :qa_gitaly_non_cluster_storage,
41
42
  'QA_COOKIES' => :qa_cookie,
42
43
  'QA_DEBUG' => :qa_debug,
43
44
  'QA_LOG_PATH' => :qa_log_path,
@@ -84,18 +85,38 @@ module Gitlab
84
85
  send(:attr_accessor, accessor) # rubocop:disable GitlabSecurity/PublicSend
85
86
  end
86
87
 
88
+ def gitlab_username
89
+ ENV['GITLAB_USERNAME'] || 'gitlab-qa'
90
+ end
91
+
92
+ def gitlab_dev_username
93
+ ENV['GITLAB_DEV_USERNAME'] || 'gitlab-qa-bot'
94
+ end
95
+
87
96
  def gitlab_api_base
88
97
  ENV['GITLAB_API_BASE'] || 'https://gitlab.com/api/v4'
89
98
  end
90
99
 
100
+ def gitlab_bot_multi_project_pipeline_polling_token
101
+ ENV['GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN']
102
+ end
103
+
91
104
  def ci_job_name
92
105
  ENV['CI_JOB_NAME']
93
106
  end
94
107
 
108
+ def ci_job_token
109
+ ENV['CI_JOB_TOKEN']
110
+ end
111
+
95
112
  def ci_job_url
96
113
  ENV['CI_JOB_URL']
97
114
  end
98
115
 
116
+ def ci_pipeline_source
117
+ ENV['CI_PIPELINE_SOURCE']
118
+ end
119
+
99
120
  def ci_project_name
100
121
  ENV['CI_PROJECT_NAME']
101
122
  end
@@ -132,6 +153,10 @@ module Gitlab
132
153
  ENV['GITLAB_QA_DEV_ACCESS_TOKEN']
133
154
  end
134
155
 
156
+ def qa_container_registry_access_token
157
+ ENV['GITLAB_QA_CONTAINER_REGISTRY_ACCESS_TOKEN']
158
+ end
159
+
135
160
  def host_artifacts_dir
136
161
  @host_artifacts_dir ||= File.join(ENV['QA_ARTIFACTS_DIR'] || '/tmp/gitlab-qa', Runtime::Env.run_id)
137
162
  end
@@ -196,7 +221,7 @@ module Gitlab
196
221
  end
197
222
 
198
223
  def skip_pull?
199
- (ENV['QA_SKIP_PULL'] =~ /^(false|no|0)$/i) != 0
224
+ enabled?(ENV['QA_SKIP_PULL'], default: false)
200
225
  end
201
226
 
202
227
  def gitlab_qa_formless_login_token
@@ -205,6 +230,12 @@ module Gitlab
205
230
 
206
231
  private
207
232
 
233
+ def enabled?(value, default: true)
234
+ return default if value.nil?
235
+
236
+ (value =~ /^(false|no|0)$/i) != 0
237
+ end
238
+
208
239
  def env_value_if_defined(variable)
209
240
  # Pass through the variables if they are defined in the environment
210
241
  return "$#{variable}" if ENV[variable]
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module QA
5
+ module Runtime
6
+ ##
7
+ # Singleton approach to global test scenario arguments.
8
+ #
9
+ module Scenario
10
+ extend self
11
+
12
+ def attributes
13
+ @attributes ||= {}
14
+ end
15
+
16
+ def define(attribute, value)
17
+ attributes.store(attribute.to_sym, value)
18
+
19
+ define_singleton_method(attribute) do
20
+ attributes[attribute.to_sym].tap do |value|
21
+ if value.to_s.empty?
22
+ raise ArgumentError, "Empty `#{attribute}` attribute!"
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ # rubocop:disable Style/MethodMissing
29
+ def method_missing(name, *)
30
+ raise ArgumentError, "Scenario attribute `#{name}` not defined!"
31
+ end
32
+ # rubocop:enable Style/MethodMissing
33
+ end
34
+ end
35
+ end
36
+ end