gitlab_quality-test_tooling 2.1.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +10 -0
- data/exe/detect-infrastructure-failures +31 -0
- data/lib/gitlab_quality/test_tooling/gitlab_client/job_client.rb +29 -0
- data/lib/gitlab_quality/test_tooling/job_trace_analyzer.rb +68 -0
- data/lib/gitlab_quality/test_tooling/report/concerns/issue_reports.rb +38 -11
- data/lib/gitlab_quality/test_tooling/test_results/json_test_results.rb +5 -0
- data/lib/gitlab_quality/test_tooling/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 349ae941a991eb8ad7c3c863cc0b840c84541d32ff742948bf06796be3b5263a
|
4
|
+
data.tar.gz: f23b333320ea5b242d7bda47f895fa6d56c8c63f2206657353749980a7470b6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 489db862072fc1f574e35de9f5eae7c8adc0909a5c1c76d3e7d30d9f0c8633b9af0e5c0b7e26cc80802bf2ca8afb599ed151133ce1fb13b2fbe719a01a9e9ede
|
7
|
+
data.tar.gz: 4cba020c783aabde06a85dc48bd68b95f97b5df9fbe0471ff85f7e1d7788912404a2e996a1a95b54e6d276fc0e1c48f7d9b6202fb6f3231f85e5a4bd0da85a29
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -191,6 +191,16 @@ Usage: exe/existing-test-health-issue [options]
|
|
191
191
|
-h, --help Show the usage
|
192
192
|
```
|
193
193
|
|
194
|
+
### `exe/detect-infrastructure-failures`
|
195
|
+
|
196
|
+
```shell
|
197
|
+
Purpose: Checks wether a job failed on a known infrastructure error by parsing its trace.
|
198
|
+
Usage: exe/detect-infrastructure-failures [options]
|
199
|
+
-j, --job-id JOB_ID A valid Job ID
|
200
|
+
-p, --project PROJECT Can be an integer or a group/project string
|
201
|
+
-t, --token TOKEN A valid access token with `api` scope and Maintainer permission in PROJECT
|
202
|
+
```
|
203
|
+
|
194
204
|
### `exe/flaky-test-issues`
|
195
205
|
|
196
206
|
```shell
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "optparse"
|
6
|
+
|
7
|
+
require_relative "../lib/gitlab_quality/test_tooling"
|
8
|
+
|
9
|
+
params = {}
|
10
|
+
|
11
|
+
OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
13
|
+
|
14
|
+
opts.on('-j', '--job-id JOB_ID', String, 'A valid job ID') do |job_id|
|
15
|
+
params[:job_id] = job_id
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on('-p', '--project PROJECT', String, 'Can be an integer or a group/project string') do |project|
|
19
|
+
params[:project] = project
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on('-t', '--token TOKEN', String, 'A valid access token with `api` scope and Maintainer permission in PROJECT') do |token|
|
23
|
+
params[:token] = token
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.parse(ARGV)
|
27
|
+
end
|
28
|
+
|
29
|
+
raise ArgumentError, "Missing argument(s). Required arguments are: --job-id, --project, --token" if params.empty? || ([:job_id, :project, :token] - params.keys).any?
|
30
|
+
|
31
|
+
exit GitlabQuality::TestTooling::JobTraceAnalyzer.new(**params).found_infrastructure_error? ? 0 : 1
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gitlab'
|
4
|
+
|
5
|
+
module GitlabQuality
|
6
|
+
module TestTooling
|
7
|
+
module GitlabClient
|
8
|
+
class JobClient < GitlabClient
|
9
|
+
attr_reader :job_id
|
10
|
+
|
11
|
+
def initialize(token:, project:, job_id:)
|
12
|
+
super
|
13
|
+
|
14
|
+
@job_id = job_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def job_trace
|
18
|
+
trace = ''
|
19
|
+
|
20
|
+
ignore_gitlab_client_exceptions do
|
21
|
+
trace = client.job_trace(project, job_id)
|
22
|
+
end
|
23
|
+
|
24
|
+
trace
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'gitlab_client/job_client'
|
4
|
+
|
5
|
+
module GitlabQuality
|
6
|
+
module TestTooling
|
7
|
+
class JobTraceAnalyzer
|
8
|
+
attr_reader :project, :token, :job_id
|
9
|
+
|
10
|
+
TRANSIENT_ROOT_CAUSE_TO_TRACE_MAP =
|
11
|
+
{
|
12
|
+
failed_to_pull_image: ['job failed: failed to pull image'],
|
13
|
+
gitlab_com_overloaded: ['gitlab is currently unable to handle this request due to load'],
|
14
|
+
runner_disk_full: [
|
15
|
+
'no space left on device',
|
16
|
+
'Check free disk space'
|
17
|
+
],
|
18
|
+
job_timeout: [
|
19
|
+
'ERROR: Job failed: execution took longer than',
|
20
|
+
'Rspec suite is exceeding the 80 minute limit and is forced to exit with error'
|
21
|
+
],
|
22
|
+
gitaly: ['gitaly spawn failed'],
|
23
|
+
infrastructure: [
|
24
|
+
'the requested url returned error: 5', # any 5XX error code should be transient
|
25
|
+
'error: downloading artifacts from coordinator',
|
26
|
+
'error: uploading artifacts as "archive" to coordinator',
|
27
|
+
'500 Internal Server Error',
|
28
|
+
"Internal Server Error 500",
|
29
|
+
'502 Bad Gateway',
|
30
|
+
'503 Service Unavailable',
|
31
|
+
'Error: EEXIST: file already exists',
|
32
|
+
'Failed to connect to 127.0.0.1',
|
33
|
+
"Failed to open TCP connection to",
|
34
|
+
'connection reset by peer',
|
35
|
+
'segmentation fault',
|
36
|
+
'no space left on device',
|
37
|
+
'Check free disk space',
|
38
|
+
'CLUSTERDOWN'
|
39
|
+
],
|
40
|
+
flaky_test: [
|
41
|
+
"We have detected a PG::QueryCanceled error in the specs, so we're failing early"
|
42
|
+
]
|
43
|
+
}.freeze
|
44
|
+
|
45
|
+
def initialize(project:, token:, job_id:)
|
46
|
+
@project = project
|
47
|
+
@token = token
|
48
|
+
@job_id = job_id
|
49
|
+
end
|
50
|
+
|
51
|
+
def found_infrastructure_error?
|
52
|
+
TRANSIENT_ROOT_CAUSE_TO_TRACE_MAP[:infrastructure].any? do |search_string|
|
53
|
+
found = job_trace.downcase.include?(search_string.downcase)
|
54
|
+
|
55
|
+
puts "Found infrastructure error stacktrace: #{search_string}" if found
|
56
|
+
|
57
|
+
found
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def job_trace
|
64
|
+
@job_trace ||= GitlabClient::JobClient.new(project: project, token: token, job_id: job_id).job_trace
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -11,13 +11,15 @@ module GitlabQuality
|
|
11
11
|
FAILED_JOB_DESCRIPTION_REGEX = /First happened in #{JOB_URL_REGEX}\./m
|
12
12
|
REPORT_ITEM_REGEX = /^1\. (?<report_date>\d{4}-\d{2}-\d{2}): #{JOB_URL_REGEX} \((?<pipeline_url>\S+)\) ?(?<extra_content>.*)$/
|
13
13
|
LATEST_REPORTS_TO_SHOW = 10
|
14
|
+
DISPLAYED_HISTORY_REPORTS_THRESHOLD = 510 # https://gitlab.com/gitlab-org/quality/engineering-productivity/team/-/issues/587
|
14
15
|
DAILY_REPORTS_THRESHOLDS = 10
|
15
16
|
|
16
17
|
class ReportsList
|
17
|
-
def initialize(preserved_content:, section_header:, reports:, extra_content:)
|
18
|
+
def initialize(preserved_content:, section_header:, reports:, total_reports_count:, extra_content:)
|
18
19
|
@preserved_content = preserved_content
|
19
20
|
@section_header = section_header
|
20
21
|
@reports = reports
|
22
|
+
@total_reports_count = total_reports_count
|
21
23
|
@extra_content = extra_content
|
22
24
|
end
|
23
25
|
|
@@ -31,14 +33,14 @@ module GitlabQuality
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def reports_count
|
34
|
-
|
36
|
+
total_reports_count
|
35
37
|
end
|
36
38
|
|
37
39
|
def to_s
|
38
40
|
[
|
39
41
|
preserved_content,
|
40
42
|
"#{section_header} (#{reports_count})",
|
41
|
-
reports_list,
|
43
|
+
reports_list(total_reports_count),
|
42
44
|
extra_content
|
43
45
|
].reject(&:blank?).compact.join("\n\n")
|
44
46
|
end
|
@@ -53,15 +55,16 @@ module GitlabQuality
|
|
53
55
|
|
54
56
|
private
|
55
57
|
|
56
|
-
attr_reader :preserved_content, :section_header, :reports, :extra_content
|
58
|
+
attr_reader :preserved_content, :section_header, :reports, :total_reports_count, :extra_content
|
57
59
|
|
58
|
-
def reports_list
|
60
|
+
def reports_list(total_reports_count)
|
59
61
|
if sorted_reports.size > LATEST_REPORTS_TO_SHOW
|
62
|
+
max_displayed_reports = DISPLAYED_HISTORY_REPORTS_THRESHOLD - LATEST_REPORTS_TO_SHOW
|
60
63
|
[
|
61
64
|
"Last #{LATEST_REPORTS_TO_SHOW} reports:",
|
62
|
-
|
63
|
-
"<details><summary>
|
64
|
-
|
65
|
+
displayed_reports[...LATEST_REPORTS_TO_SHOW].join("\n"),
|
66
|
+
"<details><summary>With #{total_reports_count - LATEST_REPORTS_TO_SHOW} more reports (displaying up to #{max_displayed_reports} reports) </summary>",
|
67
|
+
displayed_reports[LATEST_REPORTS_TO_SHOW..].join("\n"),
|
65
68
|
"</details>"
|
66
69
|
].join("\n\n")
|
67
70
|
else
|
@@ -70,7 +73,12 @@ module GitlabQuality
|
|
70
73
|
end
|
71
74
|
|
72
75
|
def sorted_reports
|
73
|
-
@sorted_reports ||=
|
76
|
+
@sorted_reports ||=
|
77
|
+
reports.sort_by { |report| [report.report_date, report.job_id, report.to_s] }.reverse
|
78
|
+
end
|
79
|
+
|
80
|
+
def displayed_reports
|
81
|
+
sorted_reports[0...DISPLAYED_HISTORY_REPORTS_THRESHOLD]
|
74
82
|
end
|
75
83
|
end
|
76
84
|
|
@@ -88,8 +96,8 @@ module GitlabQuality
|
|
88
96
|
"1. #{report_date}: #{job_url} (#{pipeline_url}) #{extra_content}".strip
|
89
97
|
end
|
90
98
|
|
91
|
-
def
|
92
|
-
|
99
|
+
def job_id
|
100
|
+
job_url.split('/').last
|
93
101
|
end
|
94
102
|
|
95
103
|
private
|
@@ -122,14 +130,33 @@ module GitlabQuality
|
|
122
130
|
preserved_content = current_reports_content.split(reports_section_header).first&.strip
|
123
131
|
reports = report_lines(current_reports_content) + [ReportsList.report_list_item(test, item_extra_content: item_extra_content)]
|
124
132
|
|
133
|
+
total_reports_count = increment_total_reports_count(
|
134
|
+
reports_section_header: reports_section_header,
|
135
|
+
content: current_reports_content,
|
136
|
+
reports: reports
|
137
|
+
)
|
138
|
+
|
125
139
|
ReportsList.new(
|
126
140
|
preserved_content: preserved_content,
|
127
141
|
section_header: reports_section_header,
|
128
142
|
reports: reports,
|
143
|
+
total_reports_count: total_reports_count,
|
129
144
|
extra_content: reports_extra_content
|
130
145
|
)
|
131
146
|
end
|
132
147
|
|
148
|
+
def increment_total_reports_count(reports_section_header:, content:, reports:)
|
149
|
+
reports_count = reports.count
|
150
|
+
|
151
|
+
return reports_count if reports_count < DISPLAYED_HISTORY_REPORTS_THRESHOLD
|
152
|
+
|
153
|
+
count_match = content.match(/#{Regexp.escape(reports_section_header)} \((\d+)\)/)
|
154
|
+
|
155
|
+
return reports_count unless count_match
|
156
|
+
|
157
|
+
count_match[1].to_i + 1
|
158
|
+
end
|
159
|
+
|
133
160
|
def failed_issue_job_url(issue)
|
134
161
|
job_urls_from_description(issue.description, REPORT_ITEM_REGEX).last ||
|
135
162
|
# Legacy format
|
@@ -16,9 +16,14 @@ module GitlabQuality
|
|
16
16
|
|
17
17
|
def parse
|
18
18
|
JSON.parse(File.read(path))
|
19
|
+
rescue JSON::ParserError
|
20
|
+
Runtime::Logger.debug("#{self.class.name}##{__method__} attempted to parse invalid JSON at path: #{path}")
|
21
|
+
{}
|
19
22
|
end
|
20
23
|
|
21
24
|
def process
|
25
|
+
return [] if results.empty?
|
26
|
+
|
22
27
|
results['examples'].map do |test|
|
23
28
|
GitlabQuality::TestTooling::TestResult::JsonTestResult.new(report: test, project: project, token: token)
|
24
29
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitlab_quality-test_tooling
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.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: 2024-
|
11
|
+
date: 2024-11-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|
@@ -414,6 +414,7 @@ description: A collection of test-related tools.
|
|
414
414
|
email:
|
415
415
|
- quality@gitlab.com
|
416
416
|
executables:
|
417
|
+
- detect-infrastructure-failures
|
417
418
|
- existing-test-health-issue
|
418
419
|
- failed-test-issues
|
419
420
|
- flaky-test-issues
|
@@ -444,6 +445,7 @@ files:
|
|
444
445
|
- LICENSE.txt
|
445
446
|
- README.md
|
446
447
|
- Rakefile
|
448
|
+
- exe/detect-infrastructure-failures
|
447
449
|
- exe/existing-test-health-issue
|
448
450
|
- exe/failed-test-issues
|
449
451
|
- exe/flaky-test-issues
|
@@ -468,10 +470,12 @@ files:
|
|
468
470
|
- lib/gitlab_quality/test_tooling/gitlab_client/gitlab_client.rb
|
469
471
|
- lib/gitlab_quality/test_tooling/gitlab_client/issues_client.rb
|
470
472
|
- lib/gitlab_quality/test_tooling/gitlab_client/issues_dry_client.rb
|
473
|
+
- lib/gitlab_quality/test_tooling/gitlab_client/job_client.rb
|
471
474
|
- lib/gitlab_quality/test_tooling/gitlab_client/jobs_client.rb
|
472
475
|
- lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_client.rb
|
473
476
|
- lib/gitlab_quality/test_tooling/gitlab_client/merge_requests_dry_client.rb
|
474
477
|
- lib/gitlab_quality/test_tooling/gitlab_client/repository_files_client.rb
|
478
|
+
- lib/gitlab_quality/test_tooling/job_trace_analyzer.rb
|
475
479
|
- lib/gitlab_quality/test_tooling/knapsack_reports/spec_run_time.rb
|
476
480
|
- lib/gitlab_quality/test_tooling/knapsack_reports/spec_run_time_report.rb
|
477
481
|
- lib/gitlab_quality/test_tooling/labels_inference.rb
|