diff options
Diffstat (limited to 'spec/models/environment_spec.rb')
-rw-r--r-- | spec/models/environment_spec.rb | 299 |
1 files changed, 173 insertions, 126 deletions
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb index 066763645ab..9d4699cb91e 100644 --- a/spec/models/environment_spec.rb +++ b/spec/models/environment_spec.rb @@ -16,13 +16,13 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ it { is_expected.to be_kind_of(ReactiveCaching) } it { is_expected.to nullify_if_blank(:external_url) } it { is_expected.to nullify_if_blank(:kubernetes_namespace) } + it { is_expected.to nullify_if_blank(:flux_resource_path) } it { is_expected.to belong_to(:project).required } it { is_expected.to belong_to(:merge_request).optional } it { is_expected.to belong_to(:cluster_agent).optional } it { is_expected.to have_many(:deployments) } - it { is_expected.to have_many(:metrics_dashboard_annotations) } it { is_expected.to have_many(:alert_management_alerts) } it { is_expected.to have_one(:upcoming_deployment) } it { is_expected.to have_one(:latest_opened_most_severe_alert) } @@ -38,6 +38,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ it { is_expected.to validate_length_of(:external_url).is_at_most(255) } it { is_expected.to validate_length_of(:kubernetes_namespace).is_at_most(63) } + it { is_expected.to validate_length_of(:flux_resource_path).is_at_most(255) } describe 'validation' do it 'does not become invalid record when external_url is empty' do @@ -690,178 +691,214 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ subject { environment.stop_with_actions!(user) } - before do - expect(environment).to receive(:available?).and_call_original - end - - context 'when no other actions' do - context 'environment is available' do - before do - environment.update!(state: :available) - end - - it do - actions = subject - - expect(environment).to be_stopped - expect(actions).to match_array([]) - end + shared_examples_for 'stop with playing a teardown job' do + before do + expect(environment).to receive(:available?).and_call_original end - context 'environment is already stopped' do - before do - environment.update!(state: :stopped) - end + context 'when no other actions' do + context 'environment is available' do + before do + environment.update!(state: :available) + end - it do - subject + it do + actions = subject - expect(environment).to be_stopped + expect(environment).to be_stopped + expect(actions).to match_array([]) + end end - end - end - - context 'when matching action is defined' do - let(:pipeline) { create(:ci_pipeline, project: project) } - let(:build_a) { create(:ci_build, :success, pipeline: pipeline) } - before do - create(:deployment, :success, - environment: environment, - deployable: build_a, - on_stop: 'close_app_a') - end + context 'environment is already stopped' do + before do + environment.update!(state: :stopped) + end - context 'when user is not allowed to stop environment' do - let!(:close_action) do - create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_a') - end + it do + subject - it 'raises an exception' do - expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError) + expect(environment).to be_stopped + end end end - context 'when user is allowed to stop environment' do - before do - project.add_developer(user) + context 'when matching action is defined' do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:job_a) { create(factory_type, :success, pipeline: pipeline, **factory_options) } - create(:protected_branch, :developers_can_merge, name: 'master', project: project) + before do + create(:deployment, :success, + environment: environment, + deployable: job_a, + on_stop: 'close_app_a') end - context 'when action did not yet finish' do + context 'when user is not allowed to stop environment' do let!(:close_action) do - create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_a') + create(factory_type, :manual, pipeline: pipeline, name: 'close_app_a', **factory_options) end - it 'returns the same action' do - action = subject.first - expect(action).to eq(close_action) - expect(action.user).to eq(user) + it 'raises an exception' do + expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError) end + end - it 'environment is not stopped' do - subject + context 'when user is allowed to stop environment' do + before do + project.add_developer(user) - expect(environment).not_to be_stopped + create(:protected_branch, :developers_can_merge, name: 'master', project: project) end - end - context 'if action did finish' do - let!(:close_action) do - create(:ci_build, :manual, :success, pipeline: pipeline, name: 'close_app_a') - end + context 'when action did not yet finish' do + let!(:close_action) do + create(factory_type, :manual, pipeline: pipeline, name: 'close_app_a', **factory_options) + end - it 'returns a new action of the same type' do - action = subject.first + it 'returns the same action' do + action = subject.first + expect(action).to eq(close_action) + expect(action.user).to eq(user) + end - expect(action).to be_persisted - expect(action.name).to eq(close_action.name) - expect(action.user).to eq(user) - end - end + it 'environment is not stopped' do + subject - context 'close action does not raise ActiveRecord::StaleObjectError' do - let!(:close_action) do - create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_a') + expect(environment).not_to be_stopped + end end - before do - # preload the build - environment.stop_actions + context 'if action did finish' do + let!(:close_action) do + create(factory_type, :manual, :success, pipeline: pipeline, name: 'close_app_a', **factory_options) + end - # Update record as the other process. This makes `environment.stop_action` stale. - close_action.drop! - end + it 'returns a new action of the same type when build job' do + skip unless factory_type == :ci_build - it 'successfully plays the build even if the build was a stale object' do - # Since build is droped. - expect(close_action.processed).to be_falsey + action = subject.first + + expect(action).to be_persisted + expect(action.name).to eq(close_action.name) + expect(action.user).to eq(user) + end - # it encounters the StaleObjectError at first, but reloads the object and runs `build.play` - expect { subject }.not_to raise_error + it 'does nothing when bridge job' do + skip unless factory_type == :ci_bridge - # Now the build should be processed. - expect(close_action.reload.processed).to be_truthy + action = subject.first + + expect(action).to be_nil + end end - end - end - end - context 'when there are more then one stop action for the environment' do - let(:pipeline) { create(:ci_pipeline, project: project) } - let(:build_a) { create(:ci_build, :success, pipeline: pipeline) } - let(:build_b) { create(:ci_build, :success, pipeline: pipeline) } + context 'close action does not raise ActiveRecord::StaleObjectError' do + let!(:close_action) do + create(factory_type, :manual, pipeline: pipeline, name: 'close_app_a', **factory_options) + end - let!(:close_actions) do - [ - create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_a'), - create(:ci_build, :manual, pipeline: pipeline, name: 'close_app_b') - ] - end + before do + # preload the job + environment.stop_actions - before do - project.add_developer(user) + # Update record as the other process. This makes `environment.stop_action` stale. + close_action.drop! + end - create(:deployment, :success, - environment: environment, - deployable: build_a, - finished_at: 5.minutes.ago, - on_stop: 'close_app_a') + it 'successfully plays the job even if the job was a stale object when build job' do + skip unless factory_type == :ci_build - create(:deployment, :success, - environment: environment, - deployable: build_b, - finished_at: 1.second.ago, - on_stop: 'close_app_b') - end + # Since job is droped. + expect(close_action.processed).to be_falsey + + # it encounters the StaleObjectError at first, but reloads the object and runs `job.play` + expect { subject }.not_to raise_error + + # Now the job should be processed. + expect(close_action.reload.processed).to be_truthy + end + + it 'does nothing when bridge job' do + skip unless factory_type == :ci_bridge + + expect(close_action.processed).to be_falsey - it 'returns the same actions' do - actions = subject + # it encounters the StaleObjectError at first, but reloads the object and runs `job.play` + expect { subject }.not_to raise_error - expect(actions.count).to eq(close_actions.count) - expect(actions.pluck(:id)).to match_array(close_actions.pluck(:id)) - expect(actions.pluck(:user)).to match_array(close_actions.pluck(:user)) + # Bridge is not retried currently. + expect(close_action.processed).to be_falsey + end + end + end end - context 'when there are failed builds' do + context 'when there are more then one stop action for the environment' do + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:job_a) { create(factory_type, :success, pipeline: pipeline, **factory_options) } + let(:job_b) { create(factory_type, :success, pipeline: pipeline, **factory_options) } + + let!(:close_actions) do + [ + create(factory_type, :manual, pipeline: pipeline, name: 'close_app_a', **factory_options), + create(factory_type, :manual, pipeline: pipeline, name: 'close_app_b', **factory_options) + ] + end + before do - create(:ci_build, :failed, pipeline: pipeline, name: 'close_app_c') + project.add_developer(user) + + create(:deployment, :success, + environment: environment, + deployable: job_a, + finished_at: 5.minutes.ago, + on_stop: 'close_app_a') - create(:deployment, :failed, + create(:deployment, :success, environment: environment, - deployable: create(:ci_build, pipeline: pipeline), - on_stop: 'close_app_c') + deployable: job_b, + finished_at: 1.second.ago, + on_stop: 'close_app_b') end - it 'returns only stop actions from successful builds' do + it 'returns the same actions' do actions = subject - expect(actions).to match_array(close_actions) - expect(actions.count).to eq(pipeline.latest_successful_builds.count) + expect(actions.count).to eq(close_actions.count) + expect(actions.pluck(:id)).to match_array(close_actions.pluck(:id)) + expect(actions.pluck(:user)).to match_array(close_actions.pluck(:user)) + end + + context 'when there are failed builds' do + before do + create(factory_type, :failed, pipeline: pipeline, name: 'close_app_c', **factory_options) + + create(:deployment, :failed, + environment: environment, + deployable: create(factory_type, pipeline: pipeline, **factory_options), + on_stop: 'close_app_c') + end + + it 'returns only stop actions from successful builds' do + actions = subject + + expect(actions).to match_array(close_actions) + expect(actions.count).to eq(pipeline.latest_successful_jobs.count) + end end end end + + it_behaves_like 'stop with playing a teardown job' do + let(:factory_type) { :ci_build } + let(:factory_options) { {} } + end + + it_behaves_like 'stop with playing a teardown job' do + let(:factory_type) { :ci_bridge } + let(:factory_options) { { downstream: project } } + end end describe '#stop_actions' do @@ -1814,13 +1851,23 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ let_it_be(:project) { create(:project, :repository) } let_it_be(:environment, reload: true) { create(:environment, project: project) } - let!(:deployment) { create(:deployment, project: project, environment: environment, deployable: build) } - let!(:build) { create(:ci_build, :running, project: project, environment: environment) } + let!(:deployment) { create(:deployment, project: project, environment: environment, deployable: job) } + let!(:job) { create(:ci_build, :running, project: project, environment: environment) } it 'cancels an active deployment job' do subject - expect(build.reset).to be_canceled + expect(job.reset).to be_canceled + end + + context 'when deployment job is bridge' do + let!(:job) { create(:ci_bridge, :running, project: project, environment: environment) } + + it 'does not cancel an active deployment job' do + subject + + expect(job.reset).to be_running + end end context 'when deployable does not exist' do @@ -1831,7 +1878,7 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching, feature_categ it 'does not raise an error' do expect { subject }.not_to raise_error - expect(build.reset).to be_running + expect(job.reset).to be_running end end end |