gitlab-qa 8.4.2 → 8.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae27f1af0edb9ce83b3ba33e8f18ddb155b62a786b7977d93ab74dfe2ba33e77
4
- data.tar.gz: 45f26af4e1796502625888791990c16403654f1abe9558deca1d7fc351135383
3
+ metadata.gz: dca93a1000f4f0b6db2759a97ae3a6a7edf18e06576d689edd42180d794da943
4
+ data.tar.gz: bcd56912a666905a27513c07c24ef1ae72c24b463d68aba9b2dcff84e0011743
5
5
  SHA512:
6
- metadata.gz: 4b7e606c2e6966d29ddc414e512ad7c2ce19a67dcf9ef20fcdaec5575219aa82631cb77c486e35c74f1a4c956004b6f7519f6422838a09d8e43873a5e9c4bbb4
7
- data.tar.gz: 7ebbdd74ef735ecbdcb107bafa1418754f3cc89b18d72da2986b0d0d455145e25e4e4c812ab2d2dcbc72e4d50b2337d5f60d66100d715bbd6d3e4750f9eb9c22
6
+ metadata.gz: 0563f087686a1bf24c5fa1581993897d0cf553465636748d51929bcdad1b35d2a26362c7f40849cd5979ecb9eb4b476f3efde52f1c59142892fddbb497b254fb
7
+ data.tar.gz: 50f84dd5ae8abca78d6b2488e9213c1f1195e0c58279697eac7a2d943c7dd147f857a328840f9369d16829b2fc581c882a1bcee28093ee1b68400d924897f5f3
data/.rubocop_todo.yml CHANGED
@@ -57,12 +57,6 @@ Naming/RescuedExceptionsVariableName:
57
57
  Exclude:
58
58
  - 'lib/gitlab/qa/component/staging.rb'
59
59
 
60
- # Offense count: 3
61
- # Cop supports --auto-correct.
62
- RSpec/EmptyLineAfterLetBlock:
63
- Exclude:
64
- - 'spec/gitlab/qa/support/dev_ee_qa_image_spec.rb'
65
-
66
60
  # Offense count: 4
67
61
  # Cop supports --auto-correct.
68
62
  # Configuration parameters: CustomTransform, IgnoredWords.
@@ -70,12 +64,6 @@ RSpec/ExampleWording:
70
64
  Exclude:
71
65
  - 'spec/gitlab/qa/component/gitlab_spec.rb'
72
66
 
73
- # Offense count: 1
74
- # Configuration parameters: CustomTransform, IgnoreMethods, SpecSuffixOnly.
75
- RSpec/FilePath:
76
- Exclude:
77
- - 'spec/gitlab/qa/support/dev_ee_qa_image_spec.rb'
78
-
79
67
  # Offense count: 2
80
68
  RSpec/LeakyConstantDeclaration:
81
69
  Exclude:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gitlab-qa (8.4.2)
4
+ gitlab-qa (8.5.0)
5
5
  activesupport (~> 6.1)
6
6
  gitlab (~> 4.18.0)
7
7
  http (~> 5.0)
data/exe/gitlab-qa CHANGED
@@ -6,4 +6,4 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
6
 
7
7
  require 'gitlab/qa'
8
8
 
9
- Gitlab::QA::Runner.run(ARGV)
9
+ Gitlab::QA::Runner.run(ARGV.dup)
@@ -157,11 +157,11 @@ module Gitlab
157
157
  reconfigure
158
158
  wait_until_ready
159
159
  process_exec_commands
160
- rescue Docker::Shellout::StatusError => e
160
+ rescue Support::ShellCommand::StatusError => e
161
161
  # for scenarios where a service fails during startup, attempt to retry to avoid flaky failures
162
162
  if (retries += 1) < 3
163
163
  Runtime::Logger.warn(
164
- "Retry instance_no_teardown due to Docker::Shellout::StatusError -- attempt #{retries}"
164
+ "Retry instance_no_teardown due to Support::ShellCommand::StatusError -- attempt #{retries}"
165
165
  )
166
166
  teardown!
167
167
  retry
@@ -338,7 +338,7 @@ module Gitlab
338
338
  output = docker.inspect(name) { |command| command << "--format='{{json .State.Health.Status}}'" }
339
339
 
340
340
  output == '"healthy"'
341
- rescue Docker::Shellout::StatusError
341
+ rescue Support::ShellCommand::StatusError
342
342
  false
343
343
  end
344
344
  end
@@ -20,31 +20,21 @@ module Gitlab
20
20
  end
21
21
 
22
22
  def self.image
23
+ # QA images are tagged with the following logic
24
+ # 1. For auto-deploy versions, they are tagged with their corresponding commit SHA
25
+ # That is, if auto-deploy version is `15.4.202209150620+70251a89db4.a625f183e2e`,
26
+ # the QA image tag will be `70251a89db4`
27
+ # 2. For stable/RC versions, they are tagged with the version with `v` prefix.
28
+ # That is, if the version is `15.3.3-ee`, the QA image tag will be `v15.3.3-ee`
29
+ # These images are available from the GitLab project's container registry.
30
+
31
+ # If token to access dev.gitlab.org registry is provided, we will
32
+ # fetch from there. Else, we will try to fetch from GitLab.com
33
+ # registry.
23
34
  if Runtime::Env.dev_access_token_variable
24
- # Auto-deploy builds have a tag formatted like 12.1.12345+5159f2949cb.59c9fa631
25
- # where `5159f2949cb` is the EE commit SHA. QA images are tagged using
26
- # the version from the VERSION file and this commit SHA, e.g.
27
- # `12.0-5159f2949cb` (note that the `major.minor` doesn't necessarily match).
28
- # To work around that, we're fetching the `revision` from the version API
29
- # and then find the corresponding QA image in the
30
- # `dev.gitlab.org:5005/gitlab/omnibus-gitlab/gitlab-ee-qa` container
31
- # registry, based on this revision.
32
- # See:
33
- # - https://gitlab.com/gitlab-org/quality/staging/issues/56
34
- # - https://gitlab.com/gitlab-org/release/framework/issues/421
35
- # - https://gitlab.com/gitlab-org/gitlab-qa/issues/398
36
- #
37
- # For official builds (currently deployed on preprod) we use the version
38
- # string e.g. '12.5.4-ee' for tag matching
39
- Support::DevEEQAImage.new.retrieve_image_from_container_registry!(tag_end)
35
+ "dev.gitlab.org:5005/gitlab/gitlab-ee/gitlab-ee-qa:#{tag}"
40
36
  else
41
- # Auto-deploy builds have a tag formatted like 12.0.12345+5159f2949cb.59c9fa631
42
- # but the version api returns a semver version like 12.0.1
43
- # so images are tagged using minor and major semver components plus
44
- # the EE commit ref, which is the 'revision' returned by the API
45
- # and so the version used for the docker image tag is like 12.0-5159f2949cb
46
- # See: https://gitlab.com/gitlab-org/quality/staging/issues/56
47
- "ee:#{Version.new(address).major_minor_revision}"
37
+ "registry.gitlab.com/gitlab-org/gitlab/gitlab-ee-qa:#{tag}"
48
38
  end
49
39
  end
50
40
 
@@ -52,8 +42,8 @@ module Gitlab
52
42
  self::ADDRESS
53
43
  end
54
44
 
55
- def self.tag_end
56
- @tag_end ||= Version.new(address).tag_end
45
+ def self.tag
46
+ @tag ||= Version.new(address).tag
57
47
  end
58
48
 
59
49
  class Version
@@ -65,16 +55,8 @@ module Gitlab
65
55
  Runtime::Env.require_qa_access_token!
66
56
  end
67
57
 
68
- def tag_end
69
- official? ? version : revision
70
- end
71
-
72
- def major_minor_revision
73
- api_response = api_get!
74
- version_regexp = /^v?(?<major>\d+)\.(?<minor>\d+)\.\d+/
75
- match = version_regexp.match(api_response.fetch('version'))
76
-
77
- "#{match[:major]}.#{match[:minor]}-#{api_response.fetch('revision')}"
58
+ def tag
59
+ official? ? "v#{version}" : revision
78
60
  end
79
61
 
80
62
  private
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module QA
5
+ module Component
6
+ class SuggestedReviewer
7
+ include Scenario::Actable
8
+
9
+ # Source: https://gitlab.com/gitlab-org/modelops/applied-ml/review-recommender/cluster-management
10
+ MANIFESTS_PATH = File.expand_path('../../../../support/manifests/suggested_reviewer', __dir__)
11
+
12
+ def initialize
13
+ @cluster = Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::K3d)
14
+ end
15
+
16
+ def name
17
+ @name ||= "suggested_reviewer"
18
+ end
19
+
20
+ def prepare
21
+ @cluster.create_registry_mirror
22
+ end
23
+
24
+ def create_cluster
25
+ @cluster.create!
26
+ end
27
+
28
+ def deploy_services
29
+ Dir.glob(File.join(MANIFESTS_PATH, '**/*')).each do |file|
30
+ Runtime::Logger.info("Applying manifest #{file}")
31
+ @cluster.apply_manifest(File.read(file))
32
+ end
33
+ end
34
+
35
+ def teardown
36
+ @cluster.remove!
37
+ end
38
+
39
+ def wait_until_ready; end
40
+
41
+ def teardown?
42
+ !Runtime::Scenario.attributes.include?(:teardown) || Runtime::Scenario.teardown
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -4,7 +4,7 @@ module Gitlab
4
4
  module QA
5
5
  module Docker
6
6
  class Command
7
- attr_reader :args, :stream_output
7
+ attr_reader :args
8
8
 
9
9
  # Shell command
10
10
  #
@@ -41,24 +41,13 @@ module Gitlab
41
41
  "docker #{@args.join(' ')}"
42
42
  end
43
43
 
44
- # Returns a masked string form of a Command
45
- #
46
- # @example
47
- # Command.new('a docker command', mask_secrets: 'command').mask_secrets #=> 'a docker *****'
48
- # Command.new('a docker command', mask_secrets: %w[docker command]).mask_secrets #=> 'a ***** *****'
49
- #
50
- # @return [String] The masked command string
51
- def mask_secrets
52
- @mask_secrets.each_with_object(+to_s) { |secret, s| s.gsub!(secret, '*****') }
53
- end
54
-
55
44
  def ==(other)
56
45
  to_s == other.to_s
57
46
  end
58
47
 
59
48
  def execute!(&block)
60
- Docker::Shellout.new(self).execute!(&block)
61
- rescue Docker::Shellout::StatusError => e
49
+ Support::ShellCommand.new(to_s, mask_secrets: @mask_secrets, stream_output: @stream_output).execute!(&block)
50
+ rescue Support::ShellCommand::StatusError => e
62
51
  e.set_backtrace([])
63
52
 
64
53
  raise e
@@ -112,7 +112,7 @@ module Gitlab
112
112
 
113
113
  def manifest_exists?(name)
114
114
  Docker::Command.execute("manifest inspect #{name}")
115
- rescue Docker::Shellout::StatusError
115
+ rescue Support::ShellCommand::StatusError
116
116
  false
117
117
  else
118
118
  true
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/module/delegation"
4
+
3
5
  module Gitlab
4
6
  module QA
5
7
  class Release
@@ -23,6 +25,8 @@ module Gitlab
23
25
  \z
24
26
  }xi.freeze
25
27
 
28
+ delegate :ci_project_path, to: Gitlab::QA::Runtime::Env
29
+
26
30
  # Official dev tag example:
27
31
  # 12.5.4(-rc42)-ee
28
32
  # |-------------|--|
@@ -109,7 +113,7 @@ module Gitlab
109
113
  def qa_image
110
114
  @qa_image ||= if omnibus_mirror?
111
115
  omnibus_project = image.match(CUSTOM_GITLAB_IMAGE_REGEX)[:project]
112
- gitlab_project = "/gitlab-org/gitlab/"
116
+ gitlab_project = ci_project_path ? "/#{ci_project_path}/" : "/gitlab-org/gitlab/"
113
117
 
114
118
  "#{image.gsub(omnibus_project, gitlab_project)}-qa"
115
119
  else
@@ -139,7 +143,7 @@ module Gitlab
139
143
  # Tag scheme for gitlab-{ce,ee}-qa images is like 11.1.0-rc12-ee
140
144
  def qa_tag
141
145
  if dev_gitlab_org? && (match_data = tag.match(DEV_TAG_REGEX))
142
- "#{match_data[:version]}-#{match_data[:gitlab_ref]}"
146
+ match_data[:gitlab_ref]
143
147
  else
144
148
  tag.sub(/[-.]([ce]e)(\.(\d+))?\z/, '-\1')
145
149
  end
@@ -32,7 +32,7 @@ module Gitlab
32
32
  end
33
33
 
34
34
  def new_issue_labels(test)
35
- ['Quality', "devops::#{test.stage}", 'status::automated']
35
+ %w[Quality status::automated]
36
36
  end
37
37
 
38
38
  def search_term(test)
@@ -94,6 +94,7 @@ module Gitlab
94
94
  'CI_NODE_TOTAL' => :ci_node_total,
95
95
  'CI_PROJECT_ID' => :ci_project_id,
96
96
  'CI_PROJECT_NAME' => :ci_project_name,
97
+ 'CI_PROJECT_PATH' => :ci_project_path,
97
98
  'CI_SLACK_WEBHOOK_URL' => :ci_slack_webhook_url,
98
99
  'CI_PIPELINE_ID' => :ci_pipeline_id,
99
100
  'CI_PIPELINE_SOURCE' => :ci_pipeline_source,
@@ -272,6 +273,12 @@ module Gitlab
272
273
  raise ArgumentError, "Please provide CI_SLACK_WEBHOOK_URL"
273
274
  end
274
275
 
276
+ def require_qa_container_registry_access_token!
277
+ return unless env_var_value_if_defined('GITLAB_QA_CONTAINER_REGISTRY_ACCESS_TOKEN').to_s.strip.empty?
278
+
279
+ raise ArgumentError, "Please provide GITLAB_QA_CONTAINER_REGISTRY_ACCESS_TOKEN"
280
+ end
281
+
275
282
  def require_kubernetes_environment!
276
283
  %w[GCLOUD_ACCOUNT_EMAIL GCLOUD_ACCOUNT_KEY CLOUDSDK_CORE_PROJECT].each do |env_key|
277
284
  unless ENV.key?(env_key)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module QA
5
+ module Scenario
6
+ module Test
7
+ module Integration
8
+ class SuggestedReviewer < Scenario::Template
9
+ attr_reader :spec_suite
10
+
11
+ def initialize
12
+ @spec_suite = 'Test::Instance::All'
13
+ @network = 'test'
14
+ @env = {}
15
+ @tag = 'suggested_reviewer'
16
+ @gitlab_name = 'gitlab-suggested-reviewer'
17
+ end
18
+
19
+ def perform(release, *rspec_args)
20
+ Component::Gitlab.perform do |gitlab|
21
+ gitlab.release = QA::Release.new(release)
22
+ gitlab.name = @gitlab_name
23
+ gitlab.network = @network
24
+
25
+ gitlab.instance do
26
+ # Wait for the suggested reviewer services to be ready before attempting to run specs
27
+ @cluster = suggested_reviewer_cluster
28
+ @cluster.wait_until_ready
29
+
30
+ Runtime::Logger.info('Running Suggested Reviewer specs!')
31
+
32
+ if @tag
33
+ rspec_args << "--" unless rspec_args.include?('--')
34
+ rspec_args << "--tag" << @tag
35
+ end
36
+
37
+ Component::Specs.perform do |specs|
38
+ specs.suite = spec_suite
39
+ specs.release = gitlab.release
40
+ specs.network = gitlab.network
41
+ specs.args = [gitlab.address, *rspec_args]
42
+ specs.env = @env
43
+ end
44
+ end
45
+ end
46
+ ensure
47
+ @cluster&.teardown if @cluster&.teardown?
48
+ end
49
+
50
+ def suggested_reviewer_cluster
51
+ Component::SuggestedReviewer.new.tap do |sr|
52
+ sr.prepare
53
+ sr.create_cluster
54
+ sr.deploy_services
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -94,7 +94,7 @@ module Gitlab
94
94
  specs.network = gitlab.network
95
95
  specs.args = [gitlab.address, *rspec_args]
96
96
  end
97
- rescue Docker::Shellout::StatusError => e
97
+ rescue Support::ShellCommand::StatusError => e
98
98
  raise e if release == current_release # only fail on current release
99
99
 
100
100
  Runtime::Logger.warn("Test run for release '#{gitlab.release}' finished with errors!")
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module QA
5
+ module Service
6
+ module ClusterProvider
7
+ class Base
8
+ include Support::Shellout
9
+
10
+ GITLAB_REGISTRY = 'registry.gitlab.com'
11
+
12
+ attr_accessor :create_args
13
+
14
+ def cluster_name
15
+ @cluster_name ||= "qa-cluster-#{Time.now.utc.strftime('%Y%m%d')}-#{SecureRandom.hex(4)}"
16
+ end
17
+
18
+ def validate_dependencies
19
+ raise NotImplementedError
20
+ end
21
+
22
+ def setup
23
+ raise NotImplementedError
24
+ end
25
+
26
+ def teardown
27
+ raise NotImplementedError
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -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