Assume that unspecified CI config is undefined

We assume that when someone adds a key for the configuration entry, but
does not provide a valid value, which causes entry to be `nil`, then
entry should be considered as the undefined one. We also assume this is
semantically correct, this is also backwards compatible with legacy CI
config processor.

See issue #18775 for more details.
This commit is contained in:
Grzegorz Bizon 2016-06-23 10:07:42 +02:00
parent bc2348f2e4
commit 2240807c1a
8 changed files with 128 additions and 110 deletions

View file

@ -27,7 +27,6 @@ module Gitlab
def create_node(key, factory)
factory.with(value: @config[key], key: key)
factory.undefine! unless @config.has_key?(key)
factory.create!
end

View file

@ -18,16 +18,20 @@ module Gitlab
self
end
def undefine!
@attributes[:value] = @node.dup
@node = Node::Undefined
self
end
def create!
raise InvalidFactory unless @attributes.has_key?(:value)
@node.new(@attributes[:value]).tap do |entry|
##
# We assume unspecified entry is undefined.
# See issue #18775.
#
if @attributes[:value].nil?
node, value = Node::Undefined, @node
else
node, value = @node, @attributes[:value]
end
node.new(value).tap do |entry|
entry.description = @attributes[:description]
entry.key = @attributes[:key]
end

View file

@ -9,11 +9,7 @@ module Gitlab
include Validatable
validations do
validates :value, variables: true
end
def value
@config || self.class.default
validates :config, variables: true
end
def self.default

View file

@ -551,8 +551,8 @@ module Ci
config_processor = GitlabCiYamlProcessor.new(config, path)
##
# TODO, in next version of CI configuration processor this
# should be invalid configuration, see #18775 and #15060
# When variables config is empty, we asumme this is a correct,
# see issue #18775
#
expect(config_processor.job_variables(:rspec))
.to be_an_instance_of(Array).and be_empty
@ -1098,14 +1098,14 @@ EOT
config = YAML.dump({ variables: "test", rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Variables value should be a hash of key value pairs")
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Variables config should be a hash of key value pairs")
end
it "returns errors if variables is not a map of key-value strings" do
config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } })
expect do
GitlabCiYamlProcessor.new(config, path)
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Variables value should be a hash of key value pairs")
end.to raise_error(GitlabCiYamlProcessor::ValidationError, "Variables config should be a hash of key value pairs")
end
it "returns errors if job when is not on_success, on_failure or always" do

View file

@ -45,11 +45,10 @@ describe Gitlab::Ci::Config::Node::Factory do
end
end
context 'when creating undefined entry' do
it 'creates a null entry' do
context 'when creating entry with nil value' do
it 'creates an undefined entry' do
entry = factory
.with(value: nil)
.undefine!
.create!
expect(entry).to be_an_instance_of Gitlab::Ci::Config::Node::Undefined

View file

@ -20,84 +20,125 @@ describe Gitlab::Ci::Config::Node::Global do
end
context 'when hash is valid' do
let(:hash) do
{ before_script: ['ls', 'pwd'],
image: 'ruby:2.2',
services: ['postgres:9.1', 'mysql:5.5'],
variables: { VAR: 'value' },
after_script: ['make clean'] }
context 'when all entries defined' do
let(:hash) do
{ before_script: ['ls', 'pwd'],
image: 'ruby:2.2',
services: ['postgres:9.1', 'mysql:5.5'],
variables: { VAR: 'value' },
after_script: ['make clean'] }
end
describe '#process!' do
before { global.process! }
it 'creates nodes hash' do
expect(global.nodes).to be_an Array
end
it 'creates node object for each entry' do
expect(global.nodes.count).to eq 5
end
it 'creates node object using valid class' do
expect(global.nodes.first)
.to be_an_instance_of Gitlab::Ci::Config::Node::Script
expect(global.nodes.second)
.to be_an_instance_of Gitlab::Ci::Config::Node::Image
end
it 'sets correct description for nodes' do
expect(global.nodes.first.description)
.to eq 'Script that will be executed before each job.'
expect(global.nodes.second.description)
.to eq 'Docker image that will be used to execute jobs.'
end
end
describe '#leaf?' do
it 'is not leaf' do
expect(global).not_to be_leaf
end
end
context 'when not processed' do
describe '#before_script' do
it 'returns nil' do
expect(global.before_script).to be nil
end
end
end
context 'when processed' do
before { global.process! }
describe '#before_script' do
it 'returns correct script' do
expect(global.before_script).to eq ['ls', 'pwd']
end
end
describe '#image' do
it 'returns valid image' do
expect(global.image).to eq 'ruby:2.2'
end
end
describe '#services' do
it 'returns array of services' do
expect(global.services).to eq ['postgres:9.1', 'mysql:5.5']
end
end
describe '#after_script' do
it 'returns after script' do
expect(global.after_script).to eq ['make clean']
end
end
describe '#variables' do
it 'returns variables' do
expect(global.variables).to eq(VAR: 'value')
end
end
end
end
describe '#process!' do
context 'when most of entires not defined' do
let(:hash) { { rspec: {} } }
before { global.process! }
it 'creates nodes hash' do
expect(global.nodes).to be_an Array
end
it 'creates node object for each entry' do
expect(global.nodes.count).to eq 5
end
it 'creates node object using valid class' do
expect(global.nodes.first)
.to be_an_instance_of Gitlab::Ci::Config::Node::Script
expect(global.nodes.second)
.to be_an_instance_of Gitlab::Ci::Config::Node::Image
end
it 'sets correct description for nodes' do
expect(global.nodes.first.description)
.to eq 'Script that will be executed before each job.'
expect(global.nodes.second.description)
.to eq 'Docker image that will be used to execute jobs.'
end
end
describe '#leaf?' do
it 'is not leaf' do
expect(global).not_to be_leaf
end
end
context 'when not processed' do
describe '#before_script' do
it 'returns nil' do
expect(global.before_script).to be nil
describe '#nodes' do
it 'instantizes all nodes' do
expect(global.nodes.count).to eq 5
end
end
end
context 'when processed' do
before { global.process! }
describe '#before_script' do
it 'returns correct script' do
expect(global.before_script).to eq ['ls', 'pwd']
end
end
describe '#image' do
it 'returns valid image' do
expect(global.image).to eq 'ruby:2.2'
end
end
describe '#services' do
it 'returns array of services' do
expect(global.services).to eq ['postgres:9.1', 'mysql:5.5']
end
end
describe '#after_script' do
it 'returns after script' do
expect(global.after_script).to eq ['make clean']
it 'contains undefined nodes' do
expect(global.nodes.last)
.to be_an_instance_of Gitlab::Ci::Config::Node::Undefined
end
end
describe '#variables' do
it 'returns variables' do
expect(global.variables).to eq(VAR: 'value')
it 'returns default value for variables' do
expect(global.variables).to eq({})
end
end
end
##
# When nodes are specified but not defined, we assume that
# configuration is valid, and we asume that entry is simply undefined,
# despite the fact, that key is present. See issue #18775 for more
# details.
#
context 'when entires specified but not defined' do
let(:hash) { { variables: nil } }
before { global.process! }
describe '#variables' do
it 'undefined entry returns a default value' do
expect(global.variables).to eq({})
end
end
end

View file

@ -3,9 +3,7 @@ require 'spec_helper'
describe Gitlab::Ci::Config::Node::Services do
let(:entry) { described_class.new(config) }
describe '#process!' do
before { entry.process! }
describe 'validations' do
context 'when entry config value is correct' do
let(:config) { ['postgres:9.1', 'mysql:5.5'] }

View file

@ -44,24 +44,5 @@ describe Gitlab::Ci::Config::Node::Variables do
end
end
end
##
# See #18775
#
context 'when entry value is not defined' do
let(:config) { nil }
describe '#valid?' do
it 'is valid' do
expect(entry).to be_valid
end
end
describe '#values' do
it 'returns an empty hash' do
expect(entry.value).to eq({})
end
end
end
end
end