gitlab-qa 10.4.1 → 11.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitlab/changelog_config.yml +13 -0
- data/.gitlab/merge_request_templates/Release.md +9 -36
- data/.rubocop_todo.yml +0 -12
- data/Dangerfile +1 -5
- data/Gemfile.lock +4 -4
- data/README.md +1 -2
- data/docs/running_against_remote_grid.md +39 -2
- data/gitlab-qa.gemspec +1 -1
- data/lib/gitlab/qa/component/gitaly_cluster.rb +0 -1
- data/lib/gitlab/qa/component/gitlab.rb +10 -9
- data/lib/gitlab/qa/component/selenoid.rb +8 -3
- data/lib/gitlab/qa/runtime/env.rb +21 -41
- data/lib/gitlab/qa/scenario/test/instance/airgapped.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/gitaly_cluster.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/mtls.rb +0 -1
- data/lib/gitlab/qa/scenario/test/integration/praefect.rb +0 -2
- data/lib/gitlab/qa/scenario/test/integration/registry_with_cdn.rb +2 -2
- data/lib/gitlab/qa/version.rb +1 -1
- data/lib/gitlab/qa.rb +0 -1
- data/support/data/admin_access_token_seed.rb +4 -1
- metadata +5 -39
- data/bin/slack +0 -14
- data/exe/gitlab-qa-report +0 -10
- data/lib/gitlab/qa/report/base_test_results.rb +0 -39
- data/lib/gitlab/qa/report/find_set_dri.rb +0 -43
- data/lib/gitlab/qa/report/generate_test_session.rb +0 -275
- data/lib/gitlab/qa/report/gitlab_issue_client.rb +0 -190
- data/lib/gitlab/qa/report/gitlab_issue_dry_client.rb +0 -28
- data/lib/gitlab/qa/report/j_unit_test_results.rb +0 -27
- data/lib/gitlab/qa/report/json_test_results.rb +0 -29
- data/lib/gitlab/qa/report/prepare_stage_reports.rb +0 -86
- data/lib/gitlab/qa/report/relate_failure_issue.rb +0 -374
- data/lib/gitlab/qa/report/report_as_issue.rb +0 -176
- data/lib/gitlab/qa/report/report_results.rb +0 -64
- data/lib/gitlab/qa/report/results_in_issues.rb +0 -126
- data/lib/gitlab/qa/report/results_in_testcases.rb +0 -111
- data/lib/gitlab/qa/report/results_reporter_shared.rb +0 -70
- data/lib/gitlab/qa/report/summary_table.rb +0 -43
- data/lib/gitlab/qa/report/test_result.rb +0 -184
- data/lib/gitlab/qa/report/update_screenshot_path.rb +0 -63
- data/lib/gitlab/qa/reporter.rb +0 -131
- data/lib/gitlab/qa/runtime/token_finder.rb +0 -44
- data/lib/gitlab/qa/slack/post_to_slack.rb +0 -30
- data/lib/gitlab/qa/system_logs/finders/json_log_finder.rb +0 -65
- data/lib/gitlab/qa/system_logs/finders/rails/api_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/application_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/exception_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/finders/rails/graphql_log_finder.rb +0 -21
- data/lib/gitlab/qa/system_logs/log_types/log.rb +0 -38
- data/lib/gitlab/qa/system_logs/log_types/rails/api_log.rb +0 -34
- data/lib/gitlab/qa/system_logs/log_types/rails/application_log.rb +0 -27
- data/lib/gitlab/qa/system_logs/log_types/rails/exception_log.rb +0 -23
- data/lib/gitlab/qa/system_logs/log_types/rails/graphql_log.rb +0 -30
- data/lib/gitlab/qa/system_logs/shared_fields.rb +0 -29
- data/lib/gitlab/qa/system_logs/system_logs_formatter.rb +0 -65
@@ -1,184 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_support/core_ext/object/blank'
|
4
|
-
|
5
|
-
module Gitlab
|
6
|
-
module QA
|
7
|
-
module Report
|
8
|
-
class TestResult
|
9
|
-
def self.from_json(report)
|
10
|
-
JsonTestResult.new(report)
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.from_junit(report)
|
14
|
-
JUnitTestResult.new(report)
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_accessor :report, :failures
|
18
|
-
|
19
|
-
def initialize(report)
|
20
|
-
self.report = report
|
21
|
-
self.failures = failures_from_exceptions
|
22
|
-
end
|
23
|
-
|
24
|
-
def stage
|
25
|
-
@stage ||= file[%r{(?:api|browser_ui)/(?:(?:\d+_)?(\w+))}, 1]
|
26
|
-
end
|
27
|
-
|
28
|
-
def name
|
29
|
-
raise NotImplementedError
|
30
|
-
end
|
31
|
-
|
32
|
-
def file
|
33
|
-
raise NotImplementedError
|
34
|
-
end
|
35
|
-
|
36
|
-
def skipped
|
37
|
-
raise NotImplementedError
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def failures_from_exceptions
|
43
|
-
raise NotImplementedError
|
44
|
-
end
|
45
|
-
|
46
|
-
class JsonTestResult < TestResult
|
47
|
-
def name
|
48
|
-
report['full_description']
|
49
|
-
end
|
50
|
-
|
51
|
-
def file
|
52
|
-
report['file_path']
|
53
|
-
end
|
54
|
-
|
55
|
-
def status
|
56
|
-
report['status']
|
57
|
-
end
|
58
|
-
|
59
|
-
def ci_job_url
|
60
|
-
report['ci_job_url']
|
61
|
-
end
|
62
|
-
|
63
|
-
def skipped
|
64
|
-
status == 'pending'
|
65
|
-
end
|
66
|
-
|
67
|
-
def testcase
|
68
|
-
report['testcase']
|
69
|
-
end
|
70
|
-
|
71
|
-
def testcase=(new_testcase)
|
72
|
-
report['testcase'] = new_testcase
|
73
|
-
end
|
74
|
-
|
75
|
-
def failure_issue
|
76
|
-
report['failure_issue']
|
77
|
-
end
|
78
|
-
|
79
|
-
def failure_issue=(new_failure_issue)
|
80
|
-
report['failure_issue'] = new_failure_issue
|
81
|
-
end
|
82
|
-
|
83
|
-
def quarantine?
|
84
|
-
# The value for 'quarantine' could be nil, a hash, a string,
|
85
|
-
# or true (if the test just has the :quarantine tag)
|
86
|
-
# But any non-nil or false value should means the test is in quarantine
|
87
|
-
report['quarantine'].present?
|
88
|
-
end
|
89
|
-
|
90
|
-
def quarantine_type
|
91
|
-
report['quarantine']['type'] if quarantine?
|
92
|
-
end
|
93
|
-
|
94
|
-
def quarantine_issue
|
95
|
-
report['quarantine']['issue'] if quarantine?
|
96
|
-
end
|
97
|
-
|
98
|
-
def screenshot?
|
99
|
-
report['screenshot'].present?
|
100
|
-
end
|
101
|
-
|
102
|
-
def failure_screenshot
|
103
|
-
report['screenshot']['image'] if screenshot?
|
104
|
-
end
|
105
|
-
|
106
|
-
def product_group?
|
107
|
-
report['product_group'].present?
|
108
|
-
end
|
109
|
-
|
110
|
-
def product_group
|
111
|
-
report['product_group'] if product_group?
|
112
|
-
end
|
113
|
-
|
114
|
-
private
|
115
|
-
|
116
|
-
# rubocop:disable Metrics/AbcSize
|
117
|
-
def failures_from_exceptions
|
118
|
-
return [] unless report.key?('exceptions')
|
119
|
-
|
120
|
-
report['exceptions'].map do |exception|
|
121
|
-
spec_file_first_index = exception['backtrace'].rindex do |line|
|
122
|
-
line.include?(File.basename(report['file_path']))
|
123
|
-
end
|
124
|
-
|
125
|
-
exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
|
126
|
-
Array(exception['message_lines']).each { |line| line.gsub!(/(private_token=)([\w-]+)/, '********') }
|
127
|
-
|
128
|
-
{
|
129
|
-
'message' => "#{exception['class']}: #{exception['message']}",
|
130
|
-
'message_lines' => exception['message_lines'],
|
131
|
-
'stacktrace' => "#{format_message_lines(exception['message_lines'])}\n#{exception['backtrace'].slice(0..spec_file_first_index).join("\n")}",
|
132
|
-
'correlation_id' => exception['correlation_id']
|
133
|
-
}
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def format_message_lines(message_lines)
|
138
|
-
message_lines.is_a?(Array) ? message_lines.join("\n") : message_lines
|
139
|
-
end
|
140
|
-
# rubocop:enable Metrics/AbcSize
|
141
|
-
end
|
142
|
-
|
143
|
-
class JUnitTestResult < TestResult
|
144
|
-
def name
|
145
|
-
report['name']
|
146
|
-
end
|
147
|
-
|
148
|
-
def file
|
149
|
-
report['file']
|
150
|
-
end
|
151
|
-
|
152
|
-
def skipped
|
153
|
-
report.search('skipped').any?
|
154
|
-
end
|
155
|
-
|
156
|
-
attr_accessor :testcase # Ignore it for now
|
157
|
-
|
158
|
-
private
|
159
|
-
|
160
|
-
# rubocop:disable Metrics/AbcSize
|
161
|
-
def failures_from_exceptions
|
162
|
-
failures = report.search('failure')
|
163
|
-
return [] if failures.empty?
|
164
|
-
|
165
|
-
failures.map do |exception|
|
166
|
-
trace = exception.content.split("\n").map(&:strip)
|
167
|
-
spec_file_first_index = trace.rindex do |line|
|
168
|
-
line.include?(File.basename(report['file']))
|
169
|
-
end
|
170
|
-
|
171
|
-
exception['message'].gsub!(/(private_token=)[\w-]+/, '********')
|
172
|
-
|
173
|
-
{
|
174
|
-
'message' => "#{exception['type']}: #{exception['message']}",
|
175
|
-
'stacktrace' => trace.slice(0..spec_file_first_index).join("\n")
|
176
|
-
}
|
177
|
-
end
|
178
|
-
end
|
179
|
-
# rubocop:enable Metrics/AbcSize
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'nokogiri'
|
4
|
-
require 'json'
|
5
|
-
require 'active_support/core_ext/object/blank'
|
6
|
-
|
7
|
-
module Gitlab
|
8
|
-
module QA
|
9
|
-
module Report
|
10
|
-
class UpdateScreenshotPath
|
11
|
-
def initialize(files:)
|
12
|
-
@files = files
|
13
|
-
end
|
14
|
-
|
15
|
-
REGEX = %r{(?<gitlab_qa_run>gitlab-qa-run-.*?(?=\/))\/(?<gitlab_ce_ee_qa>gitlab-(ee|ce)-qa-.*?(?=\/))}
|
16
|
-
CONTAINER_PATH = File.join(Docker::Volumes::QA_CONTAINER_WORKDIR, 'tmp').freeze
|
17
|
-
|
18
|
-
def invoke!
|
19
|
-
Dir.glob(@files).each do |rspec_report_file|
|
20
|
-
match_data = rspec_report_file.match(REGEX)
|
21
|
-
artifact_path = "#{match_data[:gitlab_qa_run]}/#{match_data[:gitlab_ce_ee_qa]}"
|
22
|
-
|
23
|
-
xml_report = rewrite_each_xml_screenshot_path(rspec_report_file, artifact_path)
|
24
|
-
|
25
|
-
File.write(rspec_report_file, xml_report)
|
26
|
-
|
27
|
-
puts "Saved #{rspec_report_file}"
|
28
|
-
|
29
|
-
json_rspec_report_file = rspec_report_file.gsub('.xml', '.json')
|
30
|
-
json_report = rewrite_each_json_screenshot_path(json_rspec_report_file, artifact_path)
|
31
|
-
|
32
|
-
File.write(json_rspec_report_file, json_report)
|
33
|
-
|
34
|
-
puts "Saved #{json_rspec_report_file}"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def rewrite_each_xml_screenshot_path(rspec_report_file, artifact_path)
|
41
|
-
report = Nokogiri::XML(File.open(rspec_report_file))
|
42
|
-
|
43
|
-
report.xpath('//system-out').each do |system_out|
|
44
|
-
system_out.content = system_out.content.gsub(CONTAINER_PATH, artifact_path)
|
45
|
-
end
|
46
|
-
|
47
|
-
report.to_s
|
48
|
-
end
|
49
|
-
|
50
|
-
def rewrite_each_json_screenshot_path(json_rspec_report_file, artifact_path)
|
51
|
-
report = JSON.parse(File.read(json_rspec_report_file))
|
52
|
-
examples = report['examples']
|
53
|
-
|
54
|
-
examples.each do |example|
|
55
|
-
example['screenshot']['image'] = example['screenshot']['image'].gsub(CONTAINER_PATH, artifact_path) if example['screenshot'].present?
|
56
|
-
end
|
57
|
-
|
58
|
-
JSON.pretty_generate(report)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
data/lib/gitlab/qa/reporter.rb
DELETED
@@ -1,131 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'optparse'
|
4
|
-
|
5
|
-
module Gitlab
|
6
|
-
module QA
|
7
|
-
class Reporter
|
8
|
-
# rubocop:disable Metrics/AbcSize
|
9
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
10
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
11
|
-
def self.invoke(args)
|
12
|
-
report_options = {}
|
13
|
-
slack_options = {}
|
14
|
-
|
15
|
-
options = OptionParser.new do |opts|
|
16
|
-
opts.banner = 'Usage: gitlab-qa-reporter [options]'
|
17
|
-
|
18
|
-
opts.on('--prepare-stage-reports FILES', 'Prepare separate reports for each Stage from the provided JUnit XML files') do |files|
|
19
|
-
report_options[:prepare_stage_reports] = true
|
20
|
-
report_options[:input_files] = files if files
|
21
|
-
end
|
22
|
-
|
23
|
-
opts.on('--report-results FILES', String, 'Report test results from JUnit XML files in GitLab test cases and results issues') do |files|
|
24
|
-
report_options[:report_results] = true
|
25
|
-
report_options[:input_files] = files if files
|
26
|
-
end
|
27
|
-
|
28
|
-
opts.on('--relate-failure-issue FILES', String, 'Relate test failures to failure issues from RSpec JSON files') do |files|
|
29
|
-
report_options[:relate_failure_issue] = true
|
30
|
-
report_options[:input_files] = files if files
|
31
|
-
end
|
32
|
-
|
33
|
-
opts.on('--max-diff-ratio DIFF_RATO', Float, 'Max stacktrace diff ratio for QA failure issues detection. Used by with --relate-failure-issue') do |value|
|
34
|
-
report_options[:max_diff_ratio] = value
|
35
|
-
end
|
36
|
-
|
37
|
-
opts.on('-p', '--project PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --relate-failure-issue') do |value|
|
38
|
-
report_options[:project] = value
|
39
|
-
end
|
40
|
-
|
41
|
-
opts.on('--results-issue-project RESULTS_ISSUE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
|
42
|
-
report_options[:results_issue_project] = value
|
43
|
-
end
|
44
|
-
|
45
|
-
opts.on('--test-case-project TEST_CASE_PROJECT_ID', String, 'A valid project ID. Can be an integer or a group/project string. Required by --report-results') do |value|
|
46
|
-
report_options[:test_case_project] = value
|
47
|
-
end
|
48
|
-
|
49
|
-
opts.on('--generate-test-session FILES', String, 'Generate test session report') do |files|
|
50
|
-
report_options[:generate_test_session] = true
|
51
|
-
report_options[:input_files] = files if files
|
52
|
-
end
|
53
|
-
|
54
|
-
opts.on('-t', '--token ACCESS_TOKEN', String, 'A valid access token. Required by --report-results and --relate-failure-issue') do |value|
|
55
|
-
report_options[:token] = value
|
56
|
-
end
|
57
|
-
|
58
|
-
opts.on('--post-to-slack MSG', 'Post message to slack') do |msg|
|
59
|
-
slack_options[:post_to_slack] = true
|
60
|
-
slack_options[:message] = msg
|
61
|
-
end
|
62
|
-
|
63
|
-
opts.on('--include-summary-table FILES', 'Create a results summary table to post to slack. To be used with --post-to-slack.') do |files|
|
64
|
-
raise 'This option should be used with --post-to-slack.' unless slack_options[:post_to_slack]
|
65
|
-
|
66
|
-
slack_options[:message] = slack_options[:message] + "\n\n" + Gitlab::QA::Report::SummaryTable.create(input_files: files)
|
67
|
-
end
|
68
|
-
|
69
|
-
opts.on('--include-system-log-errors FILES', String, 'Include errors from system logs in failure issues. To be used with --relate-failure-issue') do |files|
|
70
|
-
raise 'This option should be used with --relate-failure-issue.' unless report_options[:relate_failure_issue]
|
71
|
-
|
72
|
-
report_options[:system_logs] = files if files
|
73
|
-
end
|
74
|
-
|
75
|
-
opts.on('--update-screenshot-path FILES', "Update the path to screenshots to container's host") do |files|
|
76
|
-
report_options[:update_screenshot_path] = true
|
77
|
-
report_options[:files] = files
|
78
|
-
end
|
79
|
-
|
80
|
-
opts.on('--dry-run', "Perform a dry-run (don't create or update issues or test cases)") do |files|
|
81
|
-
report_options[:dry_run] = true
|
82
|
-
end
|
83
|
-
|
84
|
-
opts.on_tail('-v', '--version', 'Show the version') do
|
85
|
-
require 'gitlab/qa/version'
|
86
|
-
puts "#{$PROGRAM_NAME} : #{VERSION}"
|
87
|
-
exit
|
88
|
-
end
|
89
|
-
|
90
|
-
opts.on_tail('-h', '--help', 'Show the usage') do
|
91
|
-
puts opts
|
92
|
-
exit
|
93
|
-
end
|
94
|
-
|
95
|
-
opts.parse(args)
|
96
|
-
end
|
97
|
-
|
98
|
-
if args.any?
|
99
|
-
if report_options.delete(:prepare_stage_reports)
|
100
|
-
Gitlab::QA::Report::PrepareStageReports.new(**report_options).invoke!
|
101
|
-
|
102
|
-
elsif report_options.delete(:relate_failure_issue)
|
103
|
-
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
|
104
|
-
Gitlab::QA::Report::RelateFailureIssue.new(**report_options).invoke!
|
105
|
-
|
106
|
-
elsif report_options.delete(:report_results)
|
107
|
-
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
|
108
|
-
Gitlab::QA::Report::ReportResults.new(**report_options).invoke!
|
109
|
-
|
110
|
-
elsif report_options.delete(:generate_test_session)
|
111
|
-
report_options[:token] = Runtime::TokenFinder.find_token!(report_options[:token])
|
112
|
-
Gitlab::QA::Report::GenerateTestSession.new(**report_options).invoke!
|
113
|
-
|
114
|
-
elsif slack_options.delete(:post_to_slack)
|
115
|
-
Gitlab::QA::Slack::PostToSlack.new(**slack_options).invoke!
|
116
|
-
|
117
|
-
elsif report_options.delete(:update_screenshot_path)
|
118
|
-
Gitlab::QA::Report::UpdateScreenshotPath.new(**report_options).invoke!
|
119
|
-
|
120
|
-
end
|
121
|
-
else
|
122
|
-
puts options
|
123
|
-
exit 1
|
124
|
-
end
|
125
|
-
end
|
126
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
127
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
128
|
-
# rubocop:enable Metrics/AbcSize
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module Runtime
|
6
|
-
class TokenFinder
|
7
|
-
def self.find_token!(token, suffix: nil)
|
8
|
-
new(token, suffix).find_token!
|
9
|
-
end
|
10
|
-
|
11
|
-
attr_reader :token, :suffix
|
12
|
-
|
13
|
-
def initialize(token, suffix)
|
14
|
-
@token = token
|
15
|
-
@suffix = suffix
|
16
|
-
end
|
17
|
-
|
18
|
-
def find_token!
|
19
|
-
find_token_from_attrs || find_token_from_env || find_token_from_file
|
20
|
-
end
|
21
|
-
|
22
|
-
def find_token_from_attrs
|
23
|
-
token
|
24
|
-
end
|
25
|
-
|
26
|
-
def find_token_from_env
|
27
|
-
Env.qa_access_token
|
28
|
-
end
|
29
|
-
|
30
|
-
def find_token_from_file
|
31
|
-
@token_from_file ||= File.read(token_file_path).strip
|
32
|
-
rescue Errno::ENOENT
|
33
|
-
fail "Please provide a valid access token with the `-t/--token` option, the `GITLAB_QA_ACCESS_TOKEN` environment variable, or in the `#{token_file_path}` file!"
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def token_file_path
|
39
|
-
@token_file_path ||= File.expand_path("../api_token#{"_#{suffix}" if suffix}", __dir__)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module Slack
|
6
|
-
class PostToSlack
|
7
|
-
def initialize(message:, icon_emoji: Runtime::Env.slack_icon_emoji, channel: Runtime::Env.slack_qa_channel)
|
8
|
-
@channel = channel
|
9
|
-
@message = message
|
10
|
-
@icon_emoji = icon_emoji
|
11
|
-
end
|
12
|
-
|
13
|
-
def invoke!
|
14
|
-
Runtime::Env.require_slack_qa_channel! unless @channel
|
15
|
-
Runtime::Env.require_ci_slack_webhook_url!
|
16
|
-
|
17
|
-
params = {}
|
18
|
-
params['channel'] = @channel
|
19
|
-
params['username'] = "GitLab QA Bot"
|
20
|
-
params['icon_emoji'] = @icon_emoji
|
21
|
-
params['text'] = @message
|
22
|
-
|
23
|
-
url = Runtime::Env.ci_slack_webhook_url
|
24
|
-
|
25
|
-
Support::HttpRequest.make_http_request(method: 'post', url: url, params: params, show_response: true)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
@@ -1,65 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
module Gitlab
|
6
|
-
module QA
|
7
|
-
module SystemLogs
|
8
|
-
module Finders
|
9
|
-
class JsonLogFinder
|
10
|
-
def initialize(base_path, file_path)
|
11
|
-
@base_path = base_path
|
12
|
-
@file_path = file_path
|
13
|
-
end
|
14
|
-
|
15
|
-
def find(correlation_id)
|
16
|
-
log_file_path = "#{@base_path}/#{@file_path}"
|
17
|
-
logs = []
|
18
|
-
|
19
|
-
if File.exist?(log_file_path) && !correlation_id.nil?
|
20
|
-
File.foreach(log_file_path) do |line|
|
21
|
-
begin
|
22
|
-
json_line = JSON.parse(line, symbolize_names: true)
|
23
|
-
rescue JSON::ParserError
|
24
|
-
Runtime::Logger.debug("JsonLogFinder#find attempted to parse invalid JSON: #{line}")
|
25
|
-
|
26
|
-
next
|
27
|
-
end
|
28
|
-
|
29
|
-
if (json_line[:correlation_id])&.casecmp?(correlation_id)
|
30
|
-
normalized_line = normalize_keys(json_line)
|
31
|
-
logs << new_log(normalized_line)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
logs
|
37
|
-
end
|
38
|
-
|
39
|
-
def new_log(data)
|
40
|
-
raise 'abstract method new_log must be defined!'
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def normalize_keys(json_line)
|
46
|
-
normalized_hash = {}
|
47
|
-
|
48
|
-
json_line.each_key do |old_key|
|
49
|
-
key_string = old_key.to_s
|
50
|
-
|
51
|
-
if key_string.include?('.')
|
52
|
-
normalized_key = key_string.tr('.', '_').to_sym
|
53
|
-
normalized_hash[normalized_key] = json_line[old_key]
|
54
|
-
else
|
55
|
-
normalized_hash[old_key] = json_line[old_key]
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
normalized_hash
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module SystemLogs
|
6
|
-
module Finders
|
7
|
-
module Rails
|
8
|
-
class ApiLogFinder < JsonLogFinder
|
9
|
-
def initialize(base_path, file_path = 'gitlab-rails/api_json.log')
|
10
|
-
super(base_path, file_path)
|
11
|
-
end
|
12
|
-
|
13
|
-
def new_log(data)
|
14
|
-
LogTypes::Rails::ApiLog.new(data)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module SystemLogs
|
6
|
-
module Finders
|
7
|
-
module Rails
|
8
|
-
class ApplicationLogFinder < JsonLogFinder
|
9
|
-
def initialize(base_path, file_path = 'gitlab-rails/application_json.log')
|
10
|
-
super(base_path, file_path)
|
11
|
-
end
|
12
|
-
|
13
|
-
def new_log(data)
|
14
|
-
LogTypes::Rails::ApplicationLog.new(data)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module SystemLogs
|
6
|
-
module Finders
|
7
|
-
module Rails
|
8
|
-
class ExceptionLogFinder < JsonLogFinder
|
9
|
-
def initialize(base_path, file_path = 'gitlab-rails/exceptions_json.log')
|
10
|
-
super(base_path, file_path)
|
11
|
-
end
|
12
|
-
|
13
|
-
def new_log(data)
|
14
|
-
LogTypes::Rails::ExceptionLog.new(data)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module SystemLogs
|
6
|
-
module Finders
|
7
|
-
module Rails
|
8
|
-
class GraphqlLogFinder < JsonLogFinder
|
9
|
-
def initialize(base_path, file_path = 'gitlab-rails/graphql_json.log')
|
10
|
-
super(base_path, file_path)
|
11
|
-
end
|
12
|
-
|
13
|
-
def new_log(data)
|
14
|
-
LogTypes::Rails::GraphqlLog.new(data)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module SystemLogs
|
6
|
-
module LogTypes
|
7
|
-
class Log
|
8
|
-
def initialize(name, data)
|
9
|
-
@name = name
|
10
|
-
@data = data
|
11
|
-
end
|
12
|
-
|
13
|
-
attr_reader :name, :data
|
14
|
-
|
15
|
-
def summary_fields
|
16
|
-
[
|
17
|
-
:severity,
|
18
|
-
:correlation_id,
|
19
|
-
:time,
|
20
|
-
:message
|
21
|
-
]
|
22
|
-
end
|
23
|
-
|
24
|
-
def summary
|
25
|
-
summary = {}
|
26
|
-
|
27
|
-
summary_fields.each do |field|
|
28
|
-
value = data[field]
|
29
|
-
summary[field] = value unless value.nil?
|
30
|
-
end
|
31
|
-
|
32
|
-
summary
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Gitlab
|
4
|
-
module QA
|
5
|
-
module SystemLogs
|
6
|
-
module LogTypes
|
7
|
-
module Rails
|
8
|
-
class ApiLog < Log
|
9
|
-
include SharedFields::Exception
|
10
|
-
include SharedFields::Meta
|
11
|
-
|
12
|
-
def initialize(data)
|
13
|
-
super('Rails API', data)
|
14
|
-
end
|
15
|
-
|
16
|
-
def summary_fields
|
17
|
-
super.concat(
|
18
|
-
[
|
19
|
-
:method,
|
20
|
-
:path,
|
21
|
-
:status,
|
22
|
-
:params,
|
23
|
-
:api_error
|
24
|
-
],
|
25
|
-
exception_fields,
|
26
|
-
meta_fields
|
27
|
-
)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|