Commit b00c3f71 authored by Matija Čupić's avatar Matija Čupić

Move Needs entry to core

Moves the Needs entry to core, because it's required for use in DAG
needs.
parent 977ea357
...@@ -41,7 +41,7 @@ module EE ...@@ -41,7 +41,7 @@ module EE
description: 'CI/CD Bridge downstream trigger definition.', description: 'CI/CD Bridge downstream trigger definition.',
inherit: false inherit: false
entry :needs, ::EE::Gitlab::Ci::Config::Entry::Needs, entry :needs, ::Gitlab::Ci::Config::Entry::Needs,
description: 'CI/CD Bridge needs dependency definition.', description: 'CI/CD Bridge needs dependency definition.',
inherit: false inherit: false
......
...@@ -5,14 +5,14 @@ module EE ...@@ -5,14 +5,14 @@ module EE
module Ci module Ci
module Config module Config
module Entry module Entry
## module Need
# Entry that represents a cross-project needs dependency. extend ActiveSupport::Concern
#
class Needs < ::Gitlab::Config::Entry::Simplifiable
strategy :BridgeNeeds, if: -> (config) { config.is_a?(Hash) }
strategy :ComplexNeeds, if: -> (config) { config.is_a?(Array) }
class BridgeNeeds < ::Gitlab::Config::Entry::Node prepended do
strategy :Bridge, if: -> (config) { config.is_a?(Hash) }
end
class Bridge < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable include ::Gitlab::Config::Entry::Validatable
include ::Gitlab::Config::Entry::Attributable include ::Gitlab::Config::Entry::Attributable
...@@ -24,43 +24,22 @@ module EE ...@@ -24,43 +24,22 @@ module EE
validates :config, allowed_keys: ALLOWED_KEYS validates :config, allowed_keys: ALLOWED_KEYS
validates :pipeline, type: String, presence: true validates :pipeline, type: String, presence: true
end end
end
class ComplexNeeds < ::Gitlab::Config::Entry::Node def bridge?
include ::Gitlab::Config::Entry::Validatable true
validations do
validates :config, presence: true
validates :config, type: Array
validate :one_needs_pipeline
validate :needs_array_elements
end end
def one_needs_pipeline def pipeline?
if config.count { |element| element.is_a?(Hash) } > 1 false
errors.add(:needs, 'needs hash element needs to have a pipeline key')
end
end
def needs_array_elements
config.each do |element|
next if element.is_a?(String)
unless element.is_a?(Hash) && element[:pipeline]
errors.add(:needs, 'needs hash element needs to have a pipeline key')
end
end
end
def value
bridge, pipeline = config.partition { |element| element.is_a?(Hash) }
{ bridge: bridge.first, pipeline: pipeline }
end end
end end
class UnknownStrategy < ::Gitlab::Config::Entry::Node module UnknownStrategy
extend ::Gitlab::Utils::Override
override :errors
def errors def errors
["#{location} has to be either an array of conditions or a hash"] ["#{location} has to be a string, symbol or hash"]
end end
end end
end end
......
...@@ -88,7 +88,7 @@ describe EE::Gitlab::Ci::Config::Entry::Bridge do ...@@ -88,7 +88,7 @@ describe EE::Gitlab::Ci::Config::Entry::Bridge do
describe '#value' do describe '#value' do
it 'is returns a bridge job configuration' do it 'is returns a bridge job configuration' do
expect(subject.value).to eq(name: :my_bridge, expect(subject.value).to eq(name: :my_bridge,
needs: { pipeline: 'some/project' }, needs: { bridge: { pipeline: 'some/project' } },
ignore: false, ignore: false,
stage: 'test', stage: 'test',
only: { refs: %w[branches tags] }) only: { refs: %w[branches tags] })
......
# frozen_string_literal: true
require 'spec_helper'
describe ::Gitlab::Ci::Config::Entry::Need do
subject { described_class.new(config) }
context 'when upstream is specified' do
let(:config) { { pipeline: 'some/project' } }
describe '#valid?' do
it { is_expected.to be_valid }
end
describe '#value' do
it 'returns job needs configuration' do
expect(subject.value).to eq(pipeline: 'some/project')
end
end
end
context 'when need is empty' do
let(:config) { {} }
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
it 'is returns an error about an empty config' do
expect(subject.errors.first)
.to end_with("bridge config can't be blank")
end
end
end
context 'when need is the wrong type' do
let(:config) { 123 }
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
it 'is returns an error about an empty config' do
expect(subject.errors.first)
.to end_with('has to be a string, symbol or hash')
end
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
require 'fast_spec_helper' require 'spec_helper'
require_dependency 'active_model'
describe EE::Gitlab::Ci::Config::Entry::Needs do describe ::Gitlab::Ci::Config::Entry::Needs do
subject { described_class.new(config) } subject(:needs) { described_class.new(config) }
context 'when needs is a bridge needs' do describe 'validations' do
context 'when upstream config is a non-empty string' do before do
let(:config) { { pipeline: 'some/project' } } needs.compose!
end
context 'when entry config value is correct' do
let(:config) { ['job_name', pipeline: 'some/project'] }
describe '#valid?' do describe '#valid?' do
it { is_expected.to be_valid } it { is_expected.to be_valid }
end end
describe '#value' do
it 'is returns a upstream configuration hash' do
expect(subject.value).to eq(pipeline: 'some/project')
end
end
end end
context 'when upstream config is not a string' do context 'when wrong needs type is used' do
let(:config) { { pipeline: 123 } } let(:config) { ['job_name', { pipeline: 'some/project' }, 123] }
describe '#valid?' do describe '#valid?' do
it { is_expected.not_to be_valid } it { is_expected.not_to be_valid }
end end
describe '#errors' do describe '#errors' do
it 'returns an error message' do it 'returns error about incorrect type' do
expect(subject.errors.first) error_message = Gitlab.ee? ? 'need has to be a string, symbol or hash' : 'need has to be a string or symbol'
.to eq('bridge needs pipeline should be a string')
expect(needs.errors)
.to include error_message
end end
end end
end end
end
context 'when needs is a complex needs' do context 'when bridge needs has wrong attributes' do
let(:config) { ['test', { pipeline: 'test' }] } let(:config) { ['job_name', project: 'some/project'] }
it 'test' do describe '#valid?' do
subject it { is_expected.not_to be_valid }
end
end end
end end
context 'when needs is empty' do describe '.compose!' do
let(:config) { '' } context 'when valid job entries composed' do
let(:config) { ['first_job_name', pipeline: 'some/project'] }
describe '#valid?' do before do
it { is_expected.not_to be_valid } needs.compose!
end end
describe '#errors' do describe '#value' do
it 'is returns an error about an empty config' do it 'returns key value' do
expect(subject.errors.first) expect(needs.value).to eq(pipeline: ['first_job_name'], bridge: { pipeline: 'some/project' })
.to end_with('has to be either an array of conditions or a hash') end
end
describe '#descendants' do
it 'creates valid descendant nodes' do
expect(needs.descendants.count).to eq 2
expect(needs.descendants)
.to all(be_an_instance_of(::Gitlab::Ci::Config::Entry::Need))
end
end end
end end
end end
......
...@@ -50,7 +50,6 @@ module Gitlab ...@@ -50,7 +50,6 @@ module Gitlab
validates :timeout, duration: { limit: ChronicDuration.output(Project::MAX_BUILD_TIMEOUT) } validates :timeout, duration: { limit: ChronicDuration.output(Project::MAX_BUILD_TIMEOUT) }
validates :dependencies, array_of_strings: true validates :dependencies, array_of_strings: true
validates :needs, array_of_strings: true
validates :extends, array_of_strings_or_string: true validates :extends, array_of_strings_or_string: true
validates :rules, array_of_hashes: true validates :rules, array_of_hashes: true
end end
...@@ -114,6 +113,9 @@ module Gitlab ...@@ -114,6 +113,9 @@ module Gitlab
description: 'List of evaluable Rules to determine job inclusion.', description: 'List of evaluable Rules to determine job inclusion.',
inherit: false inherit: false
entry :needs, Entry::Needs,
description: 'Needs configuration for this job.'
entry :variables, Entry::Variables, entry :variables, Entry::Variables,
description: 'Environment variables available for this job.', description: 'Environment variables available for this job.',
inherit: false inherit: false
......
# frozen_string_literal: true
module Gitlab
module Ci
class Config
module Entry
class Need < ::Gitlab::Config::Entry::Simplifiable
strategy :Pipeline, if: -> (config) { config.is_a?(String) || config.is_a?(Symbol) }
class Pipeline < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
validations do
validates :config, presence: true
end
def bridge?
false
end
def pipeline?
true
end
def value
@config.to_s
end
end
class UnknownStrategy < ::Gitlab::Config::Entry::Node
def errors
["#{location} has to be a string or symbol"]
end
end
end
end
end
end
end
::Gitlab::Ci::Config::Entry::Need.prepend_if_ee('::EE::Gitlab::Ci::Config::Entry::Need') # rubocop: disable Cop/InjectEnterpriseEditionModule
::Gitlab::Ci::Config::Entry::Need::UnknownStrategy.prepend_if_ee('::EE::Gitlab::Ci::Config::Entry::Need::UnknownStrategy')
# frozen_string_literal: true
module Gitlab
module Ci
class Config
module Entry
##
# Entry that represents a set of needs dependencies.
#
class Needs < ::Gitlab::Config::Entry::Node
include ::Gitlab::Config::Entry::Validatable
validations do
validates :config, presence: true
end
def compose!(deps = nil)
super(deps) do
[].tap { |array| array.push(@config) }.flatten.each_with_index do |need, index|
@entries[index] = ::Gitlab::Config::Entry::Factory.new(Entry::Need)
.value(need)
.with(key: "need", parent: self, description: "need definition.") # rubocop:disable CodeReuse/ActiveRecord
.create!
end
@entries.each_value do |entry|
entry.compose!(deps)
end
end
end
def value
{}.tap do |result_hash|
result_hash[:bridge] = bridge_values.first if bridge_values.any?
result_hash[:pipeline] = pipeline_values if pipeline_values.any?
end
end
private
def bridge_values
@entries.values.select(&:bridge?).map(&:value)
end
def pipeline_values
@entries.values.select(&:pipeline?).map(&:value)
end
end
end
end
end
end
...@@ -40,7 +40,7 @@ module Gitlab ...@@ -40,7 +40,7 @@ module Gitlab
environment: job[:environment_name], environment: job[:environment_name],
coverage_regex: job[:coverage], coverage_regex: job[:coverage],
yaml_variables: yaml_variables(name), yaml_variables: yaml_variables(name),
needs_attributes: job[:needs][:pipeline]&.map { |need| { name: need } }, needs_attributes: job.dig(:needs, :pipeline)&.map { |need| { name: need } },
interruptible: job[:interruptible], interruptible: job[:interruptible],
rules: job[:rules], rules: job[:rules],
options: { options: {
...@@ -59,7 +59,7 @@ module Gitlab ...@@ -59,7 +59,7 @@ module Gitlab
instance: job[:instance], instance: job[:instance],
start_in: job[:start_in], start_in: job[:start_in],
trigger: job[:trigger], trigger: job[:trigger],
bridge_needs: job[:needs][:bridge] bridge_needs: job.dig(:needs, :bridge)
}.compact }.compact }.compact }.compact
end end
...@@ -159,7 +159,7 @@ module Gitlab ...@@ -159,7 +159,7 @@ module Gitlab
end end
def validate_job_needs!(name, job) def validate_job_needs!(name, job)
return unless job[:needs][:pipeline] return unless job.dig(:needs, :pipeline)
stage_index = @stages.index(job[:stage]) stage_index = @stages.index(job[:stage])
......
...@@ -23,7 +23,7 @@ describe Gitlab::Ci::Config::Entry::Job do ...@@ -23,7 +23,7 @@ describe Gitlab::Ci::Config::Entry::Job do
let(:result) do let(:result) do
%i[before_script script stage type after_script cache %i[before_script script stage type after_script cache
image services only except rules variables artifacts image services only except rules needs variables artifacts
environment coverage retry] environment coverage retry]
end end
...@@ -384,21 +384,6 @@ describe Gitlab::Ci::Config::Entry::Job do ...@@ -384,21 +384,6 @@ describe Gitlab::Ci::Config::Entry::Job do
end end
context 'when has needs' do context 'when has needs' do
context 'that are not a array of strings' do
let(:config) do
{
stage: 'test',
script: 'echo',
needs: 'build-job'
}
end
it 'returns error about invalid type' do
expect(entry).not_to be_valid
expect(entry.errors).to include 'job needs should be an array of strings'
end
end
context 'when have dependencies that are not subset of needs' do context 'when have dependencies that are not subset of needs' do
let(:config) do let(:config) do
{ {
......
# frozen_string_literal: true
require 'spec_helper'
describe ::Gitlab::Ci::Config::Entry::Need do
subject(:need) { described_class.new(config) }
context 'when job is specified' do
let(:config) { 'job_name' }
describe '#valid?' do
it { is_expected.to be_valid }
end
describe '#value' do
it 'returns job needs configuration' do
expect(need.value).to eq('job_name')
end
end
end
context 'when job is specified as symbol' do
let(:config) { :job_name }
describe '#valid?' do
it { is_expected.to be_valid }
end
describe '#value' do
it 'returns job needs configuration' do
expect(need.value).to eq('job_name')
end
end
end
context 'when need is empty' do
let(:config) { '' }
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
it 'is returns an error about an empty config' do
expect(need.errors.first)
.to end_with("pipeline config can't be blank")
end
end
end
context 'when need is not a string' do
let(:config) { 123 }
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
it 'is returns an error about an empty config' do
error_message = Gitlab.ee? ? 'has to be a string, symbol or hash' : 'has to be a string or symbol'
expect(need.errors.first)
.to end_with(error_message)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe ::Gitlab::Ci::Config::Entry::Needs do
subject(:needs) { described_class.new(config) }
describe 'validations' do
before do
needs.compose!
end
context 'when entry config value is correct' do
let(:config) { ['job_name'] }
describe '#valid?' do
it { is_expected.to be_valid }
end
end
context 'when wrong needs type is used' do
let(:config) { [123] }
describe '#valid?' do
it { is_expected.not_to be_valid }
end
describe '#errors' do
it 'returns error about incorrect type' do
error_message = Gitlab.ee? ? 'need has to be a string, symbol or hash' : 'need has to be a string or symbol'
expect(needs.errors)
.to include error_message
end
end
end
end
describe '.compose!' do
context 'when valid job entries composed' do
let(:config) { %w[first_job_name second_job_name] }
before do
needs.compose!
end
describe '#value' do
it 'returns key value' do
expect(needs.value).to eq(pipeline: %w[first_job_name second_job_name])
end
end
describe '#descendants' do
it 'creates valid descendant nodes' do
expect(needs.descendants.count).to eq 2
expect(needs.descendants)
.to all(be_an_instance_of(::Gitlab::Ci::Config::Entry::Need))
end
end
end
end
end
...@@ -1293,12 +1293,7 @@ module Gitlab ...@@ -1293,12 +1293,7 @@ module Gitlab
stage: "test", stage: "test",
stage_idx: 2, stage_idx: 2,
name: "test1", name: "test1",
options: { options: { script: ["test"] },
script: ["test"],
# This does not make sense, there is a follow-up:
# https://gitlab.com/gitlab-org/gitlab-foss/issues/65569
bridge_needs: %w[build1 build2]
},
needs_attributes: [ needs_attributes: [
{ name: "build1" }, { name: "build1" },
{ name: "build2" } { name: "build2" }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment