gitlab-qa 9.1.2 → 10.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-ci.yml +106 -149
- data/.simplecov +9 -0
- data/Gemfile.lock +15 -1
- data/README.md +5 -4
- data/docs/what_tests_can_be_run.md +73 -35
- data/gitlab-qa.gemspec +3 -0
- data/lib/gitlab/qa/component/base.rb +8 -1
- data/lib/gitlab/qa/component/gitaly.rb +1 -1
- data/lib/gitlab/qa/component/gitlab.rb +2 -2
- data/lib/gitlab/qa/component/mock_server.rb +13 -0
- data/lib/gitlab/qa/report/gitlab_issue_client.rb +1 -1
- data/lib/gitlab/qa/report/relate_failure_issue.rb +116 -20
- data/lib/gitlab/qa/report/report_as_issue.rb +1 -1
- data/lib/gitlab/qa/report/test_result.rb +2 -1
- data/lib/gitlab/qa/reporter.rb +8 -0
- data/lib/gitlab/qa/runtime/env.rb +17 -4
- data/lib/gitlab/qa/runtime/omnibus_configurations/decomposition_multiple_db.rb +1 -1
- data/lib/gitlab/qa/scenario/test/integration/oauth.rb +68 -0
- data/lib/gitlab/qa/scenario/test/omnibus/update_from_previous.rb +3 -2
- data/lib/gitlab/qa/system_logs/finders/json_log_finder.rb +65 -0
- data/lib/gitlab/qa/system_logs/finders/rails/api_log_finder.rb +21 -0
- data/lib/gitlab/qa/system_logs/finders/rails/application_log_finder.rb +21 -0
- data/lib/gitlab/qa/system_logs/finders/rails/exception_log_finder.rb +21 -0
- data/lib/gitlab/qa/system_logs/finders/rails/graphql_log_finder.rb +21 -0
- data/lib/gitlab/qa/system_logs/log_types/log.rb +38 -0
- data/lib/gitlab/qa/system_logs/log_types/rails/api_log.rb +34 -0
- data/lib/gitlab/qa/system_logs/log_types/rails/application_log.rb +27 -0
- data/lib/gitlab/qa/system_logs/log_types/rails/exception_log.rb +23 -0
- data/lib/gitlab/qa/system_logs/log_types/rails/graphql_log.rb +30 -0
- data/lib/gitlab/qa/system_logs/shared_fields.rb +29 -0
- data/lib/gitlab/qa/system_logs/system_logs_formatter.rb +65 -0
- data/lib/gitlab/qa/version.rb +1 -1
- data/scripts/build-package-and-test-env +15 -0
- metadata +59 -47
- data/.gitlab/ci/jobs/airgapped.gitlab-ci.yml +0 -23
- data/.gitlab/ci/jobs/base.gitlab-ci.yml +0 -273
- data/.gitlab/ci/jobs/chaos.gitlab-ci.yml +0 -22
- data/.gitlab/ci/jobs/cloud_activation.gitlab-ci.yml +0 -24
- data/.gitlab/ci/jobs/custom_parallel.gitlab-ci.yml +0 -21
- data/.gitlab/ci/jobs/decomposition_multiple_db.gitlab-ci.yml +0 -27
- data/.gitlab/ci/jobs/decomposition_single_db.gitlab-ci.yml +0 -25
- data/.gitlab/ci/jobs/ee_previous_to_ce_update.gitlab-ci.yml +0 -18
- data/.gitlab/ci/jobs/elasticsearch.gitlab-ci.yml +0 -20
- data/.gitlab/ci/jobs/geo.gitlab-ci.yml +0 -18
- data/.gitlab/ci/jobs/gitaly_cluster.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/gitlab_pages.gitlab-ci.yml +0 -19
- data/.gitlab/ci/jobs/group_saml.gitlab-ci.yml +0 -20
- data/.gitlab/ci/jobs/instance.gitlab-ci.yml +0 -55
- data/.gitlab/ci/jobs/instance_saml.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/integrations.gitlab-ci.yml +0 -14
- data/.gitlab/ci/jobs/jira.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/large_setup.gitlab-ci.yml +0 -19
- data/.gitlab/ci/jobs/ldap_no_server.gitlab-ci.yml +0 -20
- data/.gitlab/ci/jobs/ldap_no_tls.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/ldap_tls.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/mattermost.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/metrics.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/mtls.gitlab-ci.yml +0 -19
- data/.gitlab/ci/jobs/object_storage.gitlab-ci.yml +0 -49
- data/.gitlab/ci/jobs/object_storage_aws.gitlab-ci.yml +0 -25
- data/.gitlab/ci/jobs/object_storage_gcs.gitlab-ci.yml +0 -23
- data/.gitlab/ci/jobs/object_storage_registry_tls.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/omnibus_image.gitlab-ci.yml +0 -15
- data/.gitlab/ci/jobs/omnibus_upgrade.gitlab-ci.yml +0 -28
- data/.gitlab/ci/jobs/opensearch.gitlab-ci.yml +0 -20
- data/.gitlab/ci/jobs/packages.gitlab-ci.yml +0 -25
- data/.gitlab/ci/jobs/praefect.gitlab-ci.yml +0 -71
- data/.gitlab/ci/jobs/registry.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/registry_with_cdn.gitlab-ci.yml +0 -55
- data/.gitlab/ci/jobs/relative_url.gitlab-ci.yml +0 -65
- data/.gitlab/ci/jobs/repository_storage.gitlab-ci.yml +0 -41
- data/.gitlab/ci/jobs/sanity_framework.gitlab-ci.yml +0 -24
- data/.gitlab/ci/jobs/service_ping_disabled.gitlab-ci.yml +0 -19
- data/.gitlab/ci/jobs/smtp.gitlab-ci.yml +0 -19
- data/.gitlab/ci/jobs/staging.gitlab-ci.yml +0 -10
- data/.gitlab/ci/jobs/update.gitlab-ci.yml +0 -60
- data/.gitlab/ci/rules.gitlab-ci.yml +0 -183
- data/lib/gitlab/qa/scenario/test/omnibus/update.rb +0 -72
- data/scripts/generate-qa-jobs.rb +0 -99
@@ -13,15 +13,19 @@ module Gitlab
|
|
13
13
|
include FindSetDri
|
14
14
|
|
15
15
|
DEFAULT_MAX_DIFF_RATIO_FOR_DETECTION = 0.15
|
16
|
+
SPAM_THRESHOLD_FOR_FAILURE_ISSUES = 3
|
16
17
|
FAILURE_STACKTRACE_REGEX = %r{((.*Failure\/Error:(?<stacktrace>.+))|(?<stacktrace>.+))}m.freeze
|
17
18
|
ISSUE_STACKTRACE_REGEX = /### Stack trace\s*(```)#{FAILURE_STACKTRACE_REGEX}(```)/m.freeze
|
19
|
+
FAILED_JOB_DESCRIPTION_REGEX = %r{First happened in https?:\/\/\S+\.}m.freeze
|
20
|
+
FAILED_JOB_NOTE_REGEX = %r{Failed most recently in \D+ pipeline: https?:\/\/\S+}.freeze
|
18
21
|
NEW_ISSUE_LABELS = Set.new(%w[QA Quality test failure::new priority::2]).freeze
|
19
22
|
IGNORE_EXCEPTIONS = ['Net::ReadTimeout'].freeze
|
20
23
|
|
21
24
|
MultipleIssuesFound = Class.new(StandardError)
|
22
25
|
|
23
|
-
def initialize(max_diff_ratio: DEFAULT_MAX_DIFF_RATIO_FOR_DETECTION, **kwargs)
|
26
|
+
def initialize(system_logs: [], max_diff_ratio: DEFAULT_MAX_DIFF_RATIO_FOR_DETECTION, **kwargs)
|
24
27
|
super
|
28
|
+
@system_logs = Dir.glob(system_logs)
|
25
29
|
@max_diff_ratio = max_diff_ratio.to_f
|
26
30
|
@issue_type = 'issue'
|
27
31
|
@commented_issue_list = Set.new
|
@@ -60,17 +64,34 @@ module Gitlab
|
|
60
64
|
|
61
65
|
def find_and_link_issue(test)
|
62
66
|
issue, diff_ratio = find_failure_issue(test)
|
67
|
+
issue_already_commented = issue ? @commented_issue_list.include?(issue.web_url) : nil
|
63
68
|
|
64
|
-
if issue
|
69
|
+
if issue && !issue_already_commented
|
65
70
|
puts " => Found issue #{issue.web_url} for test '#{test.name}' with a diff ratio of #{(diff_ratio * 100).round(2)}%."
|
66
|
-
|
71
|
+
post_or_update_failed_job_note(issue, test)
|
67
72
|
@commented_issue_list.add(issue.web_url)
|
73
|
+
else
|
74
|
+
puts " => Failure already commented on issue." if issue_already_commented
|
75
|
+
|
76
|
+
return false
|
68
77
|
end
|
69
78
|
|
70
79
|
issue
|
71
80
|
end
|
72
81
|
|
73
82
|
def create_issue(test)
|
83
|
+
similar_issues = pipeline_issues_with_similar_stacktrace(test)
|
84
|
+
|
85
|
+
if similar_issues.size >= SPAM_THRESHOLD_FOR_FAILURE_ISSUES
|
86
|
+
puts " => Similar failure issues have already been opened for same pipeline environment"
|
87
|
+
puts " => Will not create new issue for this failing spec"
|
88
|
+
similar_issues.each do |similar_issue|
|
89
|
+
puts "Please check issue: #{similar_issue.web_url}"
|
90
|
+
gitlab.create_issue_note(iid: similar_issue.iid, note: "This failed job is most likely related: #{test.ci_job_url}")
|
91
|
+
end
|
92
|
+
return
|
93
|
+
end
|
94
|
+
|
74
95
|
issue = super
|
75
96
|
puts "for test '#{test.name}'."
|
76
97
|
|
@@ -81,6 +102,50 @@ module Gitlab
|
|
81
102
|
issue
|
82
103
|
end
|
83
104
|
|
105
|
+
def pipeline_issues_with_similar_stacktrace(test)
|
106
|
+
gitlab.find_issues(options: { state: 'opened', labels: 'QA,failure::new', created_after: past_timestamp(2) }).select do |issue|
|
107
|
+
job_url_from_issue = failed_issue_job_url(issue)
|
108
|
+
next unless pipeline == pipeline_env_from_job_url(job_url_from_issue)
|
109
|
+
|
110
|
+
stack_trace_from_issue = cleaned_stack_trace_from_issue(issue)
|
111
|
+
stack_trace_from_test = cleaned_stacktrace_from_test(test)
|
112
|
+
diff_ratio = compare_stack_traces(stack_trace_from_test, stack_trace_from_issue)
|
113
|
+
diff_ratio < max_diff_ratio
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def failed_issue_job_url(issue)
|
118
|
+
existing_note = existing_failure_note(issue)
|
119
|
+
if existing_note
|
120
|
+
job_url_string = existing_note.body
|
121
|
+
matched = job_url_string.match(FAILED_JOB_NOTE_REGEX)
|
122
|
+
else
|
123
|
+
job_url_string = issue.description
|
124
|
+
matched = job_url_string.match(FAILED_JOB_DESCRIPTION_REGEX)
|
125
|
+
end
|
126
|
+
|
127
|
+
return unless matched
|
128
|
+
|
129
|
+
job_url = matched[0].chop.split(" ").last
|
130
|
+
puts "=> Found failed job url in the issue: #{job_url}"
|
131
|
+
job_url
|
132
|
+
end
|
133
|
+
|
134
|
+
def pipeline_env_from_job_url(job_url)
|
135
|
+
return if job_url.nil?
|
136
|
+
|
137
|
+
if job_url.include?('/quality/')
|
138
|
+
job_url.partition('/quality/').last.partition('/').first
|
139
|
+
else
|
140
|
+
'master'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def past_timestamp(hours_ago)
|
145
|
+
timestamp = Time.now - (hours_ago * 60 * 60)
|
146
|
+
timestamp.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
147
|
+
end
|
148
|
+
|
84
149
|
def failure_issues(test)
|
85
150
|
gitlab.find_issues(options: { state: 'opened', labels: 'QA' }).select do |issue|
|
86
151
|
issue_title = issue.title.strip
|
@@ -96,20 +161,36 @@ module Gitlab
|
|
96
161
|
end
|
97
162
|
end
|
98
163
|
|
99
|
-
def
|
100
|
-
|
164
|
+
def cleaned_stack_trace_from_issue(issue)
|
165
|
+
relevant_issue_stacktrace = find_issue_stacktrace(issue)
|
166
|
+
return unless relevant_issue_stacktrace
|
167
|
+
|
168
|
+
remove_unique_resource_names(relevant_issue_stacktrace)
|
169
|
+
end
|
170
|
+
|
171
|
+
def cleaned_stacktrace_from_test(test)
|
101
172
|
first_test_failure_stacktrace = sanitize_stacktrace(full_stacktrace(test), FAILURE_STACKTRACE_REGEX) || full_stacktrace(test)
|
102
|
-
|
173
|
+
remove_unique_resource_names(first_test_failure_stacktrace)
|
174
|
+
end
|
175
|
+
|
176
|
+
def compare_stack_traces(stack_trace_first, stack_trace_second)
|
177
|
+
calculate_diff_ratio(stack_trace_first, stack_trace_second)
|
178
|
+
end
|
179
|
+
|
180
|
+
def calculate_diff_ratio(stack_trace_first, stack_trace_second)
|
181
|
+
ld = Class.new.extend(Gem::Text).method(:levenshtein_distance)
|
182
|
+
distance = ld.call(stack_trace_first, stack_trace_second)
|
183
|
+
distance.zero? ? 0.0 : (distance.to_f / stack_trace_first.size).round(3)
|
184
|
+
end
|
103
185
|
|
186
|
+
def find_relevant_failure_issues(test) # rubocop:disable Metrics/AbcSize
|
187
|
+
clean_first_test_failure_stacktrace = cleaned_stacktrace_from_test(test)
|
104
188
|
# Search with the `search` param returns 500 errors, so we filter by ~QA and then filter further in Ruby
|
105
189
|
failure_issues(test).each_with_object({}) do |issue, memo|
|
106
|
-
|
107
|
-
next
|
108
|
-
|
109
|
-
clean_relevant_issue_stacktrace = remove_unique_resource_names(relevant_issue_stacktrace)
|
110
|
-
distance = ld.call(clean_first_test_failure_stacktrace, clean_relevant_issue_stacktrace)
|
111
|
-
diff_ratio = distance.zero? ? 0.0 : (distance.to_f / clean_first_test_failure_stacktrace.size).round(3)
|
190
|
+
clean_relevant_issue_stacktrace = cleaned_stack_trace_from_issue(issue)
|
191
|
+
next if clean_relevant_issue_stacktrace.nil?
|
112
192
|
|
193
|
+
diff_ratio = compare_stack_traces(clean_first_test_failure_stacktrace, clean_relevant_issue_stacktrace)
|
113
194
|
if diff_ratio <= max_diff_ratio
|
114
195
|
puts " => [DEBUG] Issue #{issue.web_url} has an acceptable diff ratio of #{(diff_ratio * 100).round(2)}%."
|
115
196
|
# The `Gitlab::ObjectifiedHash` class overrides `#hash` which is used by `Hash#[]=` to compute the hash key.
|
@@ -120,8 +201,8 @@ module Gitlab
|
|
120
201
|
memo[issue.to_h] = diff_ratio
|
121
202
|
else
|
122
203
|
puts " => [DEBUG] Found issue #{issue.web_url} but stacktraces are too different (#{(diff_ratio * 100).round(2)}%).\n"
|
123
|
-
puts " => [DEBUG] Issue stacktrace:\n----------------\n#{
|
124
|
-
puts " => [DEBUG] Failure stacktrace:\n----------------\n#{
|
204
|
+
puts " => [DEBUG] Issue stacktrace:\n----------------\n#{clean_relevant_issue_stacktrace}\n----------------\n"
|
205
|
+
puts " => [DEBUG] Failure stacktrace:\n----------------\n#{clean_first_test_failure_stacktrace}\n----------------\n"
|
125
206
|
end
|
126
207
|
end
|
127
208
|
end
|
@@ -137,7 +218,7 @@ module Gitlab
|
|
137
218
|
stacktrace_match = stacktrace.match(regex)
|
138
219
|
|
139
220
|
if stacktrace_match
|
140
|
-
stacktrace_match[:stacktrace].gsub(/^\s*#.*$/, '').gsub(/^[[:space:]]+/, '').strip
|
221
|
+
stacktrace_match[:stacktrace].split('First happened in')[0].gsub(/^\s*#.*$/, '').gsub(/^[[:space:]]+/, '').strip
|
141
222
|
else
|
142
223
|
puts " => [DEBUG] Stacktrace doesn't match the expected regex (#{regex}):\n----------------\n#{stacktrace}\n----------------\n"
|
143
224
|
end
|
@@ -172,10 +253,27 @@ module Gitlab
|
|
172
253
|
"```\n#{full_stacktrace(test)}\n```",
|
173
254
|
"First happened in #{test.ci_job_url}.",
|
174
255
|
"Related test case: #{test.testcase}.",
|
175
|
-
screenshot_section(test)
|
256
|
+
screenshot_section(test),
|
257
|
+
system_log_errors_section(test)
|
176
258
|
].join("\n\n")
|
177
259
|
end
|
178
260
|
|
261
|
+
def system_log_errors_section(test)
|
262
|
+
correlation_id = test.failures.first['correlation_id']
|
263
|
+
section = ''
|
264
|
+
|
265
|
+
if @system_logs.any? && !correlation_id.nil?
|
266
|
+
section = SystemLogs::SystemLogsFormatter.new(
|
267
|
+
@system_logs,
|
268
|
+
correlation_id
|
269
|
+
).system_logs_summary_markdown
|
270
|
+
end
|
271
|
+
|
272
|
+
puts " => No system logs or correlation id provided, skipping this section in issue description" if section.empty?
|
273
|
+
|
274
|
+
section
|
275
|
+
end
|
276
|
+
|
179
277
|
def new_issue_labels(test)
|
180
278
|
up_to_date_labels(test: test, new_labels: NEW_ISSUE_LABELS)
|
181
279
|
end
|
@@ -202,11 +300,9 @@ module Gitlab
|
|
202
300
|
end
|
203
301
|
|
204
302
|
def existing_failure_note(issue)
|
205
|
-
gitlab.find_issue_notes(iid: issue.iid)
|
206
|
-
|
303
|
+
gitlab.find_issue_notes(iid: issue.iid)&.find do |note|
|
304
|
+
note.body.include?('Failed most recently in')
|
207
305
|
end
|
208
|
-
|
209
|
-
false
|
210
306
|
end
|
211
307
|
|
212
308
|
def screenshot_section(test)
|
@@ -128,7 +128,8 @@ module Gitlab
|
|
128
128
|
{
|
129
129
|
'message' => "#{exception['class']}: #{exception['message']}",
|
130
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")}"
|
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']
|
132
133
|
}
|
133
134
|
end
|
134
135
|
end
|
data/lib/gitlab/qa/reporter.rb
CHANGED
@@ -7,6 +7,7 @@ module Gitlab
|
|
7
7
|
class Reporter
|
8
8
|
# rubocop:disable Metrics/AbcSize
|
9
9
|
# rubocop:disable Metrics/PerceivedComplexity
|
10
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
10
11
|
def self.invoke(args)
|
11
12
|
report_options = {}
|
12
13
|
slack_options = {}
|
@@ -65,6 +66,12 @@ module Gitlab
|
|
65
66
|
slack_options[:message] = slack_options[:message] + "\n\n" + Gitlab::QA::Report::SummaryTable.create(input_files: files)
|
66
67
|
end
|
67
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
|
+
|
68
75
|
opts.on('--update-screenshot-path FILES', "Update the path to screenshots to container's host") do |files|
|
69
76
|
report_options[:update_screenshot_path] = true
|
70
77
|
report_options[:files] = files
|
@@ -116,6 +123,7 @@ module Gitlab
|
|
116
123
|
exit 1
|
117
124
|
end
|
118
125
|
end
|
126
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
119
127
|
# rubocop:enable Metrics/PerceivedComplexity
|
120
128
|
# rubocop:enable Metrics/AbcSize
|
121
129
|
end
|
@@ -73,8 +73,8 @@ module Gitlab
|
|
73
73
|
'GITLAB_QA_PASSWORD_1' => :gitlab_qa_password_1,
|
74
74
|
'GITLAB_QA_USERNAME_2' => :gitlab_qa_username_2,
|
75
75
|
'GITLAB_QA_PASSWORD_2' => :gitlab_qa_password_2,
|
76
|
-
'
|
77
|
-
'
|
76
|
+
'QA_GITHUB_USERNAME' => :qa_github_username,
|
77
|
+
'QA_GITHUB_PASSWORD' => :qa_github_password,
|
78
78
|
'KNAPSACK_GENERATE_REPORT' => :knapsack_generate_report,
|
79
79
|
'KNAPSACK_REPORT_PATH' => :knapsack_report_path,
|
80
80
|
'KNAPSACK_TEST_FILE_PATTERN' => :knapsack_test_file_pattern,
|
@@ -138,7 +138,13 @@ module Gitlab
|
|
138
138
|
'CHROME_DISABLE_DEV_SHM' => :chrome_disable_dev_shm,
|
139
139
|
'COLORIZED_LOGS' => :colorized_logs,
|
140
140
|
'FIPS' => :fips,
|
141
|
-
'JH_ENV' => :jh_env
|
141
|
+
'JH_ENV' => :jh_env,
|
142
|
+
'QA_GITHUB_OAUTH_APP_ID' => :github_oauth_app_id,
|
143
|
+
'QA_GITHUB_OAUTH_APP_SECRET' => :github_oauth_app_secret,
|
144
|
+
'QA_1P_EMAIL' => :qa_1p_email,
|
145
|
+
'QA_1P_PASSWORD' => :qa_1p_password,
|
146
|
+
'QA_1P_SECRET' => :qa_1p_secret,
|
147
|
+
'QA_1P_GITHUB_UUID' => :qa_1p_github_uuid
|
142
148
|
}.freeze
|
143
149
|
|
144
150
|
ENV_VARIABLES.each do |env_name, method_name|
|
@@ -304,6 +310,13 @@ module Gitlab
|
|
304
310
|
end
|
305
311
|
end
|
306
312
|
|
313
|
+
def require_oauth_environment!
|
314
|
+
%w[QA_GITHUB_OAUTH_APP_ID QA_GITHUB_OAUTH_APP_SECRET QA_GITHUB_USERNAME
|
315
|
+
QA_GITHUB_PASSWORD QA_1P_EMAIL QA_1P_PASSWORD QA_1P_SECRET QA_1P_GITHUB_UUID].each do |env_key|
|
316
|
+
raise ArgumentError, "Environment variable #{env_key} must be set to run OAuth specs" unless ENV.key?(env_key)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
307
320
|
def require_initial_password!
|
308
321
|
return unless env_var_value_if_defined('GITLAB_INITIAL_ROOT_PASSWORD').to_s.strip.empty?
|
309
322
|
|
@@ -406,7 +419,7 @@ module Gitlab
|
|
406
419
|
end
|
407
420
|
|
408
421
|
def mock_github_enabled?
|
409
|
-
enabled?(env_var_value_if_defined('QA_MOCK_GITHUB'), default:
|
422
|
+
enabled?(env_var_value_if_defined('QA_MOCK_GITHUB'), default: true)
|
410
423
|
end
|
411
424
|
|
412
425
|
private
|
@@ -32,7 +32,7 @@ module Gitlab
|
|
32
32
|
"gitlab-psql -d gitlabhq_production_ci -c 'create extension if not exists btree_gist'",
|
33
33
|
"gitlab-psql -d gitlabhq_production_ci -c 'create extension if not exists pg_trgm'",
|
34
34
|
# Load schema only if it does not exist.
|
35
|
-
"#{SCHEMA_EXISTENCE_CHECK_COMMAND} || gitlab-rake db:schema:load:ci",
|
35
|
+
"#{SCHEMA_EXISTENCE_CHECK_COMMAND} || DISABLE_DATABASE_ENVIRONMENT_CHECK=1 gitlab-rake db:schema:load:ci",
|
36
36
|
"gitlab-ctl restart"
|
37
37
|
].freeze
|
38
38
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Gitlab
|
6
|
+
module QA
|
7
|
+
module Scenario
|
8
|
+
module Test
|
9
|
+
module Integration
|
10
|
+
class OAuth < Scenario::Template
|
11
|
+
attr_reader :gitlab_name
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@gitlab_name = 'gitlab-oauth'
|
15
|
+
end
|
16
|
+
|
17
|
+
def perform(release, *rspec_args)
|
18
|
+
Runtime::Env.require_oauth_environment!
|
19
|
+
|
20
|
+
release = Release.new(release)
|
21
|
+
|
22
|
+
Component::Gitlab.perform do |gitlab|
|
23
|
+
gitlab.release = release
|
24
|
+
gitlab.network = 'test'
|
25
|
+
gitlab.name = gitlab_name
|
26
|
+
|
27
|
+
gitlab.omnibus_configuration << gitlab_omnibus_configuration
|
28
|
+
|
29
|
+
gitlab.instance do
|
30
|
+
Runtime::Logger.info('Running OAuth specs!')
|
31
|
+
|
32
|
+
Component::Specs.perform do |specs|
|
33
|
+
rspec_args << '--' unless rspec_args.include?('--')
|
34
|
+
rspec_args << %w[--tag oauth]
|
35
|
+
specs.suite = 'Test::Instance::All'
|
36
|
+
specs.release = release
|
37
|
+
specs.network = gitlab.network
|
38
|
+
specs.args = [gitlab.address, *rspec_args]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def gitlab_omnibus_configuration
|
47
|
+
<<~OMNIBUS
|
48
|
+
gitlab_rails['omniauth_enabled'] = true;
|
49
|
+
gitlab_rails['omniauth_allow_single_sign_on'] = ['github'];
|
50
|
+
gitlab_rails['omniauth_block_auto_created_users'] = false;
|
51
|
+
gitlab_rails['omniauth_providers'] = [
|
52
|
+
{
|
53
|
+
name: 'github',
|
54
|
+
app_id: '$QA_GITHUB_OAUTH_APP_ID',
|
55
|
+
app_secret: '$QA_GITHUB_OAUTH_APP_SECRET',
|
56
|
+
url: 'https://github.com/',
|
57
|
+
verify_ssl: false,
|
58
|
+
args: { scope: 'user:email' }
|
59
|
+
}
|
60
|
+
];
|
61
|
+
OMNIBUS
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -18,14 +18,15 @@ module Gitlab
|
|
18
18
|
# @param [String] release current release docker image
|
19
19
|
# @param [String] current_version current gitlab version associated with docker image
|
20
20
|
# @param [String] semver_component semver component for N - 1 version detection, major|minor|patch
|
21
|
+
# @param [String] from_edition gitlab edition to update from
|
21
22
|
# @param [Array] *rspec_args rspec arguments
|
22
23
|
# @return [void]
|
23
|
-
def perform(release, current_version, semver_component, *rspec_args)
|
24
|
+
def perform(release, current_version, semver_component, from_edition = nil, *rspec_args)
|
24
25
|
@current_release = QA::Release.new(release)
|
25
26
|
@upgrade_path = Support::GitlabUpgradePath.new(
|
26
27
|
current_version,
|
27
28
|
semver_component,
|
28
|
-
@current_release.edition
|
29
|
+
from_edition || @current_release.edition
|
29
30
|
).fetch
|
30
31
|
|
31
32
|
upgrade_info = "#{[*upgrade_path, current_release].join(' => ')} (#{current_version})".bright
|
@@ -0,0 +1,65 @@
|
|
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
|
@@ -0,0 +1,21 @@
|
|
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
|
@@ -0,0 +1,21 @@
|
|
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
|
@@ -0,0 +1,21 @@
|
|
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
|
@@ -0,0 +1,21 @@
|
|
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
|
@@ -0,0 +1,38 @@
|
|
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
|
@@ -0,0 +1,34 @@
|
|
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
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module QA
|
5
|
+
module SystemLogs
|
6
|
+
module LogTypes
|
7
|
+
module Rails
|
8
|
+
class ApplicationLog < Log
|
9
|
+
include SharedFields::Exception
|
10
|
+
include SharedFields::Meta
|
11
|
+
|
12
|
+
def initialize(data)
|
13
|
+
super('Rails Application', data)
|
14
|
+
end
|
15
|
+
|
16
|
+
def summary_fields
|
17
|
+
super.concat(
|
18
|
+
exception_fields,
|
19
|
+
meta_fields
|
20
|
+
)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|