gitlab-qa 15.7.2 → 15.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +0 -1
- data/Gemfile.lock +1 -1
- data/docs/what_tests_can_be_run.md +22 -30
- data/gitlab-qa.gemspec +5 -0
- data/lib/gitlab/qa/component/duo_workflow_service.rb +83 -0
- data/lib/gitlab/qa/component/ldap.rb +2 -1
- data/lib/gitlab/qa/scenario/test/integration/ai_gateway_base.rb +40 -3
- data/lib/gitlab/qa/scenario/test/integration/duo_agent_platform.rb +26 -0
- data/lib/gitlab/qa/support/gitlab_upgrade_path.rb +78 -2
- data/lib/gitlab/qa/version.rb +1 -1
- metadata +7 -4
- data/lib/gitlab/qa/scenario/test/integration/mattermost.rb +0 -41
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 62f35e5b964b9eebe81ee065da70d428ad6a92e42146ece90c99cb058992608d
|
|
4
|
+
data.tar.gz: dadb71a13c7fc0d5c16f939d87e004ef70bba72468624ed089fb7b6592f647ff
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e39b28201d0ae2d1f4fce45b6b37960ca2269072ffafdd0d245d35d99f7d69eafc5cd93cf0aed3b253ebf03debd57862709995ca31702ab23e490972879ca334
|
|
7
|
+
data.tar.gz: bb8ab755a2fb9edb127bf7c17d8949d000acf3e75a5b1c2fabfc6ef5b0895588836ca8ac0e0c790f9ebd7d21a48665b1ea95c13c7d1b18103bbb97ee58864fec
|
data/.rubocop_todo.yml
CHANGED
|
@@ -45,7 +45,6 @@ Metrics/AbcSize:
|
|
|
45
45
|
- 'lib/gitlab/qa/scenario/test/integration/gitlab_pages.rb'
|
|
46
46
|
- 'lib/gitlab/qa/scenario/test/integration/integrations.rb'
|
|
47
47
|
- 'lib/gitlab/qa/scenario/test/integration/jira.rb'
|
|
48
|
-
- 'lib/gitlab/qa/scenario/test/integration/mattermost.rb'
|
|
49
48
|
- 'lib/gitlab/qa/scenario/test/integration/metrics.rb'
|
|
50
49
|
- 'lib/gitlab/qa/scenario/test/integration/mtls.rb'
|
|
51
50
|
- 'lib/gitlab/qa/scenario/test/integration/oauth.rb'
|
data/Gemfile.lock
CHANGED
|
@@ -304,8 +304,12 @@ Scenario verifies upgrade from previous (major|minor|patch) version to current r
|
|
|
304
304
|
|
|
305
305
|
- Deploys previous (major|minor|patch) version and runs Health Check test suite (Smoke test suite if below 17.1.0)
|
|
306
306
|
to populate data in database before upgrade
|
|
307
|
-
- Generates upgrade path following current GitLab recommendations
|
|
308
|
-
|
|
307
|
+
- Generates upgrade path following current GitLab recommendations.
|
|
308
|
+
The upgrade path is resolved in this order:
|
|
309
|
+
1. `QA_UPGRADE_PATH_FILE` env var — explicit path to a local YAML file.
|
|
310
|
+
2. `$CI_PROJECT_DIR/config/upgrade_path.yml` — set automatically in GitLab CI when running inside a `gitlab-org/gitlab` pipeline.
|
|
311
|
+
3. `../config/upgrade_path.yml` relative to the working directory — covers running from the `qa/` subdirectory of a gitlab checkout.
|
|
312
|
+
4. Remote fetch from [`gitlab.com/gitlab-org/gitlab`](https://gitlab.com/gitlab-org/gitlab/-/raw/master/config/upgrade_path.yml) (fallback when no local file is found).
|
|
309
313
|
- Gradually upgrades GitLab instances to `<current_version>`
|
|
310
314
|
associated with provided `<full image address>` and runs tests against it
|
|
311
315
|
|
|
@@ -640,34 +644,6 @@ $ export EE_LICENSE=$(cat /path/to/gitlab_license)
|
|
|
640
644
|
$ gitlab-qa Test::Integration::OAuth EE
|
|
641
645
|
```
|
|
642
646
|
|
|
643
|
-
### `Test::Integration::Mattermost CE|EE|<full image address>`
|
|
644
|
-
|
|
645
|
-
This tests that a GitLab instance works as expected when enabling the embedded
|
|
646
|
-
Mattermost server (see `Test::Instance::Image` above).
|
|
647
|
-
|
|
648
|
-
To run tests against the GitLab container, a GitLab QA (`gitlab/gitlab-qa`)
|
|
649
|
-
container is spun up and tests are run from it by running the
|
|
650
|
-
`Test::Integration::Mattermost` scenario (located under
|
|
651
|
-
[`gitlab-org/gitlab/qa/qa/scenario/test/integration/mattermost.rb`][test-integration-mattermost]
|
|
652
|
-
in the GitLab project).
|
|
653
|
-
|
|
654
|
-
**Required environment variables:**
|
|
655
|
-
|
|
656
|
-
- [For EE only] `EE_LICENSE`: A valid EE license.
|
|
657
|
-
|
|
658
|
-
Example:
|
|
659
|
-
|
|
660
|
-
```shell
|
|
661
|
-
$ gitlab-qa Test::Integration::Mattermost CE
|
|
662
|
-
|
|
663
|
-
# For EE
|
|
664
|
-
$ export EE_LICENSE=$(cat /path/to/Geo.gitlab_license)
|
|
665
|
-
|
|
666
|
-
$ gitlab-qa Test::Integration::Mattermost EE
|
|
667
|
-
```
|
|
668
|
-
|
|
669
|
-
[test-integration-mattermost]: https://gitlab.com/gitlab-org/gitlab/blob/master/qa/qa/scenario/test/integration/mattermost.rb
|
|
670
|
-
|
|
671
647
|
### `Test::Integration::Packages CE|EE|<full image address>`
|
|
672
648
|
|
|
673
649
|
**Note: This Scenario no longer exists. See https://gitlab.com/gitlab-org/gitlab-qa/-/merge_requests/662**
|
|
@@ -1239,6 +1215,7 @@ $ export GITLAB_LICENSE_MODE="test"
|
|
|
1239
1215
|
|
|
1240
1216
|
- Runs tests tagged with `:ai_gateway`
|
|
1241
1217
|
- GitLab instance has a cloud license with either a Duo Pro add-on or Duo Enterprise add-on, and a seat is assigned to the admin user. Choose Duo Enterprise over Duo Pro when available.
|
|
1218
|
+
- Serves the Duo Chat and Code Suggestions feature areas. It does not boot the Duo Workflow Service (DWS); the Duo Agent Platform foundational flow runs under the dedicated `Test::Integration::DuoAgentPlatform` scenario (below).
|
|
1242
1219
|
|
|
1243
1220
|
Example:
|
|
1244
1221
|
|
|
@@ -1248,6 +1225,21 @@ $ gitlab-qa Test::Integration::AiGateway EE
|
|
|
1248
1225
|
|
|
1249
1226
|
----
|
|
1250
1227
|
|
|
1228
|
+
#### `Test::Integration::DuoAgentPlatform EE|<full image address>`
|
|
1229
|
+
|
|
1230
|
+
- Runs tests tagged with `:duo_agent_platform`
|
|
1231
|
+
- Subclass of `Test::Integration::AiGateway`, so it inherits the same setup (cloud license + Duo add-on + assigned seat, plus the mock AI Gateway).
|
|
1232
|
+
- In addition, this scenario boots a mock Duo Workflow Service (DWS) container in agentic-mock mode (`duo-workflow-service`, from the same `model-gateway` image switched to the DWS gRPC server) and points GitLab at it via the `GITLAB_DUO_WORKFLOW_SERVICE_URL` / `GITLAB_DUO_WORKFLOW_SECURE` environment variables. This lets the Duo Agent Platform foundational-flow spec drive a flow end-to-end without a real LLM.
|
|
1233
|
+
- DWS container logs are captured to the job artifacts (`duo-workflow-service/logs/duo-workflow-service.log`) on teardown.
|
|
1234
|
+
|
|
1235
|
+
Example:
|
|
1236
|
+
|
|
1237
|
+
```shell
|
|
1238
|
+
$ gitlab-qa Test::Integration::DuoAgentPlatform EE
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
----
|
|
1242
|
+
|
|
1251
1243
|
#### `Test::Integration::AiGatewayNoSeatAssigned EE|<full image address>`
|
|
1252
1244
|
|
|
1253
1245
|
- Runs tests tagged with `:ai_gateway_no_seat_assigned`
|
data/gitlab-qa.gemspec
CHANGED
|
@@ -16,6 +16,11 @@ Gem::Specification.new do |spec|
|
|
|
16
16
|
spec.homepage = 'http://about.gitlab.com/'
|
|
17
17
|
spec.license = 'MIT'
|
|
18
18
|
|
|
19
|
+
spec.metadata = {
|
|
20
|
+
'source_code_uri' => 'https://gitlab.com/gitlab-org/gitlab-qa',
|
|
21
|
+
'bug_tracker_uri' => 'https://gitlab.com/gitlab-org/gitlab-qa/-/issues'
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
spec.files = `git ls-files -z`
|
|
20
25
|
.split("\x0").reject { |f| f.match(%r{^spec/}) }
|
|
21
26
|
spec.bindir = 'exe'
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
|
|
5
|
+
module Gitlab
|
|
6
|
+
module QA
|
|
7
|
+
module Component
|
|
8
|
+
# Boots the Duo Workflow Service (DWS) gRPC server in agentic-mock mode for Duo Agent
|
|
9
|
+
# Platform foundational-flow E2E tests. DWS ships inside the same model-gateway image as
|
|
10
|
+
# the AI Gateway; we reuse that image and switch the container to the DWS server via
|
|
11
|
+
# ENABLE_DUO_WORKFLOW_SERVICE / DISABLE_AI_GATEWAY (handled by the image's scripts/run.sh).
|
|
12
|
+
class DuoWorkflowService < Base
|
|
13
|
+
# Same artifact as the AI Gateway container, so the agentic mock model and DWS agree.
|
|
14
|
+
DOCKER_IMAGE = AiGateway::DOCKER_IMAGE
|
|
15
|
+
DOCKER_IMAGE_TAG = AiGateway::DOCKER_IMAGE_TAG
|
|
16
|
+
|
|
17
|
+
GRPC_PORT = 50052
|
|
18
|
+
LOG_FILE_NAME = 'duo-workflow-service.log'
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
super
|
|
22
|
+
|
|
23
|
+
@secrets << AiGateway::TEST_SIGNING_KEY
|
|
24
|
+
@secrets << AiGateway::TEST_VALIDATION_KEY
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def name
|
|
28
|
+
@name ||= 'duo-workflow-service'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def configure_environment(gitlab_hostname:)
|
|
32
|
+
@environment = {
|
|
33
|
+
# Switch the shared image from the AIGW FastAPI app to the DWS gRPC server.
|
|
34
|
+
'ENABLE_DUO_WORKFLOW_SERVICE' => true,
|
|
35
|
+
'DISABLE_AI_GATEWAY' => true,
|
|
36
|
+
'PORT' => GRPC_PORT,
|
|
37
|
+
|
|
38
|
+
# Agentic mock: select the deterministic in-process mock model. With both flags set,
|
|
39
|
+
# DWS makes no real LLM/AIGW call; the goal's <response>/<tool_calls> directives drive it.
|
|
40
|
+
'AIGW_MOCK_MODEL_RESPONSES' => true,
|
|
41
|
+
'AIGW_USE_AGENTIC_MOCK' => true,
|
|
42
|
+
|
|
43
|
+
# Disable DWS auth: with auth ENABLED, DWS prefetches JWKS from GitLab + CustomersDot
|
|
44
|
+
# at startup and hard-fails if unreachable (bad in a sealed CI network). Disabling
|
|
45
|
+
# skips that prefetch and bypasses ExecuteWorkflow token validation.
|
|
46
|
+
'DUO_WORKFLOW_AUTH__ENABLED' => false,
|
|
47
|
+
|
|
48
|
+
# ...but Rails still calls the GenerateToken RPC first, which needs the signing key
|
|
49
|
+
# present or the encode fails. So supply the DWS-prefixed self-signed keypair.
|
|
50
|
+
'DUO_WORKFLOW_SELF_SIGNED_JWT__SIGNING_KEY' => AiGateway::TEST_SIGNING_KEY,
|
|
51
|
+
'DUO_WORKFLOW_SELF_SIGNED_JWT__VALIDATION_KEY' => AiGateway::TEST_VALIDATION_KEY,
|
|
52
|
+
|
|
53
|
+
# Falls back to AIGW_GITLAB_URL if unset; set both for consistency.
|
|
54
|
+
'DUO_WORKFLOW_AUTH__OIDC_GITLAB_URL' => "http://#{gitlab_hostname}",
|
|
55
|
+
'AIGW_GITLAB_URL' => "http://#{gitlab_hostname}"
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# DWS logs to stdout/stderr and writes no in-container log file (unlike the AI Gateway),
|
|
60
|
+
# so capture `docker logs` into the host artifacts directory before the container is
|
|
61
|
+
# force-removed on teardown. Without this the DWS logs are lost on removal.
|
|
62
|
+
def teardown
|
|
63
|
+
capture_logs
|
|
64
|
+
ensure
|
|
65
|
+
super
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def host_log_path
|
|
71
|
+
File.join(Runtime::Env.host_artifacts_dir, name, 'logs', LOG_FILE_NAME)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def capture_logs
|
|
75
|
+
FileUtils.mkdir_p(File.dirname(host_log_path))
|
|
76
|
+
File.write(host_log_path, Docker::Command.execute("logs #{name}", mask_secrets: @secrets))
|
|
77
|
+
rescue Support::ShellCommand::StatusError, SystemCallError => e
|
|
78
|
+
Runtime::Logger.warn("Unable to capture logs from #{name}: #{e.message}")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -104,7 +104,8 @@ module Gitlab
|
|
|
104
104
|
#
|
|
105
105
|
# @return [String]
|
|
106
106
|
def working_dir_tmp_fixture_path
|
|
107
|
-
|
|
107
|
+
FileUtils.mkdir_p("#{Dir.pwd}/tmp")
|
|
108
|
+
@local_fixture_path ||= Dir.mktmpdir('ldap', "#{Dir.pwd}/tmp")
|
|
108
109
|
end
|
|
109
110
|
|
|
110
111
|
# Copy fixtures to current working directory
|
|
@@ -14,6 +14,10 @@ module Gitlab
|
|
|
14
14
|
@ai_gateway_name = 'ai-gateway'
|
|
15
15
|
@ai_gateway_hostname = "#{@ai_gateway_name}.#{@network}"
|
|
16
16
|
@ai_gateway_port = 5000
|
|
17
|
+
@dws_name = 'duo-workflow-service'
|
|
18
|
+
@dws_hostname = "#{@dws_name}.#{@network}"
|
|
19
|
+
@dws_port = Component::DuoWorkflowService::GRPC_PORT
|
|
20
|
+
@boot_duo_workflow_service = false
|
|
17
21
|
@use_cloud_license = true
|
|
18
22
|
@has_add_on = true
|
|
19
23
|
@assign_seats = true
|
|
@@ -27,15 +31,31 @@ module Gitlab
|
|
|
27
31
|
set_up_ai_gateway(ai_gateway, gitlab_hostname: gitlab.hostname)
|
|
28
32
|
|
|
29
33
|
ai_gateway.instance do
|
|
30
|
-
gitlab.
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
with_duo_workflow_service(gitlab_hostname: gitlab.hostname) do
|
|
35
|
+
gitlab.instance do
|
|
36
|
+
set_up_gitlab_duo(gitlab) if @use_cloud_license
|
|
37
|
+
run_specs(gitlab, *rspec_args)
|
|
38
|
+
end
|
|
33
39
|
end
|
|
34
40
|
end
|
|
35
41
|
end
|
|
36
42
|
end
|
|
37
43
|
end
|
|
38
44
|
|
|
45
|
+
# Boots the agentic-mock Duo Workflow Service alongside the AI Gateway and runs the
|
|
46
|
+
# given block inside it. When @boot_duo_workflow_service is false (the default for
|
|
47
|
+
# AiGateway and the No*/NoLicense variants) it is a no-op passthrough, so only the
|
|
48
|
+
# DuoAgentPlatform scenario has the extra gRPC container.
|
|
49
|
+
def with_duo_workflow_service(gitlab_hostname:, &block)
|
|
50
|
+
return yield unless @boot_duo_workflow_service
|
|
51
|
+
|
|
52
|
+
Component::DuoWorkflowService.perform do |dws|
|
|
53
|
+
set_up_duo_workflow_service(dws, gitlab_hostname: gitlab_hostname)
|
|
54
|
+
|
|
55
|
+
dws.instance(&block)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
39
59
|
def set_up_gitlab(gitlab, release)
|
|
40
60
|
gitlab.release = QA::Release.new(release)
|
|
41
61
|
gitlab.name = 'gitlab'
|
|
@@ -44,6 +64,15 @@ module Gitlab
|
|
|
44
64
|
gitlab.omnibus_gitlab_rails_env['AI_GATEWAY_URL'] = "http://#{@ai_gateway_hostname}:#{@ai_gateway_port}"
|
|
45
65
|
gitlab.omnibus_gitlab_rails_env['LLM_DEBUG'] = 'true'
|
|
46
66
|
|
|
67
|
+
if @boot_duo_workflow_service
|
|
68
|
+
# Point the foundational-flow path at the in-network agentic-mock DWS, plaintext.
|
|
69
|
+
# These are read by config/initializers/1_settings.rb (duo_workflow.service_url /
|
|
70
|
+
# .secure); the value is a bare gRPC host:port (no scheme), and 'false' is parsed
|
|
71
|
+
# via Gitlab::Utils.to_boolean.
|
|
72
|
+
gitlab.omnibus_gitlab_rails_env['GITLAB_DUO_WORKFLOW_SERVICE_URL'] = "#{@dws_hostname}:#{@dws_port}"
|
|
73
|
+
gitlab.omnibus_gitlab_rails_env['GITLAB_DUO_WORKFLOW_SECURE'] = 'false'
|
|
74
|
+
end
|
|
75
|
+
|
|
47
76
|
gitlab.set_ee_activation_code if @use_cloud_license
|
|
48
77
|
end
|
|
49
78
|
|
|
@@ -55,6 +84,14 @@ module Gitlab
|
|
|
55
84
|
ai_gateway.configure_environment(gitlab_hostname: gitlab_hostname)
|
|
56
85
|
end
|
|
57
86
|
|
|
87
|
+
def set_up_duo_workflow_service(dws, gitlab_hostname:)
|
|
88
|
+
dws.name = @dws_name
|
|
89
|
+
dws.network = @network
|
|
90
|
+
dws.ports = [@dws_port]
|
|
91
|
+
|
|
92
|
+
dws.configure_environment(gitlab_hostname: gitlab_hostname)
|
|
93
|
+
end
|
|
94
|
+
|
|
58
95
|
def set_up_gitlab_duo(gitlab)
|
|
59
96
|
Runtime::Logger.info('Setting up Gitlab Duo on GitLab instance')
|
|
60
97
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gitlab
|
|
4
|
+
module QA
|
|
5
|
+
module Scenario
|
|
6
|
+
module Test
|
|
7
|
+
module Integration
|
|
8
|
+
# Duo Agent Platform foundational-flow smoke scenario. Reuses the AiGateway scenario's
|
|
9
|
+
# setup (EE omnibus GitLab + mock AI Gateway) and additionally boots the agentic-mock
|
|
10
|
+
# Duo Workflow Service, running under its own tag so the foundational flow gets a
|
|
11
|
+
# dedicated omnibus job, separate from the ai-gateway job (Duo Chat / Code Suggestions).
|
|
12
|
+
class DuoAgentPlatform < AiGateway
|
|
13
|
+
def initialize
|
|
14
|
+
super
|
|
15
|
+
@tag = 'duo_agent_platform'
|
|
16
|
+
# Only the DAP foundational flow needs the Duo Workflow Service; the ai-gateway
|
|
17
|
+
# job (Duo Chat / Code Suggestions) does not, so boot DWS here rather than on the
|
|
18
|
+
# base AiGateway scenario.
|
|
19
|
+
@boot_duo_workflow_service = true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -209,20 +209,82 @@ module Gitlab
|
|
|
209
209
|
UPGRADE_PATH_URL = "https://gitlab.com/gitlab-org/gitlab/-/raw/master/config/upgrade_path.yml"
|
|
210
210
|
UPGRADE_PATH_RETRIES = 3
|
|
211
211
|
UPGRADE_PATH_RETRY_DELAY = 5
|
|
212
|
+
UPGRADE_PATH_RELATIVE_FILE = "config/upgrade_path.yml"
|
|
212
213
|
|
|
213
214
|
# Upgrade path yml
|
|
214
215
|
#
|
|
215
216
|
# @return [String]
|
|
216
217
|
def upgrade_path_yml
|
|
217
|
-
@upgrade_path_yml ||= fetch_upgrade_path_with_retry
|
|
218
|
+
@upgrade_path_yml ||= read_local_upgrade_path || fetch_upgrade_path_with_retry
|
|
218
219
|
end
|
|
219
220
|
|
|
221
|
+
# Attempt to read upgrade_path.yml from a local checkout, trying candidate
|
|
222
|
+
# paths in order:
|
|
223
|
+
# 1. The file pointed to by the QA_UPGRADE_PATH_FILE env var (explicit override).
|
|
224
|
+
# 2. $CI_PROJECT_DIR/config/upgrade_path.yml (set by GitLab CI when gitlab-qa
|
|
225
|
+
# runs inside a gitlab checkout pipeline).
|
|
226
|
+
# 3. ../config/upgrade_path.yml relative to the working directory (gitlab-qa
|
|
227
|
+
# typically runs from the qa/ subdirectory of a gitlab checkout).
|
|
228
|
+
#
|
|
229
|
+
# Returns the file contents as a String when a readable file is found, or nil
|
|
230
|
+
# when none of the candidates exist so the caller can fall back to a remote fetch.
|
|
231
|
+
#
|
|
232
|
+
# @return [String, nil]
|
|
233
|
+
def read_local_upgrade_path
|
|
234
|
+
warn_if_explicit_override_missing
|
|
235
|
+
|
|
236
|
+
candidates = local_upgrade_path_candidates.select { |path| File.file?(path) }
|
|
237
|
+
return if candidates.empty?
|
|
238
|
+
|
|
239
|
+
path = candidates.first
|
|
240
|
+
content = File.read(path)
|
|
241
|
+
|
|
242
|
+
if content.strip.empty?
|
|
243
|
+
logger.warn("Local upgrade path file is empty, falling back to remote fetch: #{path}")
|
|
244
|
+
return
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
logger.info("Reading gitlab upgrade path from local file: #{path}")
|
|
248
|
+
content
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Ordered list of local file path candidates for upgrade_path.yml.
|
|
252
|
+
#
|
|
253
|
+
# @return [Array<String>]
|
|
254
|
+
def local_upgrade_path_candidates
|
|
255
|
+
candidates = []
|
|
256
|
+
candidates << ENV["QA_UPGRADE_PATH_FILE"] if ENV["QA_UPGRADE_PATH_FILE"]
|
|
257
|
+
candidates << File.join(ENV["CI_PROJECT_DIR"], UPGRADE_PATH_RELATIVE_FILE) if ENV["CI_PROJECT_DIR"]
|
|
258
|
+
candidates << File.expand_path(File.join("..", UPGRADE_PATH_RELATIVE_FILE))
|
|
259
|
+
candidates
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Log a warning when QA_UPGRADE_PATH_FILE is explicitly set but does not point
|
|
263
|
+
# to a readable file. A typo'd or stale path would otherwise silently fall
|
|
264
|
+
# through to the CI_PROJECT_DIR / relative-path candidates (or the remote
|
|
265
|
+
# fetch), producing a different upgrade path than the user intended.
|
|
266
|
+
#
|
|
267
|
+
# @return [void]
|
|
268
|
+
def warn_if_explicit_override_missing
|
|
269
|
+
explicit = ENV.fetch("QA_UPGRADE_PATH_FILE", nil)
|
|
270
|
+
return unless explicit
|
|
271
|
+
return if File.file?(explicit)
|
|
272
|
+
|
|
273
|
+
logger.warn("QA_UPGRADE_PATH_FILE is set to '#{explicit}' but the file does not exist; " \
|
|
274
|
+
"falling through to other candidates")
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# Fetch upgrade_path.yml from the remote GitLab repository, retrying on
|
|
278
|
+
# transient errors. Raises after UPGRADE_PATH_RETRIES failed attempts.
|
|
279
|
+
#
|
|
280
|
+
# @return [String]
|
|
220
281
|
def fetch_upgrade_path_with_retry
|
|
221
282
|
retries = 0
|
|
283
|
+
headers = upgrade_path_request_headers
|
|
222
284
|
|
|
223
285
|
logger.info("Fetching gitlab upgrade path from 'gitlab.com/gitlab-org/gitlab' project")
|
|
224
286
|
begin
|
|
225
|
-
HttpRequest.make_http_request(url: UPGRADE_PATH_URL).body
|
|
287
|
+
HttpRequest.make_http_request(url: UPGRADE_PATH_URL, headers: headers).body
|
|
226
288
|
rescue StandardError => e
|
|
227
289
|
retries += 1
|
|
228
290
|
if retries <= UPGRADE_PATH_RETRIES
|
|
@@ -234,6 +296,20 @@ module Gitlab
|
|
|
234
296
|
raise "Failed to fetch gitlab upgrade path after #{UPGRADE_PATH_RETRIES} retries: #{e.message}"
|
|
235
297
|
end
|
|
236
298
|
end
|
|
299
|
+
|
|
300
|
+
# Build request headers for the upgrade path HTTP fetch.
|
|
301
|
+
#
|
|
302
|
+
# Includes a User-Agent header when GITLAB_QA_USER_AGENT is set, so the
|
|
303
|
+
# request matches the Cloudflare bypass rule already in place for GitLab QA.
|
|
304
|
+
#
|
|
305
|
+
# @return [Hash]
|
|
306
|
+
def upgrade_path_request_headers
|
|
307
|
+
user_agent = Runtime::Env.gitlab_qa_user_agent.to_s.strip
|
|
308
|
+
return {} if user_agent.empty?
|
|
309
|
+
|
|
310
|
+
logger.info("Using GITLAB_QA_USER_AGENT for upgrade path fetch")
|
|
311
|
+
{ "User-Agent" => user_agent }
|
|
312
|
+
end
|
|
237
313
|
end
|
|
238
314
|
end
|
|
239
315
|
end
|
data/lib/gitlab/qa/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gitlab-qa
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 15.
|
|
4
|
+
version: 15.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- GitLab Quality
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: climate_control
|
|
@@ -403,6 +403,7 @@ files:
|
|
|
403
403
|
- lib/gitlab/qa/component/alpine.rb
|
|
404
404
|
- lib/gitlab/qa/component/base.rb
|
|
405
405
|
- lib/gitlab/qa/component/chaos.rb
|
|
406
|
+
- lib/gitlab/qa/component/duo_workflow_service.rb
|
|
406
407
|
- lib/gitlab/qa/component/elasticsearch.rb
|
|
407
408
|
- lib/gitlab/qa/component/gitaly.rb
|
|
408
409
|
- lib/gitlab/qa/component/gitaly_cluster.rb
|
|
@@ -470,6 +471,7 @@ files:
|
|
|
470
471
|
- lib/gitlab/qa/scenario/test/integration/chaos.rb
|
|
471
472
|
- lib/gitlab/qa/scenario/test/integration/client_ssl.rb
|
|
472
473
|
- lib/gitlab/qa/scenario/test/integration/continuous_vulnerability_scanning.rb
|
|
474
|
+
- lib/gitlab/qa/scenario/test/integration/duo_agent_platform.rb
|
|
473
475
|
- lib/gitlab/qa/scenario/test/integration/elasticsearch.rb
|
|
474
476
|
- lib/gitlab/qa/scenario/test/integration/geo.rb
|
|
475
477
|
- lib/gitlab/qa/scenario/test/integration/gitaly_cluster.rb
|
|
@@ -484,7 +486,6 @@ files:
|
|
|
484
486
|
- lib/gitlab/qa/scenario/test/integration/ldap_no_server.rb
|
|
485
487
|
- lib/gitlab/qa/scenario/test/integration/ldap_no_tls.rb
|
|
486
488
|
- lib/gitlab/qa/scenario/test/integration/ldap_tls.rb
|
|
487
|
-
- lib/gitlab/qa/scenario/test/integration/mattermost.rb
|
|
488
489
|
- lib/gitlab/qa/scenario/test/integration/metrics.rb
|
|
489
490
|
- lib/gitlab/qa/scenario/test/integration/mtls.rb
|
|
490
491
|
- lib/gitlab/qa/scenario/test/integration/opensearch.rb
|
|
@@ -535,7 +536,9 @@ files:
|
|
|
535
536
|
homepage: http://about.gitlab.com/
|
|
536
537
|
licenses:
|
|
537
538
|
- MIT
|
|
538
|
-
metadata:
|
|
539
|
+
metadata:
|
|
540
|
+
source_code_uri: https://gitlab.com/gitlab-org/gitlab-qa
|
|
541
|
+
bug_tracker_uri: https://gitlab.com/gitlab-org/gitlab-qa/-/issues
|
|
539
542
|
post_install_message:
|
|
540
543
|
rdoc_options: []
|
|
541
544
|
require_paths:
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Gitlab
|
|
4
|
-
module QA
|
|
5
|
-
module Scenario
|
|
6
|
-
module Test
|
|
7
|
-
module Integration
|
|
8
|
-
class Mattermost < Scenario::Template
|
|
9
|
-
def perform(release, *rspec_args)
|
|
10
|
-
Component::Gitlab.perform do |gitlab|
|
|
11
|
-
gitlab.release = release
|
|
12
|
-
gitlab.network = Runtime::Env.docker_network
|
|
13
|
-
|
|
14
|
-
mattermost_hostname = "mattermost.#{gitlab.network}"
|
|
15
|
-
mattermost_external_url = "http://#{mattermost_hostname}"
|
|
16
|
-
|
|
17
|
-
gitlab.add_network_alias(mattermost_hostname)
|
|
18
|
-
gitlab.omnibus_configuration << <<~OMNIBUS
|
|
19
|
-
mattermost_external_url '#{mattermost_external_url}';
|
|
20
|
-
OMNIBUS
|
|
21
|
-
|
|
22
|
-
gitlab.instance do
|
|
23
|
-
Component::Specs.perform do |specs|
|
|
24
|
-
specs.suite = 'Test::Integration::Mattermost'
|
|
25
|
-
specs.release = gitlab.release
|
|
26
|
-
specs.network = gitlab.network
|
|
27
|
-
specs.args = [
|
|
28
|
-
gitlab.address,
|
|
29
|
-
"--mattermost-address", mattermost_external_url,
|
|
30
|
-
*rspec_args
|
|
31
|
-
]
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|