gitlab-qa 8.4.2 → 8.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +0 -12
- data/Gemfile.lock +1 -1
- data/exe/gitlab-qa +1 -1
- data/lib/gitlab/qa/component/base.rb +2 -2
- data/lib/gitlab/qa/component/gitlab.rb +1 -1
- data/lib/gitlab/qa/component/staging.rb +17 -35
- data/lib/gitlab/qa/component/suggested_reviewer.rb +47 -0
- data/lib/gitlab/qa/docker/command.rb +3 -14
- data/lib/gitlab/qa/docker/engine.rb +1 -1
- data/lib/gitlab/qa/release.rb +6 -2
- data/lib/gitlab/qa/report/find_set_dri.rb +43 -0
- data/lib/gitlab/qa/report/gitlab_issue_client.rb +7 -0
- data/lib/gitlab/qa/report/relate_failure_issue.rb +16 -0
- data/lib/gitlab/qa/report/results_reporter_shared.rb +1 -1
- data/lib/gitlab/qa/report/test_result.rb +8 -0
- data/lib/gitlab/qa/runtime/env.rb +5 -14
- data/lib/gitlab/qa/scenario/test/integration/suggested_reviewer.rb +62 -0
- data/lib/gitlab/qa/scenario/test/omnibus/update_from_previous.rb +1 -1
- data/lib/gitlab/qa/service/cluster_provider/base.rb +33 -0
- data/lib/gitlab/qa/service/cluster_provider/k3d.rb +141 -0
- data/lib/gitlab/qa/service/kubernetes_cluster.rb +62 -0
- data/lib/gitlab/qa/support/shell_command.rb +98 -0
- data/lib/gitlab/qa/support/shellout.rb +16 -0
- data/lib/gitlab/qa/version.rb +1 -1
- data/lib/gitlab/qa.rb +0 -1
- data/support/manifests/suggested_reviewer/authenticator.yaml +41 -0
- data/support/manifests/suggested_reviewer/postgres.yaml +84 -0
- data/support/manifests/suggested_reviewer/pubsub.yaml +41 -0
- data/support/manifests/suggested_reviewer/recommender-bot.yaml +242 -0
- data/support/manifests/suggested_reviewer/recommender.yaml +52 -0
- metadata +15 -7
- data/lib/gitlab/qa/component/internet_tunnel.rb +0 -76
- data/lib/gitlab/qa/docker/shellout.rb +0 -77
- data/lib/gitlab/qa/scenario/test/integration/kubernetes.rb +0 -56
- data/lib/gitlab/qa/scenario/test/integration/ssh_tunnel.rb +0 -56
- data/lib/gitlab/qa/support/dev_ee_qa_image.rb +0 -54
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module QA
|
5
|
+
module Service
|
6
|
+
module ClusterProvider
|
7
|
+
class K3d < Base
|
8
|
+
def create_registry_mirror
|
9
|
+
return if registry_mirror_exists?('registry-gitlab')
|
10
|
+
|
11
|
+
Runtime::Env.require_qa_container_registry_access_token!
|
12
|
+
|
13
|
+
shell <<~CMD
|
14
|
+
k3d registry create registry-gitlab \
|
15
|
+
-p 5000 \
|
16
|
+
--proxy-remote-url https://#{GITLAB_REGISTRY} \
|
17
|
+
--proxy-password #{Runtime::Env.gitlab_username} \
|
18
|
+
--proxy-username #{Runtime::Env.qa_container_registry_access_token} \
|
19
|
+
-v tmp/registry-gitlab:/var/lib/registry
|
20
|
+
CMD
|
21
|
+
|
22
|
+
File.write('tmp/registry-mirror.yml', registry_mirror)
|
23
|
+
|
24
|
+
create_args << %w[--registry-use k3d-registry-gitlab:5000 --registry-config tmp/registry-mirror.yml]
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_dependencies
|
28
|
+
find_executable('k3d') || raise("You must first install `k3d` executable to run these tests.")
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup
|
32
|
+
shell "k3d cluster create #{cluster_name} #{create_args&.join(' ')}"
|
33
|
+
|
34
|
+
install_local_storage
|
35
|
+
end
|
36
|
+
|
37
|
+
def teardown
|
38
|
+
shell "k3d cluster delete #{cluster_name}"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def registry_mirror_exists?(name)
|
44
|
+
shell('k3d registry list').include?("k3d-#{name}")
|
45
|
+
end
|
46
|
+
|
47
|
+
def retry_until(max_attempts: 10, wait: 1)
|
48
|
+
max_attempts.times do
|
49
|
+
result = yield
|
50
|
+
return result if result
|
51
|
+
|
52
|
+
sleep wait
|
53
|
+
end
|
54
|
+
|
55
|
+
raise "Retried #{max_attempts} times. Aborting"
|
56
|
+
end
|
57
|
+
|
58
|
+
def install_local_storage
|
59
|
+
shell('kubectl apply -f -', stdin_data: local_storage_config)
|
60
|
+
end
|
61
|
+
|
62
|
+
# See https://github.com/rancher/k3d/issues/67
|
63
|
+
def local_storage_config
|
64
|
+
<<~YAML
|
65
|
+
---
|
66
|
+
apiVersion: v1
|
67
|
+
kind: ServiceAccount
|
68
|
+
metadata:
|
69
|
+
name: storage-provisioner
|
70
|
+
namespace: kube-system
|
71
|
+
---
|
72
|
+
apiVersion: rbac.authorization.k8s.io/v1
|
73
|
+
kind: ClusterRoleBinding
|
74
|
+
metadata:
|
75
|
+
name: storage-provisioner
|
76
|
+
roleRef:
|
77
|
+
apiGroup: rbac.authorization.k8s.io
|
78
|
+
kind: ClusterRole
|
79
|
+
name: system:persistent-volume-provisioner
|
80
|
+
subjects:
|
81
|
+
- kind: ServiceAccount
|
82
|
+
name: storage-provisioner
|
83
|
+
namespace: kube-system
|
84
|
+
---
|
85
|
+
apiVersion: v1
|
86
|
+
kind: Pod
|
87
|
+
metadata:
|
88
|
+
name: storage-provisioner
|
89
|
+
namespace: kube-system
|
90
|
+
spec:
|
91
|
+
serviceAccountName: storage-provisioner
|
92
|
+
tolerations:
|
93
|
+
- effect: NoExecute
|
94
|
+
key: node.kubernetes.io/not-ready
|
95
|
+
operator: Exists
|
96
|
+
tolerationSeconds: 300
|
97
|
+
- effect: NoExecute
|
98
|
+
key: node.kubernetes.io/unreachable
|
99
|
+
operator: Exists
|
100
|
+
tolerationSeconds: 300
|
101
|
+
hostNetwork: true
|
102
|
+
containers:
|
103
|
+
- name: storage-provisioner
|
104
|
+
image: gcr.io/k8s-minikube/storage-provisioner:v1.8.1
|
105
|
+
command: ["/storage-provisioner"]
|
106
|
+
imagePullPolicy: IfNotPresent
|
107
|
+
volumeMounts:
|
108
|
+
- mountPath: /tmp
|
109
|
+
name: tmp
|
110
|
+
volumes:
|
111
|
+
- name: tmp
|
112
|
+
hostPath:
|
113
|
+
path: /tmp
|
114
|
+
type: Directory
|
115
|
+
---
|
116
|
+
kind: StorageClass
|
117
|
+
apiVersion: storage.k8s.io/v1
|
118
|
+
metadata:
|
119
|
+
name: standard
|
120
|
+
namespace: kube-system
|
121
|
+
annotations:
|
122
|
+
storageclass.kubernetes.io/is-default-class: "true"
|
123
|
+
labels:
|
124
|
+
addonmanager.kubernetes.io/mode: EnsureExists
|
125
|
+
provisioner: k8s.io/minikube-hostpath
|
126
|
+
YAML
|
127
|
+
end
|
128
|
+
|
129
|
+
def registry_mirror
|
130
|
+
<<~YAML
|
131
|
+
mirrors:
|
132
|
+
"registry.gitlab.com":
|
133
|
+
endpoint:
|
134
|
+
- http://k3d-registry-gitlab:5000
|
135
|
+
YAML
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mkmf'
|
4
|
+
|
5
|
+
module Gitlab
|
6
|
+
module QA
|
7
|
+
module Service
|
8
|
+
class KubernetesCluster
|
9
|
+
include Support::Shellout
|
10
|
+
|
11
|
+
attr_reader :provider
|
12
|
+
|
13
|
+
def initialize(provider_class: QA::Service::ClusterProvider::K3d)
|
14
|
+
@provider = provider_class.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def create!
|
18
|
+
validate_dependencies
|
19
|
+
|
20
|
+
@provider.validate_dependencies
|
21
|
+
@provider.setup
|
22
|
+
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove!
|
27
|
+
@provider.teardown
|
28
|
+
end
|
29
|
+
|
30
|
+
def cluster_name
|
31
|
+
@provider.cluster_name
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
cluster_name
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_registry_mirror
|
39
|
+
@provider.create_registry_mirror
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_secret(secret, secret_name)
|
43
|
+
shell("kubectl create secret generic #{secret_name} --from-literal=token='#{secret}'", mask_secrets: [secret])
|
44
|
+
end
|
45
|
+
|
46
|
+
def apply_manifest(manifest)
|
47
|
+
shell('kubectl apply -f -', stdin_data: manifest)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def admin_user
|
53
|
+
@admin_user ||= "#{@provider.cluster_name}-admin"
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate_dependencies
|
57
|
+
find_executable('kubectl') || raise("You must first install `kubectl` executable to run these tests.")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
require 'active_support/core_ext/string/filters'
|
5
|
+
|
6
|
+
module Gitlab
|
7
|
+
module QA
|
8
|
+
module Support
|
9
|
+
class ShellCommand
|
10
|
+
using Rainbow
|
11
|
+
|
12
|
+
StatusError = Class.new(StandardError)
|
13
|
+
|
14
|
+
# Shell command
|
15
|
+
#
|
16
|
+
# @param [<String, Array>] command
|
17
|
+
# @param [<String, Array>] mask_secrets
|
18
|
+
# @param [Boolean] stream_output stream command output to stdout directly instead of logger
|
19
|
+
def initialize(command = nil, stdin_data: nil, mask_secrets: nil, stream_output: false)
|
20
|
+
@command = command
|
21
|
+
@mask_secrets = Array(mask_secrets)
|
22
|
+
@stream_output = stream_output
|
23
|
+
@output = []
|
24
|
+
@logger = Runtime::Logger.logger
|
25
|
+
@stdin_data = stdin_data
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :command, :output, :stream_output
|
29
|
+
|
30
|
+
def execute! # rubocop:disable Metrics/AbcSize
|
31
|
+
raise StatusError, 'Command already executed' if output.any?
|
32
|
+
|
33
|
+
logger.info("Shell command: `#{mask_secrets(command).cyan}`")
|
34
|
+
|
35
|
+
Open3.popen2e(@command.to_s) do |stdin, out, wait|
|
36
|
+
if @stdin_data
|
37
|
+
stdin.puts(@stdin_data)
|
38
|
+
stdin.close
|
39
|
+
end
|
40
|
+
|
41
|
+
out.each do |line|
|
42
|
+
output.push(line)
|
43
|
+
|
44
|
+
if stream_progress
|
45
|
+
print "."
|
46
|
+
elsif stream_output
|
47
|
+
puts line
|
48
|
+
end
|
49
|
+
|
50
|
+
yield line, wait if block_given?
|
51
|
+
end
|
52
|
+
puts if stream_progress && !output.empty?
|
53
|
+
|
54
|
+
fail! if wait.value.exited? && wait.value.exitstatus.nonzero?
|
55
|
+
|
56
|
+
logger.debug("Shell command output:\n#{string_output}") unless stream_output || output.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
string_output
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
attr_reader :logger
|
65
|
+
|
66
|
+
# Raise error and print output to error log level
|
67
|
+
#
|
68
|
+
# @return [void]
|
69
|
+
def fail!
|
70
|
+
logger.error("Shell command output:\n#{string_output}") unless stream_output
|
71
|
+
raise StatusError, "Command `#{mask_secrets(command).truncate(100)}` failed! " + "✘".red
|
72
|
+
end
|
73
|
+
|
74
|
+
# Stream only command execution progress and log output when command finished
|
75
|
+
#
|
76
|
+
# @return [Boolean]
|
77
|
+
def stream_progress
|
78
|
+
!(Runtime::Env.ci || stream_output)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Stringified command output
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
def string_output
|
85
|
+
mask_secrets(output.join.chomp)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Returns a masked string
|
89
|
+
#
|
90
|
+
# @param [String] input the string to mask
|
91
|
+
# @return [String] The masked string
|
92
|
+
def mask_secrets(input)
|
93
|
+
@mask_secrets.each_with_object(+input) { |secret, s| s.gsub!(secret, '*****') }.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Gitlab
|
4
|
+
module QA
|
5
|
+
module Support
|
6
|
+
module Shellout
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def shell(command = nil, stdin_data: nil, mask_secrets: nil, stream_output: false, &block)
|
10
|
+
Support::ShellCommand.new(
|
11
|
+
command, stdin_data: stdin_data, mask_secrets: mask_secrets, stream_output: stream_output).execute!(&block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/gitlab/qa/version.rb
CHANGED
data/lib/gitlab/qa.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
---
|
2
|
+
apiVersion: apps/v1
|
3
|
+
kind: Deployment
|
4
|
+
metadata:
|
5
|
+
name: authenticator-deployment
|
6
|
+
labels:
|
7
|
+
app: authenticator
|
8
|
+
spec:
|
9
|
+
replicas: 1
|
10
|
+
selector:
|
11
|
+
matchLabels:
|
12
|
+
app: authenticator
|
13
|
+
template:
|
14
|
+
metadata:
|
15
|
+
labels:
|
16
|
+
app: authenticator
|
17
|
+
spec:
|
18
|
+
imagePullSecrets:
|
19
|
+
- name: gitlab-registry
|
20
|
+
containers:
|
21
|
+
- name: authenticator
|
22
|
+
image: registry.gitlab.com/gitlab-org/modelops/applied-ml/review-recommender/authenticator:0.1.0
|
23
|
+
imagePullPolicy: Always
|
24
|
+
ports:
|
25
|
+
- containerPort: 8080
|
26
|
+
args: []
|
27
|
+
env:
|
28
|
+
- name: AUTHENTICATOR_SERVICE_PORT
|
29
|
+
value: "8080"
|
30
|
+
---
|
31
|
+
apiVersion: v1
|
32
|
+
kind: Service
|
33
|
+
metadata:
|
34
|
+
name: authenticator-service
|
35
|
+
spec:
|
36
|
+
type: NodePort
|
37
|
+
ports:
|
38
|
+
- port: 8080
|
39
|
+
targetPort: 8080
|
40
|
+
selector:
|
41
|
+
app: authenticator
|
@@ -0,0 +1,84 @@
|
|
1
|
+
---
|
2
|
+
apiVersion: v1
|
3
|
+
kind: Secret
|
4
|
+
metadata:
|
5
|
+
name: postgres-secret
|
6
|
+
type: kubernetes.io/basic-auth
|
7
|
+
stringData:
|
8
|
+
username: reviewer-recommender
|
9
|
+
password: ml4dawin
|
10
|
+
|
11
|
+
---
|
12
|
+
apiVersion: v1
|
13
|
+
kind: PersistentVolumeClaim
|
14
|
+
metadata:
|
15
|
+
name: postgres-pvc
|
16
|
+
spec:
|
17
|
+
storageClassName: local-path
|
18
|
+
accessModes:
|
19
|
+
- ReadWriteOnce
|
20
|
+
resources:
|
21
|
+
requests:
|
22
|
+
storage: 1Gi
|
23
|
+
|
24
|
+
---
|
25
|
+
apiVersion: apps/v1
|
26
|
+
kind: Deployment
|
27
|
+
metadata:
|
28
|
+
name: postgres-deployment
|
29
|
+
spec:
|
30
|
+
replicas: 1
|
31
|
+
selector:
|
32
|
+
matchLabels:
|
33
|
+
app: postgres
|
34
|
+
template:
|
35
|
+
metadata:
|
36
|
+
labels:
|
37
|
+
app: postgres
|
38
|
+
spec:
|
39
|
+
containers:
|
40
|
+
- name: postgres
|
41
|
+
image: postgres:13-alpine
|
42
|
+
imagePullPolicy: IfNotPresent
|
43
|
+
ports:
|
44
|
+
- containerPort: 5432
|
45
|
+
volumeMounts:
|
46
|
+
- name: postgres-pv
|
47
|
+
mountPath: /var/lib/postgresql/data
|
48
|
+
subPath: postgres
|
49
|
+
resources:
|
50
|
+
limits:
|
51
|
+
memory: 256Mi
|
52
|
+
cpu: 250m
|
53
|
+
env:
|
54
|
+
- name: POSTGRES_DB
|
55
|
+
value: reviewer-recommender
|
56
|
+
- name: POSTGRES_USER
|
57
|
+
valueFrom:
|
58
|
+
secretKeyRef:
|
59
|
+
name: postgres-secret
|
60
|
+
key: username
|
61
|
+
- name: POSTGRES_PASSWORD
|
62
|
+
valueFrom:
|
63
|
+
secretKeyRef:
|
64
|
+
name: postgres-secret
|
65
|
+
key: password
|
66
|
+
volumes:
|
67
|
+
- name: postgres-pv
|
68
|
+
persistentVolumeClaim:
|
69
|
+
claimName: postgres-pvc
|
70
|
+
|
71
|
+
---
|
72
|
+
apiVersion: v1
|
73
|
+
kind: Service
|
74
|
+
metadata:
|
75
|
+
name: postgres
|
76
|
+
labels:
|
77
|
+
app: postgres
|
78
|
+
spec:
|
79
|
+
type: ClusterIP
|
80
|
+
selector:
|
81
|
+
app: postgres
|
82
|
+
ports:
|
83
|
+
- port: 5432
|
84
|
+
targetPort: 5432
|
@@ -0,0 +1,41 @@
|
|
1
|
+
---
|
2
|
+
apiVersion: apps/v1
|
3
|
+
kind: Deployment
|
4
|
+
metadata:
|
5
|
+
name: pubsub-deployment
|
6
|
+
labels:
|
7
|
+
app: pubsub
|
8
|
+
spec:
|
9
|
+
replicas: 1
|
10
|
+
selector:
|
11
|
+
matchLabels:
|
12
|
+
app: pubsub
|
13
|
+
template:
|
14
|
+
metadata:
|
15
|
+
labels:
|
16
|
+
app: pubsub
|
17
|
+
spec:
|
18
|
+
containers:
|
19
|
+
- name: pubsub
|
20
|
+
image: singularities/pubsub-emulator:latest
|
21
|
+
imagePullPolicy: IfNotPresent
|
22
|
+
env:
|
23
|
+
- name: PUBSUB_PROJECT_ID
|
24
|
+
value: project-test
|
25
|
+
- name: PUBSUB_LISTEN_ADDRESS
|
26
|
+
value: 0.0.0.0:8432
|
27
|
+
|
28
|
+
---
|
29
|
+
apiVersion: v1
|
30
|
+
kind: Service
|
31
|
+
metadata:
|
32
|
+
name: pubsub
|
33
|
+
labels:
|
34
|
+
app: pubsub
|
35
|
+
spec:
|
36
|
+
type: ClusterIP
|
37
|
+
selector:
|
38
|
+
app: pubsub
|
39
|
+
ports:
|
40
|
+
- port: 8432
|
41
|
+
targetPort: 8432
|