Add support for docker image configuration in .gitlab-ci.yml
This commit is contained in:
parent
5478ff6dc2
commit
8c6e2bada2
|
@ -0,0 +1,31 @@
|
|||
module Gitlab
|
||||
module Ci
|
||||
class Config
|
||||
module Entry
|
||||
module DockerImage
|
||||
def hash?
|
||||
@config.is_a?(Hash)
|
||||
end
|
||||
|
||||
def string?
|
||||
@config.is_a?(String)
|
||||
end
|
||||
|
||||
def name
|
||||
value[:name]
|
||||
end
|
||||
|
||||
def entrypoint
|
||||
value[:entrypoint]
|
||||
end
|
||||
|
||||
def value
|
||||
return { name: @config } if string?
|
||||
return @config if hash?
|
||||
{}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,9 +7,16 @@ module Gitlab
|
|||
#
|
||||
class Image < Node
|
||||
include Validatable
|
||||
include DockerImage
|
||||
|
||||
ALLOWED_KEYS = %i[name entrypoint].freeze
|
||||
|
||||
validations do
|
||||
validates :config, type: String
|
||||
validates :config, hash_or_string: true
|
||||
validates :config, allowed_keys: ALLOWED_KEYS
|
||||
|
||||
validates :name, type: String, presence: true
|
||||
validates :entrypoint, type: String, allow_nil: true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
module Gitlab
|
||||
module Ci
|
||||
class Config
|
||||
module Entry
|
||||
##
|
||||
# Entry that represents a configuration of Docker service.
|
||||
#
|
||||
class Service < Node
|
||||
include Validatable
|
||||
include DockerImage
|
||||
|
||||
ALLOWED_KEYS = %i[name entrypoint command alias].freeze
|
||||
|
||||
validations do
|
||||
validates :config, hash_or_string: true
|
||||
validates :config, allowed_keys: ALLOWED_KEYS
|
||||
|
||||
validates :name, type: String, presence: true
|
||||
validates :entrypoint, type: String, allow_nil: true
|
||||
validates :command, type: String, allow_nil: true
|
||||
validates :alias, type: String, allow_nil: true
|
||||
end
|
||||
|
||||
def alias
|
||||
value[:alias]
|
||||
end
|
||||
|
||||
def command
|
||||
value[:command]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -9,7 +9,30 @@ module Gitlab
|
|||
include Validatable
|
||||
|
||||
validations do
|
||||
validates :config, array_of_strings: true
|
||||
validates :config, type: Array
|
||||
end
|
||||
|
||||
def compose!(deps = nil)
|
||||
super do
|
||||
@entries = []
|
||||
@config.each do |config|
|
||||
@entries << Entry::Factory.new(Entry::Service)
|
||||
.value(config || {})
|
||||
.create!
|
||||
end
|
||||
|
||||
@entries.each do |entry|
|
||||
entry.compose!(deps)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def value
|
||||
@entries.map(&:value)
|
||||
end
|
||||
|
||||
def descendants
|
||||
@entries
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,6 +44,14 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
class HashOrStringValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
unless value.is_a?(Hash) || value.is_a?(String)
|
||||
record.errors.add(attribute, 'should be a hash or a string')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class KeyValidator < ActiveModel::EachValidator
|
||||
include LegacyValidationHelpers
|
||||
|
||||
|
|
|
@ -598,8 +598,8 @@ module Ci
|
|||
describe "Image and service handling" do
|
||||
it "returns image and service when defined" do
|
||||
config = YAML.dump({
|
||||
image: "ruby:2.1",
|
||||
services: ["mysql"],
|
||||
image: { name: "ruby:2.1" },
|
||||
services: ["mysql", { name: "docker:dind", alias: "docker" }],
|
||||
before_script: ["pwd"],
|
||||
rspec: { script: "rspec" }
|
||||
})
|
||||
|
@ -615,8 +615,8 @@ module Ci
|
|||
coverage_regex: nil,
|
||||
tag_list: [],
|
||||
options: {
|
||||
image: "ruby:2.1",
|
||||
services: ["mysql"]
|
||||
image: { name: "ruby:2.1" },
|
||||
services: [{ name: "mysql" }, { name: "docker:dind", alias: "docker" }]
|
||||
},
|
||||
allow_failure: false,
|
||||
when: "on_success",
|
||||
|
@ -630,7 +630,7 @@ module Ci
|
|||
image: "ruby:2.1",
|
||||
services: ["mysql"],
|
||||
before_script: ["pwd"],
|
||||
rspec: { image: "ruby:2.5", services: ["postgresql"], script: "rspec" }
|
||||
rspec: { image: "ruby:2.5", services: [{ name: "postgresql" }, "docker:dind"], script: "rspec" }
|
||||
})
|
||||
|
||||
config_processor = GitlabCiYamlProcessor.new(config, path)
|
||||
|
@ -644,8 +644,8 @@ module Ci
|
|||
coverage_regex: nil,
|
||||
tag_list: [],
|
||||
options: {
|
||||
image: "ruby:2.5",
|
||||
services: ["postgresql"]
|
||||
image: { name: "ruby:2.5" },
|
||||
services: [{ name: "postgresql" }, { name: "docker:dind" }]
|
||||
},
|
||||
allow_failure: false,
|
||||
when: "on_success",
|
||||
|
@ -884,8 +884,8 @@ module Ci
|
|||
coverage_regex: nil,
|
||||
tag_list: [],
|
||||
options: {
|
||||
image: "ruby:2.1",
|
||||
services: ["mysql"],
|
||||
image: { name: "ruby:2.1" },
|
||||
services: [{ name: "mysql" }],
|
||||
artifacts: {
|
||||
name: "custom_name",
|
||||
paths: ["logs/", "binaries/"],
|
||||
|
@ -1261,7 +1261,7 @@ EOT
|
|||
config = YAML.dump({ image: ["test"], rspec: { script: "test" } })
|
||||
expect do
|
||||
GitlabCiYamlProcessor.new(config, path)
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image config should be a string")
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "image config should be a hash or a string")
|
||||
end
|
||||
|
||||
it "returns errors if job name is blank" do
|
||||
|
@ -1282,35 +1282,35 @@ EOT
|
|||
config = YAML.dump({ rspec: { script: "test", image: ["test"] } })
|
||||
expect do
|
||||
GitlabCiYamlProcessor.new(config, path)
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:image config should be a string")
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:image config should be a hash or a string")
|
||||
end
|
||||
|
||||
it "returns errors if services parameter is not an array" do
|
||||
config = YAML.dump({ services: "test", rspec: { script: "test" } })
|
||||
expect do
|
||||
GitlabCiYamlProcessor.new(config, path)
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings")
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be a array")
|
||||
end
|
||||
|
||||
it "returns errors if services parameter is not an array of strings" do
|
||||
config = YAML.dump({ services: [10, "test"], rspec: { script: "test" } })
|
||||
expect do
|
||||
GitlabCiYamlProcessor.new(config, path)
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "services config should be an array of strings")
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "service config should be a hash or a string")
|
||||
end
|
||||
|
||||
it "returns errors if job services parameter is not an array" do
|
||||
config = YAML.dump({ rspec: { script: "test", services: "test" } })
|
||||
expect do
|
||||
GitlabCiYamlProcessor.new(config, path)
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings")
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be a array")
|
||||
end
|
||||
|
||||
it "returns errors if job services parameter is not an array of strings" do
|
||||
config = YAML.dump({ rspec: { script: "test", services: [10, "test"] } })
|
||||
expect do
|
||||
GitlabCiYamlProcessor.new(config, path)
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:rspec:services config should be an array of strings")
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "service config should be a hash or a string")
|
||||
end
|
||||
|
||||
it "returns error if job configuration is invalid" do
|
||||
|
@ -1324,7 +1324,7 @@ EOT
|
|||
config = YAML.dump({ extra: { script: 'rspec', services: "test" } })
|
||||
expect do
|
||||
GitlabCiYamlProcessor.new(config, path)
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra:services config should be an array of strings")
|
||||
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "jobs:extra:services config should be a array")
|
||||
end
|
||||
|
||||
it "returns errors if there are no jobs defined" do
|
||||
|
|
|
@ -95,13 +95,13 @@ describe Gitlab::Ci::Config::Entry::Global do
|
|||
|
||||
describe '#image_value' do
|
||||
it 'returns valid image' do
|
||||
expect(global.image_value).to eq 'ruby:2.2'
|
||||
expect(global.image_value).to eq(name: 'ruby:2.2')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#services_value' do
|
||||
it 'returns array of services' do
|
||||
expect(global.services_value).to eq ['postgres:9.1', 'mysql:5.5']
|
||||
expect(global.services_value).to eq [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -150,8 +150,8 @@ describe Gitlab::Ci::Config::Entry::Global do
|
|||
script: %w[rspec ls],
|
||||
before_script: %w(ls pwd),
|
||||
commands: "ls\npwd\nrspec\nls",
|
||||
image: 'ruby:2.2',
|
||||
services: ['postgres:9.1', 'mysql:5.5'],
|
||||
image: { name: 'ruby:2.2' },
|
||||
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
|
||||
stage: 'test',
|
||||
cache: { key: 'k', untracked: true, paths: ['public/'] },
|
||||
variables: { 'VAR' => 'value' },
|
||||
|
@ -161,8 +161,8 @@ describe Gitlab::Ci::Config::Entry::Global do
|
|||
before_script: [],
|
||||
script: %w[spinach],
|
||||
commands: 'spinach',
|
||||
image: 'ruby:2.2',
|
||||
services: ['postgres:9.1', 'mysql:5.5'],
|
||||
image: { name: 'ruby:2.2' },
|
||||
services: [{ name: 'postgres:9.1' }, { name: 'mysql:5.5' }],
|
||||
stage: 'test',
|
||||
cache: { key: 'k', untracked: true, paths: ['public/'] },
|
||||
variables: {},
|
||||
|
|
|
@ -3,43 +3,104 @@ require 'spec_helper'
|
|||
describe Gitlab::Ci::Config::Entry::Image do
|
||||
let(:entry) { described_class.new(config) }
|
||||
|
||||
describe 'validation' do
|
||||
context 'when entry config value is correct' do
|
||||
let(:config) { 'ruby:2.2' }
|
||||
context 'when configuration is a string' do
|
||||
let(:config) { 'ruby:2.2' }
|
||||
|
||||
describe '#value' do
|
||||
it 'returns image string' do
|
||||
expect(entry.value).to eq 'ruby:2.2'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#errors' do
|
||||
it 'does not append errors' do
|
||||
expect(entry.errors).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is valid' do
|
||||
expect(entry).to be_valid
|
||||
end
|
||||
describe '#value' do
|
||||
it 'returns image hash' do
|
||||
expect(entry.value).to eq({ name: 'ruby:2.2' })
|
||||
end
|
||||
end
|
||||
|
||||
context 'when entry value is not correct' do
|
||||
let(:config) { ['ruby:2.2'] }
|
||||
|
||||
describe '#errors' do
|
||||
it 'saves errors' do
|
||||
expect(entry.errors)
|
||||
.to include 'image config should be a string'
|
||||
end
|
||||
describe '#errors' do
|
||||
it 'does not append errors' do
|
||||
expect(entry.errors).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is not valid' do
|
||||
expect(entry).not_to be_valid
|
||||
end
|
||||
describe '#valid?' do
|
||||
it 'is valid' do
|
||||
expect(entry).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe '#image' do
|
||||
it "returns image's name" do
|
||||
expect(entry.name).to eq 'ruby:2.2'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#entrypoint' do
|
||||
it "returns image's entrypoint" do
|
||||
expect(entry.entrypoint).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when configuration is a hash' do
|
||||
let(:config) { { name: 'ruby:2.2', entrypoint: '/bin/sh' } }
|
||||
|
||||
describe '#value' do
|
||||
it 'returns image hash' do
|
||||
expect(entry.value).to eq(config)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#errors' do
|
||||
it 'does not append errors' do
|
||||
expect(entry.errors).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is valid' do
|
||||
expect(entry).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe '#image' do
|
||||
it "returns image's name" do
|
||||
expect(entry.name).to eq 'ruby:2.2'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#entrypoint' do
|
||||
it "returns image's entrypoint" do
|
||||
expect(entry.entrypoint).to eq '/bin/sh'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when entry value is not correct' do
|
||||
let(:config) { ['ruby:2.2'] }
|
||||
|
||||
describe '#errors' do
|
||||
it 'saves errors' do
|
||||
expect(entry.errors)
|
||||
.to include 'image config should be a hash or a string'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is not valid' do
|
||||
expect(entry).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when unexpected key is specified' do
|
||||
let(:config) { { name: 'ruby:2.2', non_existing: 'test' } }
|
||||
|
||||
describe '#errors' do
|
||||
it 'saves errors' do
|
||||
expect(entry.errors)
|
||||
.to include 'image config contains unknown keys: non_existing'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is not valid' do
|
||||
expect(entry).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -104,7 +104,7 @@ describe Gitlab::Ci::Config::Entry::Job do
|
|||
end
|
||||
|
||||
it 'overrides global config' do
|
||||
expect(entry[:image].value).to eq 'some_image'
|
||||
expect(entry[:image].value).to eq(name: 'some_image')
|
||||
expect(entry[:cache].value).to eq(key: 'test')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Gitlab::Ci::Config::Entry::Service do
|
||||
let(:entry) { described_class.new(config) }
|
||||
|
||||
before { entry.compose! }
|
||||
|
||||
context 'when configuration is a string' do
|
||||
let(:config) { 'postgresql:9.5' }
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is valid' do
|
||||
expect(entry).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe '#value' do
|
||||
it 'returns valid hash' do
|
||||
expect(entry.value).to include(name: 'postgresql:9.5')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#image' do
|
||||
it "returns service's image name" do
|
||||
expect(entry.name).to eq 'postgresql:9.5'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#alias' do
|
||||
it "returns service's alias" do
|
||||
expect(entry.alias).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#command' do
|
||||
it "returns service's command" do
|
||||
expect(entry.command).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when configuration is a hash' do
|
||||
let(:config) do
|
||||
{ name: 'postgresql:9.5', alias: 'db', command: 'cmd', entrypoint: '/bin/sh' }
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is valid' do
|
||||
expect(entry).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe '#value' do
|
||||
it 'returns valid hash' do
|
||||
expect(entry.value).to eq config
|
||||
end
|
||||
end
|
||||
|
||||
describe '#image' do
|
||||
it "returns service's image name" do
|
||||
expect(entry.name).to eq 'postgresql:9.5'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#alias' do
|
||||
it "returns service's alias" do
|
||||
expect(entry.alias).to eq 'db'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#command' do
|
||||
it "returns service's command" do
|
||||
expect(entry.command).to eq 'cmd'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#entrypoint' do
|
||||
it "returns service's entrypoint" do
|
||||
expect(entry.entrypoint).to eq '/bin/sh'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when entry value is not correct' do
|
||||
let(:config) { ['postgresql:9.5'] }
|
||||
|
||||
describe '#errors' do
|
||||
it 'saves errors' do
|
||||
expect(entry.errors)
|
||||
.to include 'service config should be a hash or a string'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is not valid' do
|
||||
expect(entry).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when unexpected key is specified' do
|
||||
let(:config) { { name: 'postgresql:9.5', non_existing: 'test' } }
|
||||
|
||||
describe '#errors' do
|
||||
it 'saves errors' do
|
||||
expect(entry.errors)
|
||||
.to include 'service config contains unknown keys: non_existing'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is not valid' do
|
||||
expect(entry).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,37 +3,30 @@ require 'spec_helper'
|
|||
describe Gitlab::Ci::Config::Entry::Services do
|
||||
let(:entry) { described_class.new(config) }
|
||||
|
||||
describe 'validations' do
|
||||
context 'when entry config value is correct' do
|
||||
let(:config) { ['postgres:9.1', 'mysql:5.5'] }
|
||||
before { entry.compose! }
|
||||
|
||||
describe '#value' do
|
||||
it 'returns array of services as is' do
|
||||
expect(entry.value).to eq config
|
||||
end
|
||||
end
|
||||
context 'when configuration is valid' do
|
||||
let(:config) { ['postgresql:9.5', { name: 'postgresql:9.1', alias: 'postgres_old' }] }
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is valid' do
|
||||
expect(entry).to be_valid
|
||||
end
|
||||
describe '#valid?' do
|
||||
it 'is valid' do
|
||||
expect(entry).to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context 'when entry value is not correct' do
|
||||
let(:config) { 'ls' }
|
||||
|
||||
describe '#errors' do
|
||||
it 'saves errors' do
|
||||
expect(entry.errors)
|
||||
.to include 'services config should be an array of strings'
|
||||
end
|
||||
describe '#value' do
|
||||
it 'returns valid array' do
|
||||
expect(entry.value).to eq([{ name: 'postgresql:9.5' }, { name: 'postgresql:9.1', alias: 'postgres_old' }])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is not valid' do
|
||||
expect(entry).not_to be_valid
|
||||
end
|
||||
context 'when configuration is invalid' do
|
||||
let(:config) { 'postgresql:9.5' }
|
||||
|
||||
describe '#valid?' do
|
||||
it 'is invalid' do
|
||||
expect(entry).not_to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue