diff options
Diffstat (limited to 'spec/lib/gitlab/ci/pipeline')
5 files changed, 321 insertions, 28 deletions
diff --git a/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb new file mode 100644 index 00000000000..3eaecb11ae0 --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/cancel_pending_pipelines_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Pipeline::Chain::CancelPendingPipelines do + let_it_be(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let(:prev_pipeline) { create(:ci_pipeline, project: project) } + let(:new_commit) { create(:commit, project: project) } + let(:pipeline) { create(:ci_pipeline, project: project, sha: new_commit.sha) } + + let(:command) do + Gitlab::Ci::Pipeline::Chain::Command.new(project: project, current_user: user) + end + + let(:step) { described_class.new(pipeline, command) } + + before do + create(:ci_build, :interruptible, :running, pipeline: prev_pipeline) + create(:ci_build, :interruptible, :success, pipeline: prev_pipeline) + create(:ci_build, :created, pipeline: prev_pipeline) + + create(:ci_build, :interruptible, pipeline: pipeline) + end + + describe '#perform!' do + subject(:perform) { step.perform! } + + before do + expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created') + expect(build_statuses(pipeline)).to contain_exactly('pending') + end + + context 'when auto-cancel is enabled' do + before do + project.update!(auto_cancel_pending_pipelines: 'enabled') + end + + it 'cancels only previous interruptible builds' do + perform + + expect(build_statuses(prev_pipeline)).to contain_exactly('canceled', 'success', 'canceled') + expect(build_statuses(pipeline)).to contain_exactly('pending') + end + + context 'when the previous pipeline has a child pipeline' do + let(:child_pipeline) { create(:ci_pipeline, child_of: prev_pipeline) } + + context 'when the child pipeline has an interruptible job' do + before do + create(:ci_build, :interruptible, :running, pipeline: child_pipeline) + end + + it 'cancels interruptible builds of child pipeline' do + expect(build_statuses(child_pipeline)).to contain_exactly('running') + + perform + + expect(build_statuses(child_pipeline)).to contain_exactly('canceled') + end + + context 'when FF ci_auto_cancel_all_pipelines is disabled' do + before do + stub_feature_flags(ci_auto_cancel_all_pipelines: false) + end + + it 'does not cancel interruptible builds of child pipeline' do + expect(build_statuses(child_pipeline)).to contain_exactly('running') + + perform + + expect(build_statuses(child_pipeline)).to contain_exactly('running') + end + end + end + + context 'when the child pipeline has not an interruptible job' do + before do + create(:ci_build, :running, pipeline: child_pipeline) + end + + it 'does not cancel the build of child pipeline' do + expect(build_statuses(child_pipeline)).to contain_exactly('running') + + perform + + expect(build_statuses(child_pipeline)).to contain_exactly('running') + end + end + end + + context 'when the prev pipeline source is webide' do + let(:prev_pipeline) { create(:ci_pipeline, :webide, project: project) } + + it 'does not cancel builds of the previous pipeline' do + perform + + expect(build_statuses(prev_pipeline)).to contain_exactly('created', 'running', 'success') + expect(build_statuses(pipeline)).to contain_exactly('pending') + end + end + end + + context 'when auto-cancel is disabled' do + before do + project.update!(auto_cancel_pending_pipelines: 'disabled') + end + + it 'does not cancel any build' do + subject + + expect(build_statuses(prev_pipeline)).to contain_exactly('running', 'success', 'created') + expect(build_statuses(pipeline)).to contain_exactly('pending') + end + end + end + + private + + def build_statuses(pipeline) + pipeline.builds.pluck(:status) + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb index 8c02121857a..5506b079d0f 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/populate_spec.rb @@ -22,6 +22,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do [ Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::SeedBlock.new(pipeline, command), Gitlab::Ci::Pipeline::Chain::Seed.new(pipeline, command) ] end @@ -180,23 +181,21 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Populate do ->(pipeline) { pipeline.variables.create!(key: 'VAR', value: '123') } end - it 'wastes pipeline iid' do - expect { run_chain }.to raise_error(ActiveRecord::RecordNotSaved) - - last_iid = InternalId.ci_pipelines - .where(project_id: project.id) - .last.last_value - - expect(last_iid).to be > 0 + it 'raises error' do + expect { run_chain }.to raise_error(ActiveRecord::RecordNotSaved, + 'You cannot call create unless the parent is saved') end end end context 'when pipeline gets persisted during the process' do - let(:pipeline) { create(:ci_pipeline, project: project) } + before do + dependencies.each(&:perform!) + pipeline.save! + end it 'raises error' do - expect { run_chain }.to raise_error(described_class::PopulateError) + expect { step.perform! }.to raise_error(described_class::PopulateError) end end diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb new file mode 100644 index 00000000000..85c8e20767f --- /dev/null +++ b/spec/lib/gitlab/ci/pipeline/chain/seed_block_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Ci::Pipeline::Chain::SeedBlock do + let(:project) { create(:project, :repository) } + let(:user) { create(:user, developer_projects: [project]) } + let(:seeds_block) { } + + let(:command) do + Gitlab::Ci::Pipeline::Chain::Command.new( + project: project, + current_user: user, + origin_ref: 'master', + seeds_block: seeds_block) + end + + let(:pipeline) { build(:ci_pipeline, project: project) } + + describe '#perform!' do + before do + stub_ci_pipeline_yaml_file(YAML.dump(config)) + end + + subject(:run_chain) do + [ + Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command) + ].map(&:perform!) + + described_class.new(pipeline, command).perform! + end + + let(:config) do + { rspec: { script: 'rake' } } + end + + context 'when there is not seeds_block' do + it 'does nothing' do + expect { run_chain }.not_to raise_error + end + end + + context 'when there is seeds_block' do + let(:seeds_block) do + ->(pipeline) { pipeline.variables.build(key: 'VAR', value: '123') } + end + + it 'executes the block' do + run_chain + + expect(pipeline.variables.size).to eq(1) + end + + context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do + before do + stub_feature_flags(ci_seed_block_run_before_workflow_rules: false) + end + + it 'does not execute the block' do + run_chain + + expect(pipeline.variables.size).to eq(0) + end + end + end + + context 'when the seeds_block tries to save the pipelie' do + let(:seeds_block) do + ->(pipeline) { pipeline.save! } + end + + it 'raises error' do + expect { run_chain }.to raise_error('Pipeline cannot be persisted by `seeds_block`') + end + end + end +end diff --git a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb index f83cd49d780..d849c768a3c 100644 --- a/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/chain/seed_spec.rb @@ -5,22 +5,14 @@ require 'spec_helper' RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do let(:project) { create(:project, :repository) } let(:user) { create(:user, developer_projects: [project]) } + let(:seeds_block) { } let(:command) do Gitlab::Ci::Pipeline::Chain::Command.new( project: project, current_user: user, origin_ref: 'master', - seeds_block: nil) - end - - def run_chain(pipeline, command) - [ - Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), - Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command) - ].map(&:perform!) - - described_class.new(pipeline, command).perform! + seeds_block: seeds_block) end let(:pipeline) { build(:ci_pipeline, project: project) } @@ -28,22 +20,36 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do describe '#perform!' do before do stub_ci_pipeline_yaml_file(YAML.dump(config)) - run_chain(pipeline, command) end let(:config) do { rspec: { script: 'rake' } } end + subject(:run_chain) do + [ + Gitlab::Ci::Pipeline::Chain::Config::Content.new(pipeline, command), + Gitlab::Ci::Pipeline::Chain::Config::Process.new(pipeline, command) + ].map(&:perform!) + + described_class.new(pipeline, command).perform! + end + it 'allocates next IID' do + run_chain + expect(pipeline.iid).to be_present end it 'ensures ci_ref' do + run_chain + expect(pipeline.ci_ref).to be_present end it 'sets the seeds in the command object' do + run_chain + expect(command.stage_seeds).to all(be_a Gitlab::Ci::Pipeline::Seed::Base) expect(command.stage_seeds.count).to eq 1 end @@ -58,6 +64,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'correctly fabricates a stage seeds object' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 2 expect(seeds.first.attributes[:name]).to eq 'test' @@ -81,6 +89,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'returns stage seeds only assigned to master' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 1 @@ -100,6 +110,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'returns stage seeds only assigned to schedules' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 1 @@ -127,6 +139,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do let(:pipeline) { build(:ci_pipeline, project: project) } it 'returns seeds for kubernetes dependent job' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 2 @@ -138,6 +152,8 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do context 'when kubernetes is not active' do it 'does not return seeds for kubernetes dependent job' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 1 @@ -155,11 +171,39 @@ RSpec.describe Gitlab::Ci::Pipeline::Chain::Seed do end it 'returns stage seeds only when variables expression is truthy' do + run_chain + seeds = command.stage_seeds expect(seeds.size).to eq 1 expect(seeds.dig(0, 0, :name)).to eq 'unit' end end + + context 'when there is seeds_block' do + let(:seeds_block) do + ->(pipeline) { pipeline.variables.build(key: 'VAR', value: '123') } + end + + context 'when FF ci_seed_block_run_before_workflow_rules is enabled' do + it 'does not execute the block' do + run_chain + + expect(pipeline.variables.size).to eq(0) + end + end + + context 'when FF ci_seed_block_run_before_workflow_rules is disabled' do + before do + stub_feature_flags(ci_seed_block_run_before_workflow_rules: false) + end + + it 'executes the block' do + run_chain + + expect(pipeline.variables.size).to eq(1) + end + end + end end end diff --git a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb index 0c8a0de2f34..e62bf042fba 100644 --- a/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb +++ b/spec/lib/gitlab/ci/pipeline/seed/environment_spec.rb @@ -16,20 +16,37 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do subject { seed.to_resource } shared_examples_for 'returning a correct environment' do + let(:expected_auto_stop_in_seconds) do + if expected_auto_stop_in + ChronicDuration.parse(expected_auto_stop_in).seconds + end + end + it 'returns a persisted environment object' do - expect { subject }.to change { Environment.count }.by(1) + freeze_time do + expect { subject }.to change { Environment.count }.by(1) - expect(subject).to be_a(Environment) - expect(subject).to be_persisted - expect(subject.project).to eq(project) - expect(subject.name).to eq(expected_environment_name) + expect(subject).to be_a(Environment) + expect(subject).to be_persisted + expect(subject.project).to eq(project) + expect(subject.name).to eq(expected_environment_name) + expect(subject.auto_stop_in).to eq(expected_auto_stop_in_seconds) + end end context 'when environment has already existed' do - let!(:environment) { create(:environment, project: project, name: expected_environment_name) } + let!(:environment) do + create(:environment, + project: project, + name: expected_environment_name + ).tap do |env| + env.auto_stop_in = expected_auto_stop_in + end + end it 'returns the existing environment object' do expect { subject }.not_to change { Environment.count } + expect { subject }.not_to change { environment.auto_stop_at } expect(subject).to be_persisted expect(subject).to eq(environment) @@ -37,9 +54,10 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do end end - context 'when job has environment attribute' do + context 'when job has environment name attribute' do let(:environment_name) { 'production' } let(:expected_environment_name) { 'production' } + let(:expected_auto_stop_in) { nil } let(:attributes) do { @@ -49,11 +67,41 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do end it_behaves_like 'returning a correct environment' + + context 'and job environment also has an auto_stop_in attribute' do + let(:environment_auto_stop_in) { '5 minutes' } + let(:expected_auto_stop_in) { '5 minutes' } + + let(:attributes) do + { + environment: environment_name, + options: { + environment: { + name: environment_name, + auto_stop_in: environment_auto_stop_in + } + } + } + end + + it_behaves_like 'returning a correct environment' + + context 'but the environment auto_stop_in on create flag is disabled' do + let(:expected_auto_stop_in) { nil } + + before do + stub_feature_flags(environment_auto_stop_start_on_create: false) + end + + it_behaves_like 'returning a correct environment' + end + end end context 'when job starts a review app' do let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' } let(:expected_environment_name) { "review/#{job.ref}" } + let(:expected_auto_stop_in) { nil } let(:attributes) do { @@ -68,6 +116,7 @@ RSpec.describe Gitlab::Ci::Pipeline::Seed::Environment do context 'when job stops a review app' do let(:environment_name) { 'review/$CI_COMMIT_REF_NAME' } let(:expected_environment_name) { "review/#{job.ref}" } + let(:expected_auto_stop_in) { nil } let(:attributes) do { |