Commit 95141b32 authored by Grzegorz Bizon's avatar Grzegorz Bizon

Initialize gitlab-qa gem structure and move files

[ci skip]
parent 12ffc185
/.bundle/
/.yardoc
/Gemfile.lock
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
sudo: false
language: ruby
rvm:
- 2.3.1
before_install: gem install bundler -v 1.12.5
source 'https://rubygems.org'
gem 'rake', '~> 12.0.0'
gem 'rspec', '~> 3.5'
gem 'rubocop', '~> 0.47.1'
GEM
remote: https://rubygems.org/
specs:
ast (2.3.0)
diff-lcs (1.2.5)
parser (2.4.0.0)
ast (~> 2.2)
powerpack (0.1.1)
rainbow (2.2.1)
rake (12.0.0)
rspec (3.5.0)
rspec-core (~> 3.5.0)
rspec-expectations (~> 3.5.0)
rspec-mocks (~> 3.5.0)
rspec-core (3.5.2)
rspec-support (~> 3.5.0)
rspec-expectations (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-mocks (3.5.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
rubocop (0.47.1)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.8.1)
unicode-display_width (1.1.3)
PLATFORMS
ruby
DEPENDENCIES
rake (~> 12.0.0)
rspec (~> 3.5)
rubocop (~> 0.47.1)
BUNDLED WITH
1.12.5
require "bundler/gem_tasks"
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec)
task :default => :spec
#!/usr/bin/env ruby
require "bundler/setup"
require "gitlab/qa"
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start
require "irb"
IRB.start
#!/usr/bin/env ruby
require_relative '../lib/qa'
require 'gitlab-qa'
QA::Scenario
Gitlab::QA::Scenario
.const_get(ARGV.shift)
.perform(*ARGV)
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx
bundle install
# Do any other automated setup that you need to do here
#!/usr/bin/env ruby
require 'gitlab-qa'
Gitlab::QA::Scenario
.const_get(ARGV.shift)
.perform(*ARGV)
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'gitlab/qa/version'
Gem::Specification.new do |spec|
spec.name = "gitlab-qa"
spec.version = Gitlab::QA::VERSION
spec.authors = ['Grzegorz Bizon']
spec.email = ['grzesiek.bizon@gmail.com']
spec.summary = 'Integration tests for GitLab'
spec.homepage = 'http://about.gitlab.com'
spec.license = 'MIT'
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^spec/}) }
spec.bindir = 'exe'
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ['lib']
spec.add_development_dependency 'bundler', '~> 1.12'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'rubocop', '~> 0.47.1'
end
require 'open3'
module Gitlab
module QA
module Docker
class Command
class StatusError < StandardError; end
attr_reader :args
def self.execute(cmd, &block)
new(cmd).execute!(&block)
end
def initialize(cmd = nil)
@args = Array(cmd)
end
def <<(*args)
tap { @args.concat(args) }
end
def execute!(&block)
engine("docker #{@args.join(' ')}", &block)
end
private
def engine(cmd)
puts "Running shell command: `#{cmd}`"
Open3.popen2e(cmd) do |_in, out, wait|
out.each do |line|
puts line
yield line, wait if block_given?
end
if wait.value.exited? && wait.value.exitstatus.nonzero?
raise StatusError, "Docker command `#{cmd}` failed!"
end
end
end
end
end
end
end
module Gitlab
module QA
module Docker
class Engine
DOCKER_HOST = ENV['DOCKER_HOST'] || 'http://localhost'
def hostname
URI(DOCKER_HOST).host
end
def pull(image, tag)
Docker::Command.execute("pull #{image}:#{tag}")
end
def run(image, tag, *args)
Docker::Command.new('run').tap do |command|
yield command
command << "#{image}:#{tag}"
command << args if args.any?
command.execute!
end
end
def attach(name, &block)
Docker::Command.execute("attach --sig-proxy=false #{name}", &block)
end
def stop(name)
Docker::Command.execute("stop #{name}")
end
def remove(name)
Docker::Command.execute("rm -f #{name}")
end
def network_exists?(name)
Docker::Command.execute("network inspect #{name}")
rescue Docker::Command::StatusError
false
else
true
end
def network_create(name)
Docker::Command.execute("network create #{name}")
end
end
end
end
end
require 'securerandom'
require 'net/http'
require 'uri'
module Gitlab
module QA
module Docker
class Gitlab
include Scenario::Actable
# rubocop:disable Style/Semicolon
attr_accessor :release, :image, :tag, :volumes, :network
attr_reader :name
def initialize
@docker = Docker::Engine.new
end
def name=(name)
@name = "#{name}-#{SecureRandom.hex(4)}"
end
def ee?
@release == :ee
end
def address
"http://#{hostname}"
end
def hostname
"#{@name}.#{@network}"
end
def instance
raise 'Please provide a block!' unless block_given?
prepare; start; reconfigure; wait
yield self
teardown
end
def prepare
@docker.pull(@image, @tag)
return if @docker.network_exists?(@network)
@docker.network_create(@network)
end
def start
unless [@name, @image, @tag, @network].all?
raise 'Please configure an instance first!'
end
@docker.run(@image, @tag) do |command|
command << "-d --name #{@name} -p 80:80"
command << "--net #{@network} --hostname #{hostname}"
@volumes.to_h.each do |to, from|
command << "--volume #{to}:#{from}:Z"
end
end
end
def reconfigure
@docker.attach(@name) do |line, wait|
# TODO, workaround which allows to detach from the container
#
Process.kill('INT', wait.pid) if line =~ /gitlab Reconfigured!/
end
end
def teardown
raise 'Invalid instance name!' unless @name
@docker.stop(@name)
@docker.remove(@name)
end
def wait
puts "GitLab URL: #{address}"
print 'Waiting for GitLab to become available '
if Availability.new("http://#{@docker.hostname}").check(180)
sleep 12 # TODO, handle that better
puts ' -> GitLab is available.'
else
abort ' -> GitLab unavailable!'
end
end
class Availability
def initialize(address)
@uri = URI.join(address, '/help')
end
def check(retries)
retries.times do
return true if service_available?
print '.'
sleep 1
end
false
end
private
def service_available?
response = Net::HTTP.start(@uri.host, @uri.port) do |http|
http.head2(@uri.request_uri)
end
response.code.to_i == 200
rescue Errno::ECONNREFUSED, Errno::ECONNRESET, EOFError
false
end
end
end
end
end
end
module Gitlab
module QA
module Docker
class Specs
include Scenario::Actable
IMAGE_NAME = 'gitlab/gitlab-qa'.freeze
attr_accessor :env
def initialize
@docker = Docker::Engine.new
end
# rubocop:disable Metrics/AbcSize
#
def test(gitlab)
tag = "#{gitlab.release}-#{gitlab.tag}"
args = ['Test::Instance', gitlab.address]
puts 'Running instance test scenarios for Gitlab ' \
"#{gitlab.release.upcase} at #{gitlab.address}"
@docker.run(IMAGE_NAME, tag, *args) do |command|
command << "-t --rm --net #{gitlab.network}"
command << %(-e #{env}="$#{env}") if env
command << "--name #{gitlab.name}-specs"
end
end
end
end
end
end
module Gitlab
module QA
module Scenario
module Actable
def act(*args, &block)
instance_exec(*args, &block)
end
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def perform
yield new if block_given?
end
def act(*args, &block)
new.act(*args, &block)
end
end
end
end
end
end
module Gitlab
module QA
module Scenario
class Template
def self.perform(*args)
new.tap do |scenario|
yield scenario if block_given?
return scenario.perform(*args)
end
end
def perform(*_args)
raise NotImplementedError
end
end
end
end
end
module Gitlab
module QA
module Scenario
module Test
module Gitlab
##
# Run test suite against any GitLab instance,
# including staging and on-premises installation.
#
class Any < Scenario::Template
def perform(address)
Spec::Image.perform do |specs|
specs.test(address)
end
end
end
end
end
end
end
end
module Gitlab
module QA
module Scenario
module Test
module Gitlab
class Image < Scenario::Template
attr_writer :tag, :volumes
def initialize
@tag = 'nightly'
@volumes = {}
end
# rubocop:disable Metrics/MethodLength
#
def perform(version)
unless %w(CE EE).include?(version)
raise 'Unknown GitLab release type specified!'
end
Docker::Gitlab.perform do |gitlab|
gitlab.release = version.downcase.to_sym
gitlab.name = "gitlab-qa-#{gitlab.release}"
gitlab.image = "gitlab/gitlab-#{gitlab.release}"
gitlab.tag = @tag
gitlab.volumes = @volumes
gitlab.network = 'test'
gitlab.instance do
Docker::Specs.perform do |instance|
instance.env = 'EE_LICENSE' if gitlab.ee?
instance.test(gitlab)
end
end
end
end
end
end
end
end
end
end
module Gitlab
module QA
module Scenario
module Test
module Omnibus
class Image < Scenario::Template
# rubocop:disable Style/Semicolon
def perform(version)
Docker::Gitlab.perform do |instance|
instance.name = "gitlab-qa-#{version.downcase}"
instance.image = "gitlab/gitlab-#{version.downcase}"
instance.tag = 'nightly'
instance.network = 'bridge'
instance.act { prepare; start; reconfigure; teardown }
end
end
end
end
end
end
end
end
require 'tmpdir'
require 'fileutils'
module Gitlab
module QA
module Scenario
module Test
module Omnibus
class Upgrade < Scenario::Template
VOLUMES = { 'config' => '/etc/gitlab',
'logs' => '/var/log/gitlab',
'data' => '/var/opt/gitlab' }.freeze
def perform(version)
with_temporary_volumes do |volumes|
Scenario::Test::Gitlab::Image.perform(version) do |scenario|
scenario.tag = 'latest'
scenario.volumes = volumes
end
Scenario::Test::Gitlab::Image.perform(version) do |scenario|
scenario.tag = 'nightly'
scenario.volumes = volumes
end
end
end
def with_temporary_volumes
Dir.mktmpdir('gitlab-qa-').tap do |dir|
yield Hash[VOLUMES.map { |k, v| ["#{dir}/#{k}", v] }]
end
end
end
end
end
end
end
end
module Gitlab
module QA
VERSION = '0.0.1'.freeze
end
end
require 'open3'
module QA
module Docker
class Command
class StatusError < StandardError; end
attr_reader :args
def self.execute(cmd, &block)
new(cmd).execute!(&block)
end
def initialize(cmd = nil)
@args = Array(cmd)
end
def <<(*args)
tap { @args.concat(args) }
end
def execute!(&block)
engine("docker #{@args.join(' ')}", &block)
end
private
def engine(cmd)
puts "Running shell command: `#{cmd}`"
Open3.popen2e(cmd) do |_in, out, wait|
out.each do |line|
puts line
yield line, wait if block_given?
end
if wait.value.exited? && wait.value.exitstatus.nonzero?
raise StatusError, "Docker command `#{cmd}` failed!"
end
end
end
end
end
end
module QA
module Docker
class Engine
DOCKER_HOST = ENV['DOCKER_HOST'] || 'http://localhost'
def hostname
URI(DOCKER_HOST).host
end
def pull(image, tag)
Docker::Command.execute("pull #{image}:#{tag}")
end
def run(image, tag, *args)
Docker::Command.new('run').tap do |command|
yield command
command << "#{image}:#{tag}"
command << args if args.any?
command.execute!
end
end
def attach(name, &block)
Docker::Command.execute("attach --sig-proxy=false #{name}", &block)
end
def stop(name)
Docker::Command.execute("stop #{name}")
end
def remove(name)
Docker::Command.execute("rm -f #{name}")
end
def network_exists?(name)
Docker::Command.execute("network inspect #{name}")
rescue Docker::Command::StatusError
false
else
true
end
def network_create(name)
Docker::Command.execute("network create #{name}")
end
end
end
end
require 'securerandom'
require 'net/http'
require 'uri'
module QA
module Docker
class Gitlab
include Scenario::Actable
# rubocop:disable Style/Semicolon
attr_accessor :release, :image, :tag, :volumes, :network
attr_reader :name
def initialize
@docker = Docker::Engine.new
end
def name=(name)
@name = "#{name}-#{SecureRandom.hex(4)}"
end
def ee?
@release == :ee
end
def address
"http://#{hostname}"
end
def hostname
"#{@name}.#{@network}"
end
def instance
raise 'Please provide a block!' unless block_given?
prepare; start; reconfigure; wait
yield self
teardown
end
def prepare
@docker.pull(@image, @tag)