gitlab-qa 6.21.3 → 7.0.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +3 -2
  3. data/docs/configuring_omnibus.md +208 -0
  4. data/docs/run_qa_against_gdk.md +4 -0
  5. data/docs/what_tests_can_be_run.md +34 -0
  6. data/gitlab-qa.gemspec +1 -1
  7. data/lib/gitlab/qa.rb +7 -0
  8. data/lib/gitlab/qa/component/gitlab.rb +37 -30
  9. data/lib/gitlab/qa/component/minio.rb +2 -10
  10. data/lib/gitlab/qa/component/specs.rb +10 -9
  11. data/lib/gitlab/qa/docker/engine.rb +36 -5
  12. data/lib/gitlab/qa/report/test_result.rb +5 -1
  13. data/lib/gitlab/qa/runner.rb +57 -5
  14. data/lib/gitlab/qa/runtime/env.rb +6 -0
  15. data/lib/gitlab/qa/runtime/omnibus_configuration.rb +69 -0
  16. data/lib/gitlab/qa/runtime/omnibus_configurations/default.rb +25 -0
  17. data/lib/gitlab/qa/runtime/omnibus_configurations/object_storage.rb +48 -0
  18. data/lib/gitlab/qa/runtime/omnibus_configurations/packages.rb +17 -0
  19. data/lib/gitlab/qa/scenario/cli_commands.rb +3 -3
  20. data/lib/gitlab/qa/scenario/test/instance/relative_url.rb +1 -3
  21. data/lib/gitlab/qa/scenario/test/instance/repository_storage.rb +1 -1
  22. data/lib/gitlab/qa/scenario/test/integration/actioncable.rb +1 -3
  23. data/lib/gitlab/qa/scenario/test/integration/geo.rb +4 -5
  24. data/lib/gitlab/qa/scenario/test/integration/gitaly_cluster.rb +3 -3
  25. data/lib/gitlab/qa/scenario/test/integration/group_saml.rb +1 -1
  26. data/lib/gitlab/qa/scenario/test/integration/instance_saml.rb +1 -1
  27. data/lib/gitlab/qa/scenario/test/integration/kubernetes.rb +1 -1
  28. data/lib/gitlab/qa/scenario/test/integration/ldap_no_server.rb +1 -1
  29. data/lib/gitlab/qa/scenario/test/integration/ldap_no_tls.rb +1 -1
  30. data/lib/gitlab/qa/scenario/test/integration/ldap_tls.rb +1 -2
  31. data/lib/gitlab/qa/scenario/test/integration/mattermost.rb +1 -1
  32. data/lib/gitlab/qa/scenario/test/integration/mtls.rb +3 -7
  33. data/lib/gitlab/qa/scenario/test/integration/smtp.rb +1 -1
  34. data/lib/gitlab/qa/scenario/test/integration/ssh_tunnel.rb +1 -1
  35. data/lib/gitlab/qa/version.rb +1 -1
  36. data/tls_certificates/authority/ca.crt +32 -0
  37. data/tls_certificates/authority/ca.key +51 -0
  38. data/tls_certificates/authority/ca.pem +83 -0
  39. data/tls_certificates/gitaly/gitaly.test.crt +30 -0
  40. data/tls_certificates/gitaly/gitaly.test.csr +28 -0
  41. data/tls_certificates/gitaly/gitaly.test.key +51 -0
  42. data/tls_certificates/gitlab/gitlab.test.crt +28 -29
  43. data/tls_certificates/gitlab/gitlab.test.csr +28 -0
  44. data/tls_certificates/gitlab/gitlab.test.key +51 -52
  45. metadata +16 -10
  46. data/lib/gitlab/qa/scenario/test/integration/object_storage.rb +0 -64
  47. data/lib/gitlab/qa/scenario/test/integration/packages.rb +0 -36
  48. data/tls_certificates/gitaly/ssl/gitaly.test.crt +0 -33
  49. data/tls_certificates/gitaly/ssl/gitaly.test.key +0 -52
  50. data/tls_certificates/gitaly/trusted-certs/gitaly.test.crt +0 -33
  51. data/tls_certificates/gitaly/trusted-certs/gitlab.test.crt +0 -31
@@ -1,5 +1,6 @@
1
1
  require 'securerandom'
2
2
  require 'fileutils'
3
+ require 'yaml'
3
4
 
4
5
  # This component sets up the Minio (https://hub.docker.com/r/minio/minio)
5
6
  # image with the proper configuration for GitLab users to use object storage.
@@ -23,18 +24,12 @@ module Gitlab
23
24
  @buckets = []
24
25
  end
25
26
 
26
- def instance
27
- raise 'Please provide a block!' unless block_given?
28
-
29
- super
30
- end
31
-
32
27
  def add_bucket(name)
33
28
  @buckets << name
34
29
  end
35
30
 
36
31
  def to_config
37
- config = YAML.safe_load <<~CFG
32
+ YAML.safe_load <<~CFG
38
33
  provider: AWS
39
34
  aws_access_key_id: #{AWS_ACCESS_KEY}
40
35
  aws_secret_access_key: #{AWS_SECRET_KEY}
@@ -43,9 +38,6 @@ module Gitlab
43
38
  endpoint: http://#{hostname}:#{port}
44
39
  path_style: true
45
40
  CFG
46
-
47
- # Quotes get eaten up when the string is set in the environment
48
- config.to_s.gsub('"', '\\"')
49
41
  end
50
42
 
51
43
  private
@@ -10,9 +10,6 @@ module Gitlab
10
10
  class Specs < Scenario::Template
11
11
  attr_accessor :suite, :release, :network, :args, :volumes, :env, :runner_network
12
12
 
13
- TRUSTED_CERTIFICATES_PATH = File.expand_path('../../../../tls_certificates/gitaly/trusted-certs'.freeze, __dir__)
14
- TRUSTED_PATH = '/etc/ssl/certs'.freeze
15
-
16
13
  def initialize
17
14
  @docker = Docker::Engine.new
18
15
  @volumes = {}
@@ -26,7 +23,7 @@ module Gitlab
26
23
 
27
24
  @docker.login(**release.login_params) if release.login_params
28
25
 
29
- @docker.pull(release.qa_image, release.qa_tag) unless Runtime::Env.skip_pull?
26
+ @docker.pull(qa_image) unless Runtime::Env.skip_pull?
30
27
 
31
28
  puts "Running test suite `#{suite}` for #{release.project_name}"
32
29
 
@@ -46,7 +43,7 @@ module Gitlab
46
43
  feature_flag_sets << [] unless feature_flag_sets.any?
47
44
 
48
45
  feature_flag_sets.each do |feature_flag_set|
49
- @docker.run(release.qa_image, release.qa_tag, suite, *args_with_flags(args, feature_flag_set)) do |command|
46
+ @docker.run(qa_image, nil, suite, *args_with_flags(args, feature_flag_set)) do |command|
50
47
  command << "-t --rm --net=#{network || 'bridge'}"
51
48
 
52
49
  env.merge(Runtime::Env.variables).each do |key, value|
@@ -65,10 +62,6 @@ module Gitlab
65
62
  end
66
63
  end
67
64
 
68
- def mtls
69
- @volumes[TRUSTED_CERTIFICATES_PATH] = TRUSTED_PATH
70
- end
71
-
72
65
  private
73
66
 
74
67
  def args_with_flags(args, feature_flag_set)
@@ -83,6 +76,14 @@ module Gitlab
83
76
  def skip_tests?
84
77
  Runtime::Scenario.attributes.include?(:run_tests) && !Runtime::Scenario.run_tests
85
78
  end
79
+
80
+ def qa_image
81
+ if Runtime::Scenario.attributes.include?(:qa_image)
82
+ Runtime::Scenario.qa_image
83
+ else
84
+ "#{release.qa_image}:#{release.qa_tag}"
85
+ end
86
+ end
86
87
  end
87
88
  end
88
89
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Gitlab
2
4
  module QA
3
5
  module Docker
@@ -13,15 +15,15 @@ module Gitlab
13
15
  Docker::Command.execute(%(login --username "#{username}" --password "#{password}" #{registry}), mask_secrets: password)
14
16
  end
15
17
 
16
- def pull(image, tag)
17
- Docker::Command.execute("pull #{image}:#{tag}")
18
+ def pull(image, tag = nil)
19
+ Docker::Command.execute("pull #{full_image_name(image, tag)}")
18
20
  end
19
21
 
20
22
  def run(image, tag, *args)
21
23
  Docker::Command.new('run').tap do |command|
22
24
  yield command if block_given?
23
25
 
24
- command << "#{image}:#{tag}"
26
+ command << full_image_name(image, tag)
25
27
  command << args if args.any?
26
28
 
27
29
  command.execute!
@@ -36,14 +38,37 @@ module Gitlab
36
38
  false
37
39
  end
38
40
 
41
+ # Write to file(s) in the Docker container specified by @param name
42
+ # @param name The name of the Docker Container
43
+ # @example
44
+ # engine.write_files('gitlab-abc123') do |files|
45
+ # files.append('/etc/hosts', '127.0.0.1 localhost')
46
+ # files.write('/opt/other', <<~TEXT
47
+ # This is content
48
+ # That goes within /opt/other
49
+ # TEXT)
50
+ def write_files(name)
51
+ exec(name, yield(
52
+ Class.new do
53
+ def self.write(file, contents)
54
+ %(echo "#{contents}" > #{file};)
55
+ end
56
+
57
+ def self.append(file, contents)
58
+ %(echo "#{contents}" >> #{file};)
59
+ end
60
+ end
61
+ ))
62
+ end
63
+
39
64
  def exec(name, command)
40
65
  cmd = ['exec']
41
66
  cmd << '--privileged' if privileged_command?(command)
42
- Docker::Command.execute("#{cmd.join(' ')} #{name} bash -c '#{command}'")
67
+ Docker::Command.execute(%(#{cmd.join(' ')} #{name} bash -c "#{command.gsub('"', '\\"')}"))
43
68
  end
44
69
 
45
70
  def read_file(image, tag, path, &block)
46
- cat_file = "run --rm --entrypoint /bin/cat #{image}:#{tag} #{path}"
71
+ cat_file = "run --rm --entrypoint /bin/cat #{full_image_name(image, tag)} #{path}"
47
72
  Docker::Command.execute(cat_file, &block)
48
73
  end
49
74
 
@@ -94,6 +119,12 @@ module Gitlab
94
119
  def ps(name = nil)
95
120
  Docker::Command.execute(['ps', name].compact.join(' '))
96
121
  end
122
+
123
+ private
124
+
125
+ def full_image_name(image, tag)
126
+ [image, tag].compact.join(':')
127
+ end
97
128
  end
98
129
  end
99
130
  end
@@ -101,10 +101,14 @@ module Gitlab
101
101
  {
102
102
  'message' => "#{exception['class']}: #{exception['message']}",
103
103
  'message_lines' => exception['message_lines'],
104
- 'stacktrace' => "#{exception['message_lines'].join("\n")}\n#{exception['backtrace'].slice(0..spec_file_first_index).join("\n")}"
104
+ 'stacktrace' => "#{format_message_lines(exception['message_lines'])}\n#{exception['backtrace'].slice(0..spec_file_first_index).join("\n")}"
105
105
  }
106
106
  end
107
107
  end
108
+
109
+ def format_message_lines(message_lines)
110
+ message_lines.is_a?(Array) ? message_lines.join("\n") : message_lines
111
+ end
108
112
  # rubocop:enable Metrics/AbcSize
109
113
  end
110
114
 
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
4
+ require 'active_support/inflector'
2
5
 
3
6
  module Gitlab
4
7
  module QA
@@ -7,6 +10,12 @@ module Gitlab
7
10
  def self.run(args)
8
11
  Runtime::Scenario.define(:teardown, true)
9
12
  Runtime::Scenario.define(:run_tests, true)
13
+ Runtime::Scenario.define(:qa_image, Runtime::Env.qa_image) if Runtime::Env.qa_image
14
+ Runtime::Scenario.define(:omnibus_configuration, Runtime::OmnibusConfiguration.new)
15
+
16
+ # Omnibus Configurators specified by flags
17
+ @active_configurators = []
18
+ @omnibus_configurations = %w[default] # always load default configuration
10
19
 
11
20
  @options = OptionParser.new do |opts|
12
21
  opts.banner = 'Usage: gitlab-qa [options] Scenario URL [[--] path] [rspec_options]'
@@ -20,12 +29,22 @@ module Gitlab
20
29
  Runtime::Scenario.define(:teardown, false)
21
30
  end
22
31
 
32
+ opts.on('--qa-image QA_IMAGE', String, 'Specifies a QA image to be used instead of inferring it from the GitLab image. See Gitlab::QA::Release#qa_image') do |value|
33
+ Runtime::Scenario.define(:qa_image, value)
34
+ end
35
+
23
36
  opts.on_tail('-v', '--version', 'Show the version') do
24
37
  require 'gitlab/qa/version'
25
38
  puts "#{$PROGRAM_NAME} : #{VERSION}"
26
39
  exit
27
40
  end
28
41
 
42
+ opts.on('--omnibus-config config1[,config2,...]', 'Use Omnibus Configuration package') do |configuration|
43
+ configuration.split(',').map do |config|
44
+ @omnibus_configurations << config
45
+ end
46
+ end
47
+
29
48
  opts.on_tail('-h', '--help', 'Show the usage') do
30
49
  puts opts
31
50
  exit
@@ -39,21 +58,54 @@ module Gitlab
39
58
  end
40
59
 
41
60
  args.reject! { |arg| gitlab_qa_options.include?(arg) }
42
-
43
61
  if args.size >= 1
44
- Scenario
45
- .const_get(args.shift)
46
- .perform(*args)
62
+ load_omnibus_configurations
63
+
64
+ begin
65
+ @active_configurators.compact.each do |configurator|
66
+ configurator.instance(skip_teardown: true)
67
+ end
68
+
69
+ Scenario
70
+ .const_get(args.shift)
71
+ .perform(*args)
72
+ ensure
73
+ @active_configurators.compact.each(&:teardown)
74
+ end
47
75
  else
48
76
  puts @options
49
77
  exit 1
50
78
  end
51
79
  end
52
- # rubocop:enable Metrics/AbcSize
53
80
 
54
81
  def self.gitlab_qa_options
55
82
  @gitlab_qa_options ||= @options.top.list.map(&:long).flatten
56
83
  end
84
+
85
+ def self.load_omnibus_configurations
86
+ # OmnibusConfiguration::Test => --test
87
+ # OmnibusConfiguration::HelloThere => --hello_there
88
+ @omnibus_configurations.uniq.each do |config|
89
+ Runtime::OmnibusConfigurations.const_get(config.camelize).new.tap do |configurator|
90
+ @active_configurators << configurator.prepare
91
+
92
+ # */etc/gitlab/gitlab.rb*
93
+ # -----------------------
94
+ # # Runtime::OmnibusConfiguration::Packages
95
+ # gitlab_rails['packages_enabled'] = true
96
+ Runtime::Scenario.omnibus_configuration << "# #{configurator.class.name}"
97
+
98
+ # Load the configuration
99
+ configurator.configuration.split("\n").each { |c| Runtime::Scenario.omnibus_configuration << c }
100
+ end
101
+ rescue NameError
102
+ raise <<~ERROR
103
+ Invalid Omnibus Configuration `#{config}`.
104
+ Possible configurations: #{Runtime::OmnibusConfigurations.constants.map { |c| c.to_s.underscore }.join(',')}"
105
+ ERROR
106
+ end
107
+ end
57
108
  end
109
+ # rubocop:enable Metrics/AbcSize
58
110
  end
59
111
  end
@@ -10,6 +10,7 @@ module Gitlab
10
10
  # These variables should be listed in /docs/what_tests_can_be_run.md#supported-gitlab-environment-variables
11
11
  # unless they're defined elsewhere (e.g.: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html)
12
12
  ENV_VARIABLES = {
13
+ 'QA_IMAGE' => :qa_image,
13
14
  'QA_REMOTE_GRID' => :remote_grid,
14
15
  'QA_REMOTE_GRID_USERNAME' => :remote_grid_username,
15
16
  'QA_REMOTE_GRID_ACCESS_KEY' => :remote_grid_access_key,
@@ -64,6 +65,7 @@ module Gitlab
64
65
  'KNAPSACK_TEST_FILE_PATTERN' => :knapsack_test_file_pattern,
65
66
  'KNAPSACK_TEST_DIR' => :knapsack_test_dir,
66
67
  'CI' => :ci,
68
+ 'CI_JOB_NAME' => :ci_job_name,
67
69
  'CI_JOB_URL' => :ci_job_url,
68
70
  'CI_RUNNER_ID' => :ci_runner_id,
69
71
  'CI_SERVER_HOST' => :ci_server_host,
@@ -106,6 +108,10 @@ module Gitlab
106
108
  ENV['QA_DEFAULT_BRANCH'] || 'master'
107
109
  end
108
110
 
111
+ def gitlab_availability_timeout
112
+ ENV.fetch('GITLAB_QA_AVAILABILITY_TIMEOUT', 360).to_i
113
+ end
114
+
109
115
  def gitlab_username
110
116
  ENV['GITLAB_USERNAME'] || 'gitlab-qa'
111
117
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/object/blank'
4
+
5
+ module Gitlab
6
+ module QA
7
+ module Runtime
8
+ class OmnibusConfiguration
9
+ def initialize
10
+ @config = ["# Generated by GitLab QA Omnibus Configurator at #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"]
11
+ end
12
+
13
+ def to_s
14
+ sanitize!.join("\n")
15
+ end
16
+
17
+ def configuration
18
+ raise NotImplementedError
19
+ end
20
+
21
+ # Before hook for any additional configuration
22
+ # This would usually be a container that needs to be running
23
+ # @return Any instance of [Gitlab::QA::Component::Base]
24
+ def prepare; end
25
+
26
+ # Commands to execute before GitLab boots
27
+ def exec_commands
28
+ []
29
+ end
30
+
31
+ # Ensures no duplicate entries
32
+ # @raise RuntimeError if competing configurations exist
33
+ # rubocop:disable Metrics/AbcSize
34
+ def sanitize!
35
+ sanitized = @config.map do |config|
36
+ next config if config.start_with?('#') # allow for comment
37
+
38
+ # sometimes "=" is part of a Hash. Only split based on the first "="
39
+ k, v = config.split("=", 2)
40
+ # make sure each config is well-formed
41
+ # e.g., gitlab_rails['packages_enabled'] = true
42
+ # NOT gitlab_rails['packages_enabled']=true
43
+ "#{k.strip} = #{v.strip.tr('"', "'")}".strip
44
+ end.uniq
45
+
46
+ errors = []
47
+
48
+ # check for duplicates
49
+ duplicate_keys = []
50
+ duplicates = sanitized.reject do |n|
51
+ key = n.split('=').first
52
+ duplicate_keys << key unless duplicate_keys.include?(key)
53
+ end
54
+
55
+ duplicates.each { |duplicate| errors << "Duplicate entry found: `#{duplicate}`" }
56
+
57
+ raise "Errors exist within the Omnibus Configuration!\n#{errors.join(',')}" if errors.any?
58
+
59
+ @config = sanitized
60
+ end
61
+ # rubocop:enable Metrics/AbcSize
62
+
63
+ def <<(config)
64
+ @config << config.strip unless config.strip.empty?
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module QA
5
+ module Runtime
6
+ module OmnibusConfigurations
7
+ # Default Configuration for Omnibus
8
+ # All runs will include this configuration
9
+ class Default < Runtime::OmnibusConfiguration
10
+ def configuration
11
+ <<~OMNIBUS
12
+ gitlab_rails['gitlab_default_theme'] = 10 # Light Red Theme
13
+ gitlab_rails['gitlab_disable_animations'] = true # Disable animations
14
+ gitlab_rails['application_settings_cache_seconds'] = 0 # Settings cache expiry
15
+ OMNIBUS
16
+ end
17
+
18
+ def self.configuration
19
+ new.configuration
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Gitlab
4
+ module QA
5
+ module Runtime
6
+ module OmnibusConfigurations
7
+ class ObjectStorage < Default
8
+ TYPES = %w[artifacts external_diffs lfs uploads packages dependency_proxy].freeze
9
+
10
+ def configuration
11
+ TYPES.each_with_object(+'') do |object_type, omnibus|
12
+ omnibus << <<~OMNIBUS
13
+ gitlab_rails['#{object_type}_enabled'] = true
14
+ gitlab_rails['#{object_type}_storage_path'] = '/var/opt/gitlab/gitlab-rails/shared/#{object_type}'
15
+ gitlab_rails['#{object_type}_object_store_enabled'] = true
16
+ gitlab_rails['#{object_type}_object_store_remote_directory'] = '#{object_type}-bucket'
17
+ gitlab_rails['#{object_type}_object_store_background_upload'] = false
18
+ gitlab_rails['#{object_type}_object_store_direct_upload'] = true
19
+ gitlab_rails['#{object_type}_object_store_proxy_download'] = true
20
+ gitlab_rails['#{object_type}_object_store_connection'] = #{minio.to_config}
21
+ OMNIBUS
22
+ end
23
+ end
24
+
25
+ def prepare
26
+ minio.network = 'test'
27
+
28
+ TYPES.each do |bucket_name|
29
+ minio.add_bucket("#{bucket_name}-bucket")
30
+ end
31
+
32
+ minio
33
+ end
34
+
35
+ def exec_commands
36
+ QA::Scenario::CLICommands.git_lfs_install_commands
37
+ end
38
+
39
+ private
40
+
41
+ def minio
42
+ @minio ||= Component::Minio.new
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end