summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Maczukin <tomasz@maczukin.pl>2017-01-14 14:39:53 -0500
committerTomasz Maczukin <tomasz@maczukin.pl>2017-06-12 12:58:35 +0200
commit8c6e2bada222745c9994da42f21bad2ab43b9351 (patch)
treeafd67edfeda2c7d81b51379f85b0e12dfb22e0cc
parent5478ff6dc2ad59a7c7d91f61339e8acee9bbb434 (diff)
downloadgitlab-ce-8c6e2bada222745c9994da42f21bad2ab43b9351.tar.gz
Add support for docker image configuration in .gitlab-ci.yml
-rw-r--r--lib/gitlab/ci/config/entry/docker_image.rb31
-rw-r--r--lib/gitlab/ci/config/entry/image.rb9
-rw-r--r--lib/gitlab/ci/config/entry/service.rb35
-rw-r--r--lib/gitlab/ci/config/entry/services.rb25
-rw-r--r--lib/gitlab/ci/config/entry/validators.rb8
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb32
-rw-r--r--spec/lib/gitlab/ci/config/entry/global_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/config/entry/image_spec.rb113
-rw-r--r--spec/lib/gitlab/ci/config/entry/job_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/config/entry/service_spec.rb117
-rw-r--r--spec/lib/gitlab/ci/config/entry/services_spec.rb41
11 files changed, 350 insertions, 75 deletions
diff --git a/lib/gitlab/ci/config/entry/docker_image.rb b/lib/gitlab/ci/config/entry/docker_image.rb
new file mode 100644
index 00000000000..1170e129bc0
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/docker_image.rb
@@ -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
diff --git a/lib/gitlab/ci/config/entry/image.rb b/lib/gitlab/ci/config/entry/image.rb
index b5050257688..10f35a2dbc1 100644
--- a/lib/gitlab/ci/config/entry/image.rb
+++ b/lib/gitlab/ci/config/entry/image.rb
@@ -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
diff --git a/lib/gitlab/ci/config/entry/service.rb b/lib/gitlab/ci/config/entry/service.rb
new file mode 100644
index 00000000000..fd0aec23803
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/service.rb
@@ -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
diff --git a/lib/gitlab/ci/config/entry/services.rb b/lib/gitlab/ci/config/entry/services.rb
index 84f8ab780f5..0066894e069 100644
--- a/lib/gitlab/ci/config/entry/services.rb
+++ b/lib/gitlab/ci/config/entry/services.rb
@@ -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
diff --git a/lib/gitlab/ci/config/entry/validators.rb b/lib/gitlab/ci/config/entry/validators.rb
index bd7428b1272..b2ca3c881e4 100644
--- a/lib/gitlab/ci/config/entry/validators.rb
+++ b/lib/gitlab/ci/config/entry/validators.rb
@@ -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
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 2ca0773ad1d..2c2c394ba46 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb
index 23270ad5053..e5f85c712ca 100644
--- a/spec/lib/gitlab/ci/config/entry/global_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb
@@ -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: {},
diff --git a/spec/lib/gitlab/ci/config/entry/image_spec.rb b/spec/lib/gitlab/ci/config/entry/image_spec.rb
index 3c99cb0a1ee..bca22e39500 100644
--- a/spec/lib/gitlab/ci/config/entry/image_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/image_spec.rb
@@ -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
+ describe '#value' do
+ it 'returns image hash' do
+ expect(entry.value).to eq({ name: '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
+ end
+
+ describe '#image' do
+ it "returns image's name" do
+ expect(entry.name).to eq 'ruby:2.2'
+ end
+ end
- describe '#errors' do
- it 'does not append errors' do
- expect(entry.errors).to be_empty
- end
+ describe '#entrypoint' do
+ it "returns image's entrypoint" do
+ expect(entry.entrypoint).to be_nil
end
+ end
+ end
- describe '#valid?' do
- it 'is valid' do
- expect(entry).to be_valid
- 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
- context 'when entry value is not correct' do
- let(:config) { ['ruby:2.2'] }
+ 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 '#errors' do
- it 'saves errors' do
- expect(entry.errors)
- .to include 'image config should be a string'
- 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
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
end
end
end
diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb
index 9249bb9c172..8dc94a4eb33 100644
--- a/spec/lib/gitlab/ci/config/entry/job_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/ci/config/entry/service_spec.rb b/spec/lib/gitlab/ci/config/entry/service_spec.rb
new file mode 100644
index 00000000000..2376de74554
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/entry/service_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/ci/config/entry/services_spec.rb b/spec/lib/gitlab/ci/config/entry/services_spec.rb
index 66fad3b6b16..b32e52f8f26 100644
--- a/spec/lib/gitlab/ci/config/entry/services_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/services_spec.rb
@@ -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
+
+ context 'when configuration is invalid' do
+ let(:config) { 'postgresql:9.5' }
- describe '#valid?' do
- it 'is not valid' do
- expect(entry).not_to be_valid
- end
+ describe '#valid?' do
+ it 'is invalid' do
+ expect(entry).not_to be_valid
end
end
end