gitlab-qa 9.1.2 → 10.1.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/.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
|