gitlab-qa 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -25,6 +25,10 @@ command in your system.
25
25
 
26
26
  `gitlab-qa Test::Instance::Image CE|EE|<full image address>`
27
27
 
28
+ 1. Run tests against a Docker image with GitLab and Mattermost:
29
+
30
+ `gitlab-qa Test::Integration::Mattermost CE|EE|<full image address>`
31
+
28
32
  1. Test upgrade process:
29
33
 
30
34
  `gitlab-qa Test::Omnibus::Upgrade CE|EE|<full image address>`
@@ -61,16 +65,29 @@ This scenario tests that the GitLab Docker container works as expected by
61
65
  running tests against it.
62
66
 
63
67
  To run tests against the GitLab containers, a GitLab QA (`gitlab/gitlab-qa`)
64
- container is spin up and tests are run from it by running the `Test::Instance`
68
+ container is spun up and tests are run from it by running the `Test::Instance`
65
69
  scenario (located under `qa/scenario/test/instance` in the GitLab codebase).
66
70
 
71
+ ### `Test::Integration::Mattermost CE|EE|<full image address>`
72
+
73
+ This scenario tests that GitLab instance works as expected when
74
+ enabling the embedded Mattermost server (see `Test::Instance::Image`
75
+ above).
76
+
67
77
  ### `Test::Instance::Any CE|EE|<full image address>`
68
78
 
69
79
  This scenario tests that the any GitLab instance works as expected by running
70
- tests against it (see `Test::Instance::Image` below).
80
+ tests against it (see `Test::Instance::Image` above).
71
81
 
72
82
  ## Supported environment variables
73
83
 
74
84
  * `GITLAB_USERNAME` - username to use when signing in to GitLab
75
- * `GITLAB_PASSWORD` - password to use when signing in to GitLab
85
+ * `GITLAB_PASSWORD` - password to use when signing in to GitLab
76
86
  * `EE_LICENSE` - Enterprise Edition license
87
+ * `QA_SCREENSHOTS_DIR` - Path to a directory where screenshots for failing tests
88
+ will be saved (default: `/tmp/gitlab-qa-screenshots`)
89
+ * `DOCKER_HOST` - Docker host to run tests against (default: `http://localhost`)
90
+
91
+ ## Contributing
92
+
93
+ Please see the [contribution guidelines](CONTRIBUTING.md)
@@ -22,14 +22,22 @@ module Gitlab
22
22
  autoload :Image, 'qa/scenario/test/omnibus/image'
23
23
  autoload :Upgrade, 'qa/scenario/test/omnibus/upgrade'
24
24
  end
25
+
26
+ module Integration
27
+ autoload :Mattermost, 'qa/scenario/test/integration/mattermost'
28
+ end
25
29
  end
26
30
  end
27
31
 
32
+ module Component
33
+ autoload :Gitlab, 'qa/component/gitlab'
34
+ autoload :Specs, 'qa/component/specs'
35
+ end
36
+
28
37
  module Docker
29
38
  autoload :Engine, 'qa/docker/engine'
30
39
  autoload :Command, 'qa/docker/command'
31
- autoload :Gitlab, 'qa/docker/gitlab'
32
- autoload :Specs, 'qa/docker/specs'
40
+ autoload :Shellout, 'qa/docker/shellout'
33
41
  end
34
42
  end
35
43
  end
@@ -1,28 +1,42 @@
1
1
  require 'securerandom'
2
2
  require 'net/http'
3
3
  require 'uri'
4
+ require 'forwardable'
5
+ require 'shellwords'
4
6
 
5
7
  module Gitlab
6
8
  module QA
7
- module Docker
9
+ module Component
8
10
  class Gitlab
11
+ extend Forwardable
9
12
  include Scenario::Actable
10
13
 
11
14
  # rubocop:disable Style/Semicolon
12
15
 
13
- attr_accessor :image, :tag, :volumes, :network
14
- attr_reader :name, :release
16
+ attr_reader :release, :docker
17
+ attr_accessor :volumes, :network, :environment, :network_aliases
18
+
19
+ def_delegators :release, :tag, :image, :edition
15
20
 
16
21
  def initialize
17
22
  @docker = Docker::Engine.new
23
+ @environment = {}
24
+ @volumes = {}
25
+ @network_aliases = []
26
+
27
+ self.release = 'CE'
18
28
  end
19
29
 
20
- def name=(name)
21
- @name = "#{name}-#{SecureRandom.hex(4)}"
30
+ def omnibus_config=(config)
31
+ @environment['GITLAB_OMNIBUS_CONFIG'] = config
22
32
  end
23
33
 
24
34
  def release=(release)
25
- @release ||= Release.new(release)
35
+ @release = Release.new(release)
36
+ end
37
+
38
+ def name
39
+ @name ||= "gitlab-qa-#{edition}-#{SecureRandom.hex(4)}"
26
40
  end
27
41
 
28
42
  def address
@@ -30,7 +44,7 @@ module Gitlab
30
44
  end
31
45
 
32
46
  def hostname
33
- "#{@name}.#{@network}"
47
+ "#{name}.#{network}"
34
48
  end
35
49
 
36
50
  def instance
@@ -44,29 +58,40 @@ module Gitlab
44
58
  end
45
59
 
46
60
  def prepare
47
- @docker.pull(@image, @tag)
61
+ @docker.pull(image, tag)
48
62
 
49
- return if @docker.network_exists?(@network)
50
- @docker.network_create(@network)
63
+ return if @docker.network_exists?(network)
64
+ @docker.network_create(network)
51
65
  end
52
66
 
67
+ # rubocop:disable Metrics/MethodLength
68
+ # rubocop:disable Metrics/AbcSize
53
69
  def start
54
- unless [@name, @image, @tag, @network].all?
55
- raise 'Please configure an instance first!'
56
- end
70
+ ensure_configured!
57
71
 
58
- @docker.run(@image, @tag) do |command|
59
- command << "-d --name #{@name} -p 80:80"
60
- command << "--net #{@network} --hostname #{hostname}"
72
+ docker.run(image, tag) do |command|
73
+ command << '-d -p 80:80'
74
+ command << "--name #{name}"
75
+ command << "--net #{network}"
76
+ command << "--hostname #{hostname}"
61
77
 
62
78
  @volumes.to_h.each do |to, from|
63
79
  command << "--volume #{to}:#{from}:Z"
64
80
  end
81
+
82
+ @environment.to_h.each do |key, value|
83
+ escaped_value = Shellwords.escape(value)
84
+ command << "--env #{key}=#{escaped_value}"
85
+ end
86
+
87
+ @network_aliases.to_a.each do |network_alias|
88
+ command << "--network-alias #{network_alias}"
89
+ end
65
90
  end
66
91
  end
67
92
 
68
93
  def reconfigure
69
- @docker.attach(@name) do |line, wait|
94
+ @docker.attach(name) do |line, wait|
70
95
  # TODO, workaround which allows to detach from the container
71
96
  #
72
97
  Process.kill('INT', wait.pid) if line =~ /gitlab Reconfigured!/
@@ -74,14 +99,14 @@ module Gitlab
74
99
  end
75
100
 
76
101
  def restart
77
- @docker.restart(@name)
102
+ @docker.restart(name)
78
103
  end
79
104
 
80
105
  def teardown
81
- raise 'Invalid instance name!' unless @name
106
+ raise 'Invalid instance name!' unless name
82
107
 
83
- @docker.stop(@name)
84
- @docker.remove(@name)
108
+ @docker.stop(name)
109
+ @docker.remove(name)
85
110
  end
86
111
 
87
112
  def wait
@@ -96,6 +121,15 @@ module Gitlab
96
121
  end
97
122
  end
98
123
 
124
+ private
125
+
126
+ # rubocop:disable Style/GuardClause
127
+ def ensure_configured!
128
+ unless [name, release, network].all?
129
+ raise 'Please configure an instance first!'
130
+ end
131
+ end
132
+
99
133
  class Availability
100
134
  def initialize(address)
101
135
  @uri = URI.join(address, '/help')
@@ -0,0 +1,59 @@
1
+ module Gitlab
2
+ module QA
3
+ module Component
4
+ ##
5
+ # This class represents GitLab QA specs image that is implemented in
6
+ # the `qa/` directory located in GitLab CE / EE repositories.
7
+ #
8
+ # TODO, it needs some refactoring.
9
+ #
10
+ class Specs
11
+ include Scenario::Actable
12
+
13
+ IMAGE_NAME = 'gitlab/gitlab-qa'.freeze
14
+
15
+ def initialize
16
+ @docker = Docker::Engine.new
17
+ end
18
+
19
+ def test(gitlab:, suite: 'Test::Instance', extra_args: [])
20
+ test_address(
21
+ release: gitlab.release,
22
+ address: gitlab.address,
23
+ name: "#{gitlab.name}-specs",
24
+ network: gitlab.network,
25
+ suite: suite,
26
+ extra_args: extra_args
27
+ )
28
+ end
29
+
30
+ # rubocop:disable Metrics/ParameterLists
31
+ def test_address(release:, address:, name: nil, network: nil,
32
+ suite: 'Test::Instance', extra_args: [])
33
+ puts "Running instance suite #{suite} for Gitlab " \
34
+ "#{release.edition.upcase} at #{address}"
35
+
36
+ args = [suite, address] + extra_args
37
+
38
+ @docker.run(IMAGE_NAME, release.edition_tag, *args) do |command|
39
+ build_command(command, name, network)
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def build_command(command, name, network)
46
+ command << "-t --rm --net=#{network || 'bridge'}"
47
+
48
+ Runtime::Env.delegated.each do |env|
49
+ command.env(env, "$#{env}")
50
+ end
51
+
52
+ command.volume('/var/run/docker.sock', '/var/run/docker.sock')
53
+ command.volume(Runtime::Env.screenshots_dir, '/home/qa/tmp')
54
+ command.name(name || "gitlab-specs-#{Time.now.to_i}")
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,17 +1,9 @@
1
- require 'open3'
2
-
3
1
  module Gitlab
4
2
  module QA
5
3
  module Docker
6
4
  class Command
7
- class StatusError < StandardError; end
8
-
9
5
  attr_reader :args
10
6
 
11
- def self.execute(cmd, &block)
12
- new(cmd).execute!(&block)
13
- end
14
-
15
7
  def initialize(cmd = nil)
16
8
  @args = Array(cmd)
17
9
  end
@@ -20,25 +12,28 @@ module Gitlab
20
12
  tap { @args.concat(args) }
21
13
  end
22
14
 
23
- def execute!(&block)
24
- engine("docker #{@args.join(' ')}", &block)
15
+ def volume(from, to)
16
+ tap { @args << "-v #{from}:#{to}" }
25
17
  end
26
18
 
27
- private
19
+ def name(identity)
20
+ tap { @args << "--name #{identity}" }
21
+ end
28
22
 
29
- def engine(cmd)
30
- puts "Running shell command: `#{cmd}`"
23
+ def env(name, value)
24
+ tap { @args << %(-e #{name}="#{value}") }
25
+ end
31
26
 
32
- Open3.popen2e(cmd) do |_in, out, wait|
33
- out.each do |line|
34
- puts line
35
- yield line, wait if block_given?
36
- end
27
+ def to_s
28
+ "docker #{@args.join(' ')}"
29
+ end
37
30
 
38
- if wait.value.exited? && wait.value.exitstatus.nonzero?
39
- raise StatusError, "Docker command `#{cmd}` failed!"
40
- end
41
- end
31
+ def execute!(&block)
32
+ Docker::Shellout.execute!(self, &block)
33
+ end
34
+
35
+ def self.execute(cmd, &block)
36
+ new(cmd).execute!(&block)
42
37
  end
43
38
  end
44
39
  end
@@ -41,7 +41,7 @@ module Gitlab
41
41
 
42
42
  def network_exists?(name)
43
43
  Docker::Command.execute("network inspect #{name}")
44
- rescue Docker::Command::StatusError
44
+ rescue Docker::Shellout::StatusError
45
45
  false
46
46
  else
47
47
  true
@@ -0,0 +1,27 @@
1
+ require 'open3'
2
+
3
+ module Gitlab
4
+ module QA
5
+ module Docker
6
+ class Shellout
7
+ class StatusError < StandardError; end
8
+
9
+ def self.execute!(command)
10
+ puts "Running shell command: `#{command}`"
11
+
12
+ Open3.popen2e(command.to_s) do |_in, out, wait|
13
+ out.each do |line|
14
+ puts line
15
+
16
+ yield line, wait if block_given?
17
+ end
18
+
19
+ if wait.value.exited? && wait.value.exitstatus.nonzero?
20
+ raise StatusError, "Docker command `#{command}` failed!"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -5,33 +5,55 @@ module Gitlab
5
5
  DEFAULT_TAG = 'nightly'.freeze
6
6
 
7
7
  attr_reader :release
8
+ attr_writer :tag
8
9
 
9
10
  def initialize(release)
10
11
  @release = release.to_s
11
12
  end
12
13
 
14
+ def to_s
15
+ "#{image}:#{tag}"
16
+ end
17
+
18
+ def previous_stable
19
+ # The previous stable is always gitlab/gitlab-ce:latest or
20
+ # gitlab/gitlab-ee:latest
21
+ self.class.new("#{canonical_image}:latest")
22
+ end
23
+
13
24
  def edition
14
- if canonical?
15
- release.downcase.to_sym
16
- else
17
- release.match(CUSTOM_GITLAB_IMAGE_REGEX)[1].to_sym
18
- end
25
+ @edition ||=
26
+ if canonical?
27
+ release.downcase.to_sym
28
+ else
29
+ release.match(CUSTOM_GITLAB_IMAGE_REGEX)[1].to_sym
30
+ end
19
31
  end
20
32
 
21
33
  def image
22
- if canonical?
23
- "gitlab/gitlab-#{edition}"
24
- else
25
- release.sub(/:.+\z/, '')
26
- end
34
+ @image ||=
35
+ if canonical?
36
+ "gitlab/gitlab-#{edition}"
37
+ else
38
+ release.sub(/:.+\z/, '')
39
+ end
40
+ end
41
+
42
+ def canonical_image
43
+ @canonical_image ||= "gitlab/gitlab-#{edition}"
27
44
  end
28
45
 
29
46
  def tag
30
- if canonical?
31
- DEFAULT_TAG
32
- else
33
- release.match(CUSTOM_GITLAB_IMAGE_REGEX)[2]
34
- end
47
+ @tag ||=
48
+ if canonical?
49
+ DEFAULT_TAG
50
+ else
51
+ release.match(CUSTOM_GITLAB_IMAGE_REGEX)[2]
52
+ end
53
+ end
54
+
55
+ def edition_tag
56
+ @edition_tag ||= "#{edition}-#{tag}"
35
57
  end
36
58
 
37
59
  private
@@ -4,12 +4,17 @@ module Gitlab
4
4
  module Env
5
5
  extend self
6
6
 
7
+ VARIABLES = %w(GITLAB_USERNAME
8
+ GITLAB_PASSWORD
9
+ GITLAB_URL
10
+ EE_LICENSE).freeze
11
+
7
12
  def screenshots_dir
8
13
  ENV['QA_SCREENSHOTS_DIR'] || '/tmp/gitlab-qa-screenshots'
9
14
  end
10
15
 
11
16
  def delegated
12
- %w(GITLAB_USERNAME GITLAB_PASSWORD GITLAB_URL EE_LICENSE).freeze
17
+ VARIABLES.select { |name| ENV[name] }
13
18
  end
14
19
  end
15
20
  end