gitlab-qa 6.21.3 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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