diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-18 13:16:36 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-18 13:16:36 +0000 |
commit | 311b0269b4eb9839fa63f80c8d7a58f32b8138a0 (patch) | |
tree | 07e7870bca8aed6d61fdcc810731c50d2c40af47 /spec/models/ci | |
parent | 27909cef6c4170ed9205afa7426b8d3de47cbb0c (diff) | |
download | gitlab-ce-14.5.0-rc42.tar.gz |
Add latest changes from gitlab-org/gitlab@14-5-stable-eev14.5.0-rc42
Diffstat (limited to 'spec/models/ci')
-rw-r--r-- | spec/models/ci/bridge_spec.rb | 2 | ||||
-rw-r--r-- | spec/models/ci/build_metadata_spec.rb | 12 | ||||
-rw-r--r-- | spec/models/ci/build_spec.rb | 267 | ||||
-rw-r--r-- | spec/models/ci/job_artifact_spec.rb | 62 | ||||
-rw-r--r-- | spec/models/ci/pipeline_schedule_spec.rb | 2 | ||||
-rw-r--r-- | spec/models/ci/pipeline_spec.rb | 18 | ||||
-rw-r--r-- | spec/models/ci/runner_spec.rb | 53 | ||||
-rw-r--r-- | spec/models/ci/trigger_spec.rb | 4 |
8 files changed, 328 insertions, 92 deletions
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index 8f1ae9c5f02..6fde55103f8 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -17,8 +17,6 @@ RSpec.describe Ci::Bridge do { trigger: { project: 'my/project', branch: 'master' } } end - it { is_expected.to respond_to(:runner_features) } - it 'has many sourced pipelines' do expect(bridge).to have_many(:sourced_pipelines) end diff --git a/spec/models/ci/build_metadata_spec.rb b/spec/models/ci/build_metadata_spec.rb index 069864fa765..b2ffb34da1d 100644 --- a/spec/models/ci/build_metadata_spec.rb +++ b/spec/models/ci/build_metadata_spec.rb @@ -121,4 +121,16 @@ RSpec.describe Ci::BuildMetadata do end end end + + describe 'set_cancel_gracefully' do + it 'sets cancel_gracefully' do + build.set_cancel_gracefully + + expect(build.cancel_gracefully?).to be true + end + + it 'returns false' do + expect(build.cancel_gracefully?).to be false + end + end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 2ebf75a1d8a..b7de8ca4337 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -35,7 +35,8 @@ RSpec.describe Ci::Build do it { is_expected.to respond_to(:has_trace?) } it { is_expected.to respond_to(:trace) } - it { is_expected.to respond_to(:runner_features) } + it { is_expected.to respond_to(:set_cancel_gracefully) } + it { is_expected.to respond_to(:cancel_gracefully?) } it { is_expected.to delegate_method(:merge_request?).to(:pipeline) } it { is_expected.to delegate_method(:merge_request_ref?).to(:pipeline) } @@ -214,6 +215,26 @@ RSpec.describe Ci::Build do end end + describe '.license_management_jobs' do + subject { described_class.license_management_jobs } + + let!(:management_build) { create(:ci_build, :success, name: :license_management) } + let!(:scanning_build) { create(:ci_build, :success, name: :license_scanning) } + let!(:another_build) { create(:ci_build, :success, name: :another_type) } + + it 'returns license_scanning jobs' do + is_expected.to include(scanning_build) + end + + it 'returns license_management jobs' do + is_expected.to include(management_build) + end + + it 'doesnt return filtered out jobs' do + is_expected.not_to include(another_build) + end + end + describe '.finished_before' do subject { described_class.finished_before(date) } @@ -350,7 +371,7 @@ RSpec.describe Ci::Build do it 'sticks the build if the status changed' do job = create(:ci_build, :pending) - expect(ApplicationRecord.sticking).to receive(:stick) + expect(described_class.sticking).to receive(:stick) .with(:build, job.id) job.update!(status: :running) @@ -1290,7 +1311,7 @@ RSpec.describe Ci::Build do end end - shared_examples_for 'state transition as a deployable' do + describe 'state transition as a deployable' do subject { build.send(event) } let!(:build) { create(:ci_build, :with_deployment, :start_review_app, project: project, pipeline: pipeline) } @@ -1332,6 +1353,22 @@ RSpec.describe Ci::Build do expect(deployment).to be_running end + + context 'when deployment is already running state' do + before do + build.deployment.success! + end + + it 'does not change deployment status and tracks an error' do + expect(Gitlab::ErrorTracking) + .to receive(:track_exception).with( + instance_of(Deployment::StatusSyncError), deployment_id: deployment.id, build_id: build.id) + + with_cross_database_modification_prevented do + expect { subject }.not_to change { deployment.reload.status } + end + end + end end context 'when transits to success' do @@ -1399,36 +1436,6 @@ RSpec.describe Ci::Build do end end - it_behaves_like 'state transition as a deployable' do - context 'when transits to running' do - let(:event) { :run! } - - context 'when deployment is already running state' do - before do - build.deployment.success! - end - - it 'does not change deployment status and tracks an error' do - expect(Gitlab::ErrorTracking) - .to receive(:track_exception).with( - instance_of(Deployment::StatusSyncError), deployment_id: deployment.id, build_id: build.id) - - with_cross_database_modification_prevented do - expect { subject }.not_to change { deployment.reload.status } - end - end - end - end - end - - context 'when update_deployment_after_transaction_commit feature flag is disabled' do - before do - stub_feature_flags(update_deployment_after_transaction_commit: false) - end - - it_behaves_like 'state transition as a deployable' - end - describe '#on_stop' do subject { build.on_stop } @@ -2759,7 +2766,10 @@ RSpec.describe Ci::Build do let(:job_dependency_var) { { key: 'job_dependency', value: 'value', public: true, masked: false } } before do - allow(build).to receive(:predefined_variables) { [build_pre_var] } + allow_next_instance_of(Gitlab::Ci::Variables::Builder) do |builder| + allow(builder).to receive(:predefined_variables) { [build_pre_var] } + end + allow(build).to receive(:yaml_variables) { [build_yaml_var] } allow(build).to receive(:persisted_variables) { [] } allow(build).to receive(:job_jwt_variables) { [job_jwt_var] } @@ -3411,75 +3421,122 @@ RSpec.describe Ci::Build do end describe '#scoped_variables' do - context 'when build has not been persisted yet' do - let(:build) do - described_class.new( - name: 'rspec', - stage: 'test', - ref: 'feature', - project: project, - pipeline: pipeline, - scheduling_type: :stage - ) - end + before do + pipeline.clear_memoization(:predefined_vars_in_builder_enabled) + end - let(:pipeline) { create(:ci_pipeline, project: project, ref: 'feature') } + it 'records a prometheus metric' do + histogram = double(:histogram) + expect(::Gitlab::Ci::Pipeline::Metrics).to receive(:pipeline_builder_scoped_variables_histogram) + .and_return(histogram) - it 'does not persist the build' do - expect(build).to be_valid - expect(build).not_to be_persisted + expect(histogram).to receive(:observe) + .with({}, a_kind_of(ActiveSupport::Duration)) - build.scoped_variables + build.scoped_variables + end - expect(build).not_to be_persisted - end + shared_examples 'calculates scoped_variables' do + context 'when build has not been persisted yet' do + let(:build) do + described_class.new( + name: 'rspec', + stage: 'test', + ref: 'feature', + project: project, + pipeline: pipeline, + scheduling_type: :stage + ) + end - it 'returns static predefined variables' do - keys = %w[CI_JOB_NAME - CI_COMMIT_SHA - CI_COMMIT_SHORT_SHA - CI_COMMIT_REF_NAME - CI_COMMIT_REF_SLUG - CI_JOB_STAGE] + let(:pipeline) { create(:ci_pipeline, project: project, ref: 'feature') } - variables = build.scoped_variables + it 'does not persist the build' do + expect(build).to be_valid + expect(build).not_to be_persisted - variables.map { |env| env[:key] }.tap do |names| - expect(names).to include(*keys) + build.scoped_variables + + expect(build).not_to be_persisted end - expect(variables) - .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true, masked: false) + it 'returns static predefined variables' do + keys = %w[CI_JOB_NAME + CI_COMMIT_SHA + CI_COMMIT_SHORT_SHA + CI_COMMIT_REF_NAME + CI_COMMIT_REF_SLUG + CI_JOB_STAGE] + + variables = build.scoped_variables + + variables.map { |env| env[:key] }.tap do |names| + expect(names).to include(*keys) + end + + expect(variables) + .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true, masked: false) + end + + it 'does not return prohibited variables' do + keys = %w[CI_JOB_ID + CI_JOB_URL + CI_JOB_TOKEN + CI_BUILD_ID + CI_BUILD_TOKEN + CI_REGISTRY_USER + CI_REGISTRY_PASSWORD + CI_REPOSITORY_URL + CI_ENVIRONMENT_URL + CI_DEPLOY_USER + CI_DEPLOY_PASSWORD] + + build.scoped_variables.map { |env| env[:key] }.tap do |names| + expect(names).not_to include(*keys) + end + end end - it 'does not return prohibited variables' do - keys = %w[CI_JOB_ID - CI_JOB_URL - CI_JOB_TOKEN - CI_BUILD_ID - CI_BUILD_TOKEN - CI_REGISTRY_USER - CI_REGISTRY_PASSWORD - CI_REPOSITORY_URL - CI_ENVIRONMENT_URL - CI_DEPLOY_USER - CI_DEPLOY_PASSWORD] + context 'with dependency variables' do + let!(:prepare) { create(:ci_build, name: 'prepare', pipeline: pipeline, stage_idx: 0) } + let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, options: { dependencies: ['prepare'] }) } + + let!(:job_variable) { create(:ci_job_variable, :dotenv_source, job: prepare) } - build.scoped_variables.map { |env| env[:key] }.tap do |names| - expect(names).not_to include(*keys) + it 'inherits dependent variables' do + expect(build.scoped_variables.to_hash).to include(job_variable.key => job_variable.value) end end end - context 'with dependency variables' do - let!(:prepare) { create(:ci_build, name: 'prepare', pipeline: pipeline, stage_idx: 0) } - let!(:build) { create(:ci_build, pipeline: pipeline, stage_idx: 1, options: { dependencies: ['prepare'] }) } + it_behaves_like 'calculates scoped_variables' - let!(:job_variable) { create(:ci_job_variable, :dotenv_source, job: prepare) } + it 'delegates to the variable builders' do + expect_next_instance_of(Gitlab::Ci::Variables::Builder) do |builder| + expect(builder) + .to receive(:scoped_variables).with(build, hash_including(:environment, :dependencies)) + .and_call_original - it 'inherits dependent variables' do - expect(build.scoped_variables.to_hash).to include(job_variable.key => job_variable.value) + expect(builder).to receive(:predefined_variables).and_call_original end + + build.scoped_variables + end + + context 'when ci builder feature flag is disabled' do + before do + stub_feature_flags(ci_predefined_vars_in_builder: false) + end + + it 'does not delegate to the variable builders' do + expect_next_instance_of(Gitlab::Ci::Variables::Builder) do |builder| + expect(builder).not_to receive(:predefined_variables) + end + + build.scoped_variables + end + + it_behaves_like 'calculates scoped_variables' end end @@ -3569,6 +3626,27 @@ RSpec.describe Ci::Build do include_examples "secret CI variables" end + describe '#kubernetes_variables' do + let(:build) { create(:ci_build) } + let(:service) { double(execute: template) } + let(:template) { double(to_yaml: 'example-kubeconfig', valid?: template_valid) } + let(:template_valid) { true } + + subject { build.kubernetes_variables } + + before do + allow(Ci::GenerateKubeconfigService).to receive(:new).with(build).and_return(service) + end + + it { is_expected.to include(key: 'KUBECONFIG', value: 'example-kubeconfig', public: false, file: true) } + + context 'generated config is invalid' do + let(:template_valid) { false } + + it { is_expected.not_to include(key: 'KUBECONFIG', value: 'example-kubeconfig', public: false, file: true) } + end + end + describe '#deployment_variables' do let(:build) { create(:ci_build, environment: environment) } let(:environment) { 'production' } @@ -3728,7 +3806,7 @@ RSpec.describe Ci::Build do it 'ensures that it is not run in database transaction' do expect(job.pipeline.persistent_ref).to receive(:create) do - expect(Gitlab::Database.main).not_to be_inside_transaction + expect(ApplicationRecord).not_to be_inside_transaction end run_job_without_exception @@ -5326,4 +5404,23 @@ RSpec.describe Ci::Build do create(:ci_build) end end + + describe '#runner_features' do + subject do + build.save! + build.cancel_gracefully? + end + + let_it_be(:build) { create(:ci_build, pipeline: pipeline) } + + it 'cannot cancel gracefully' do + expect(subject).to be false + end + + it 'can cancel gracefully' do + build.set_cancel_gracefully + + expect(subject).to be true + end + end end diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb index a94a1dd284a..d63f87e8943 100644 --- a/spec/models/ci/job_artifact_spec.rb +++ b/spec/models/ci/job_artifact_spec.rb @@ -351,6 +351,21 @@ RSpec.describe Ci::JobArtifact do end end + context 'when updating any field except the file' do + let(:artifact) { create(:ci_job_artifact, :unarchived_trace_artifact, file_store: 2) } + + before do + stub_artifacts_object_storage(direct_upload: true) + artifact.file.object_store = 1 + end + + it 'the `after_commit` hook does not update `file_store`' do + artifact.update!(expire_at: Time.current) + + expect(artifact.file_store).to be(2) + end + end + describe 'validates file format' do subject { artifact } @@ -507,6 +522,53 @@ RSpec.describe Ci::JobArtifact do end end + describe '#store_after_commit?' do + let(:file_type) { :archive } + let(:artifact) { build(:ci_job_artifact, file_type) } + + context 'when direct upload is enabled' do + before do + stub_artifacts_object_storage(direct_upload: true) + end + + context 'when the artifact is a trace' do + let(:file_type) { :trace } + + context 'when ci_store_trace_outside_transaction is enabled' do + it 'returns true' do + expect(artifact.store_after_commit?).to be_truthy + end + end + + context 'when ci_store_trace_outside_transaction is disabled' do + before do + stub_feature_flags(ci_store_trace_outside_transaction: false) + end + + it 'returns false' do + expect(artifact.store_after_commit?).to be_falsey + end + end + end + + context 'when the artifact is not a trace' do + it 'returns false' do + expect(artifact.store_after_commit?).to be_falsey + end + end + end + + context 'when direct upload is disabled' do + before do + stub_artifacts_object_storage(direct_upload: false) + end + + it 'returns false' do + expect(artifact.store_after_commit?).to be_falsey + end + end + end + describe 'file is being stored' do subject { create(:ci_job_artifact, :archive) } diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb index c7e1fe91b1e..fee74f8f674 100644 --- a/spec/models/ci/pipeline_schedule_spec.rb +++ b/spec/models/ci/pipeline_schedule_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' RSpec.describe Ci::PipelineSchedule do - let_it_be(:project) { create_default(:project) } + let_it_be_with_reload(:project) { create_default(:project) } subject { build(:ci_pipeline_schedule) } diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 5f3aad0ab24..e573a6ef780 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -3361,7 +3361,7 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do shared_examples 'sending a notification' do it 'sends an email', :sidekiq_might_not_need_inline do - should_only_email(pipeline.user, kind: :bcc) + should_only_email(pipeline.user) end end @@ -4595,4 +4595,20 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do end end end + + describe '#authorized_cluster_agents' do + let(:pipeline) { create(:ci_empty_pipeline, :created) } + let(:agent) { instance_double(Clusters::Agent) } + let(:authorization) { instance_double(Clusters::Agents::GroupAuthorization, agent: agent) } + let(:finder) { double(execute: [authorization]) } + + it 'retrieves agent records from the finder and caches the result' do + expect(Clusters::AgentAuthorizationsFinder).to receive(:new).once + .with(pipeline.project) + .and_return(finder) + + expect(pipeline.authorized_cluster_agents).to contain_exactly(agent) + expect(pipeline.authorized_cluster_agents).to contain_exactly(agent) # cached + end + end end diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 826332268c5..2e79159cc60 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -5,6 +5,14 @@ require 'spec_helper' RSpec.describe Ci::Runner do it_behaves_like 'having unique enum values' + it_behaves_like 'it has loose foreign keys' do + let(:factory_name) { :ci_runner } + + before do + Clusters::Applications::Runner # ensure that the referenced model is loaded + end + end + describe 'groups association' do # Due to other assoctions such as projects this whole spec is allowed to # generate cross-database queries. So we have this temporary spec to @@ -44,7 +52,7 @@ RSpec.describe Ci::Runner do let(:runner) { create(:ci_runner, :group, groups: [group]) } it 'disallows assigning group if already assigned to a group' do - runner.groups << build(:group) + runner.runner_namespaces << build(:ci_runner_namespace) expect(runner).not_to be_valid expect(runner.errors.full_messages).to include('Runner needs to be assigned to exactly one group') @@ -397,7 +405,7 @@ RSpec.describe Ci::Runner do it 'sticks the runner to the primary and calls the original method' do runner = create(:ci_runner) - expect(ApplicationRecord.sticking).to receive(:stick) + expect(described_class.sticking).to receive(:stick) .with(:runner, runner.id) expect(Gitlab::Workhorse).to receive(:set_key_and_notify) @@ -618,7 +626,7 @@ RSpec.describe Ci::Runner do end describe '#status' do - let(:runner) { create(:ci_runner, :instance, contacted_at: 1.second.ago) } + let(:runner) { build(:ci_runner, :instance) } subject { runner.status } @@ -630,6 +638,45 @@ RSpec.describe Ci::Runner do it { is_expected.to eq(:not_connected) } end + context 'inactive but online' do + before do + runner.contacted_at = 1.second.ago + runner.active = false + end + + it { is_expected.to eq(:online) } + end + + context 'contacted 1s ago' do + before do + runner.contacted_at = 1.second.ago + end + + it { is_expected.to eq(:online) } + end + + context 'contacted long time ago' do + before do + runner.contacted_at = 1.year.ago + end + + it { is_expected.to eq(:offline) } + end + end + + describe '#deprecated_rest_status' do + let(:runner) { build(:ci_runner, :instance, contacted_at: 1.second.ago) } + + subject { runner.deprecated_rest_status } + + context 'never connected' do + before do + runner.contacted_at = nil + end + + it { is_expected.to eq(:not_connected) } + end + context 'contacted 1s ago' do before do runner.contacted_at = 1.second.ago diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 4ba6c6e50f7..c254279a32f 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -57,4 +57,8 @@ RSpec.describe Ci::Trigger do it { is_expected.to eq(false) } end end + + it_behaves_like 'includes Limitable concern' do + subject { build(:ci_trigger, owner: project.owner, project: project) } + end end |