Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-01 15:07:40 +00:00
parent 098ec8c914
commit 04e74bf311
58 changed files with 793 additions and 280 deletions

View File

@ -55,7 +55,7 @@ class ProjectsFinder < UnionFinder
collection = Project.wrap_with_cte(collection) if use_cte
collection = filter_projects(collection)
if params[:sort] == 'similarity' && params[:search] && Feature.enabled?(:project_finder_similarity_sort, current_user)
if params[:sort] == 'similarity' && params[:search]
collection.sorted_by_similarity_desc(params[:search])
else
sort(collection)

View File

@ -38,7 +38,7 @@ module Security
def execute
return [] if @job_types.empty?
if Feature.enabled?(:ci_build_metadata_config)
if Feature.enabled?(:ci_build_metadata_config, pipeline.project, default_enabled: :yaml)
find_jobs
else
find_jobs_legacy

View File

@ -0,0 +1,49 @@
# frozen_string_literal: true
module Mutations
module Ci
module JobTokenScope
class AddProject < BaseMutation
include FindsProject
graphql_name 'CiJobTokenScopeAddProject'
authorize :admin_project
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: 'The project that the CI job token scope belongs to.'
argument :target_project_path, GraphQL::ID_TYPE,
required: true,
description: 'The project to be added to the CI job token scope.'
field :ci_job_token_scope,
Types::Ci::JobTokenScopeType,
null: true,
description: "The CI job token's scope of access."
def resolve(project_path:, target_project_path:)
project = authorized_find!(project_path)
target_project = Project.find_by_full_path(target_project_path)
result = ::Ci::JobTokenScope::AddProjectService
.new(project, current_user)
.execute(target_project)
if result.success?
{
ci_job_token_scope: ::Ci::JobToken::Scope.new(project),
errors: []
}
else
{
ci_job_token_scope: nil,
errors: [result.message]
}
end
end
end
end
end
end

View File

@ -99,6 +99,7 @@ module Types
mount_mutation Mutations::Ci::CiCdSettingsUpdate
mount_mutation Mutations::Ci::Job::Play
mount_mutation Mutations::Ci::Job::Retry
mount_mutation Mutations::Ci::JobTokenScope::AddProject
mount_mutation Mutations::Ci::Runner::Update, feature_flag: :runner_graphql_query
mount_mutation Mutations::Ci::Runner::Delete, feature_flag: :runner_graphql_query
mount_mutation Mutations::Ci::RunnersRegistrationToken::Reset, feature_flag: :runner_graphql_query

View File

@ -172,10 +172,6 @@ module IntegrationsHelper
name: integration.to_param
}
end
def show_service_templates_nav_link?
Feature.disabled?(:disable_service_templates, type: :development, default_enabled: :yaml)
end
end
IntegrationsHelper.prepend_mod_with('IntegrationsHelper')

View File

@ -22,8 +22,8 @@ module Ci
validates :build, presence: true
validates :secrets, json_schema: { filename: 'build_metadata_secrets' }
serialize :config_options, Serializers::Json # rubocop:disable Cop/ActiveRecordSerialize
serialize :config_variables, Serializers::Json # rubocop:disable Cop/ActiveRecordSerialize
serialize :config_options, Serializers::SymbolizedJson # rubocop:disable Cop/ActiveRecordSerialize
serialize :config_variables, Serializers::SymbolizedJson # rubocop:disable Cop/ActiveRecordSerialize
chronic_duration_attr_reader :timeout_human_readable, :timeout

View File

@ -77,7 +77,7 @@ module Ci
def write_metadata_attribute(legacy_key, metadata_key, value)
# save to metadata or this model depending on the state of feature flag
if Feature.enabled?(:ci_build_metadata_config)
if Feature.enabled?(:ci_build_metadata_config, project, default_enabled: :yaml)
ensure_metadata.write_attribute(metadata_key, value)
write_attribute(legacy_key, nil)
else

View File

@ -0,0 +1,54 @@
# frozen_string_literal: true
module Ci
module JobTokenScope
class AddProjectService < ::BaseService
TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND = "The target_project that you are attempting to access does " \
"not exist or you don't have permission to perform this action"
def execute(target_project)
if error_response = validation_error(target_project)
return error_response
end
link = add_project!(target_project)
ServiceResponse.success(payload: { project_link: link })
rescue ActiveRecord::RecordNotUnique
ServiceResponse.error(message: "Target project is already in the job token scope")
rescue ActiveRecord::RecordInvalid => e
ServiceResponse.error(message: e.message)
end
private
def validation_error(target_project)
unless project.ci_job_token_scope_enabled?
return ServiceResponse.error(message: "Job token scope is disabled for this project")
end
unless can?(current_user, :admin_project, project)
return ServiceResponse.error(message: "Insufficient permissions to modify the job token scope")
end
unless target_project
return ServiceResponse.error(message: TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND)
end
unless can?(current_user, :read_project, target_project)
return ServiceResponse.error(message: TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND)
end
nil
end
def add_project!(target_project)
::Ci::JobToken::ProjectScopeLink.create!(
source_project: project,
target_project: target_project,
added_by: current_user
)
end
end
end
end

View File

@ -18,7 +18,7 @@ module Ci
.joins('LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id')
.where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0')
if Feature.enabled?(:ci_queueing_disaster_recovery, runner, type: :ops, default_enabled: :yaml)
if Feature.enabled?(:ci_queueing_disaster_recovery_disable_fair_scheduling, runner, type: :ops, default_enabled: :yaml)
# if disaster recovery is enabled, we fallback to FIFO scheduling
relation.order('ci_builds.id ASC')
else

View File

@ -18,7 +18,7 @@ module Ci
.joins('LEFT JOIN project_features ON ci_pending_builds.project_id = project_features.project_id')
.where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0')
if Feature.enabled?(:ci_queueing_disaster_recovery, runner, type: :ops, default_enabled: :yaml)
if Feature.enabled?(:ci_queueing_disaster_recovery_disable_fair_scheduling, runner, type: :ops, default_enabled: :yaml)
# if disaster recovery is enabled, we fallback to FIFO scheduling
relation.order('ci_pending_builds.build_id ASC')
else

View File

@ -209,19 +209,6 @@
= render_if_exists 'layouts/nav/sidebar/credentials_link'
- if show_service_templates_nav_link?
= nav_link(controller: :services) do
= link_to admin_application_settings_services_path do
.nav-icon-container
= sprite_icon('template')
%span.nav-item-name
= _('Service Templates')
%ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: :services, html_options: { class: "fly-out-top-item" } ) do
= link_to admin_application_settings_services_path do
%strong.fly-out-top-item-name
= _('Service Templates')
= nav_link(controller: :labels) do
= link_to admin_labels_path do
.nav-icon-container

View File

@ -5,4 +5,4 @@ rollout_issue_url:
milestone: '14.0'
type: development
group: group::source code
default_enabled: false
default_enabled: true

View File

@ -1,7 +1,7 @@
---
name: ci_build_metadata_config
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7238
rollout_issue_url:
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330954
milestone: '11.7'
type: development
group: group::pipeline execution

View File

@ -1,8 +0,0 @@
---
name: erase_traces_from_already_archived_jobs_when_archiving_again
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56353
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/326679
milestone: "13.11"
type: development
group: group::pipeline execution
default_enabled: true

View File

@ -1,8 +1,8 @@
---
name: disable_service_templates
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59098
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/327436
milestone: '13.12'
name: jira_issue_details_edit_status
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/60092
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330628
milestone: '14.1'
type: development
group: group::ecosystem
default_enabled: true
default_enabled: false

View File

@ -1,8 +0,0 @@
---
name: project_finder_similarity_sort
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43136
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/263249
milestone: '13.5'
type: development
group: group::threat insights
default_enabled: false

View File

@ -1,7 +1,7 @@
---
name: use_traversal_ids_for_ancestors
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57137
rollout_issue_url:
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/334952
milestone: '13.12'
type: development
group: group::access

View File

@ -0,0 +1,8 @@
---
name: ci_queueing_disaster_recovery_disable_fair_scheduling
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56658
rollout_issue_url:
milestone: "13.12"
type: ops
group: group::pipeline execution
default_enabled: false

View File

@ -1,5 +1,5 @@
---
name: ci_queueing_disaster_recovery
name: ci_queueing_disaster_recovery_disable_quota
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/56658
rollout_issue_url:
milestone: "13.12"

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
class ScheduleDeleteOrphanedDeployments < ActiveRecord::Migration[6.1]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'DeleteOrphanedDeployments'
BATCH_SIZE = 100_000
DELAY_INTERVAL = 2.minutes
disable_ddl_transaction!
def up
queue_background_migration_jobs_by_range_at_intervals(
define_batchable_model('deployments'),
MIGRATION,
DELAY_INTERVAL,
batch_size: BATCH_SIZE,
track_jobs: true
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class RemoveDeprecatedModsecurityColumns < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
INDEX_NAME = 'index_clusters_applications_ingress_on_modsecurity'
def up
remove_column :clusters_applications_ingress, :modsecurity_enabled if column_exists?(:clusters_applications_ingress, :modsecurity_enabled)
remove_column :clusters_applications_ingress, :modsecurity_mode if column_exists?(:clusters_applications_ingress, :modsecurity_mode)
end
def down
add_column :clusters_applications_ingress, :modsecurity_enabled, :boolean unless column_exists?(:clusters_applications_ingress, :modsecurity_enabled)
add_column :clusters_applications_ingress, :modsecurity_mode, :smallint, null: false, default: 0 unless column_exists?(:clusters_applications_ingress, :modsecurity_mode)
add_concurrent_index :clusters_applications_ingress, [:modsecurity_enabled, :modsecurity_mode, :cluster_id], name: INDEX_NAME
end
end

View File

@ -0,0 +1 @@
432954295d6f3a2a45f3deef42b547ffe42501beaea4f376e1be51cf148de671

View File

@ -0,0 +1 @@
28e448810fdf8bab4de44d45acac862e752f578b5b8fd77b885a385b9ef16b2d

View File

@ -11682,9 +11682,7 @@ CREATE TABLE clusters_applications_ingress (
cluster_ip character varying,
status_reason text,
external_ip character varying,
external_hostname character varying,
modsecurity_enabled boolean,
modsecurity_mode smallint DEFAULT 0 NOT NULL
external_hostname character varying
);
CREATE SEQUENCE clusters_applications_ingress_id_seq
@ -23169,8 +23167,6 @@ CREATE UNIQUE INDEX index_clusters_applications_helm_on_cluster_id ON clusters_a
CREATE UNIQUE INDEX index_clusters_applications_ingress_on_cluster_id ON clusters_applications_ingress USING btree (cluster_id);
CREATE INDEX index_clusters_applications_ingress_on_modsecurity ON clusters_applications_ingress USING btree (modsecurity_enabled, modsecurity_mode, cluster_id);
CREATE UNIQUE INDEX index_clusters_applications_jupyter_on_cluster_id ON clusters_applications_jupyter USING btree (cluster_id);
CREATE INDEX index_clusters_applications_jupyter_on_oauth_application_id ON clusters_applications_jupyter USING btree (oauth_application_id);

View File

@ -162,6 +162,7 @@ The following user actions are recorded:
- Failed second-factor authentication attempt ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16826) in GitLab 13.5)
- A user's personal access token was successfully created or revoked ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6)
- A failed attempt to create or revoke a user's personal access token ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/276921) in GitLab 13.6)
- Administrator added or removed ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/323905) in GitLab 14.1)
Instance events can also be accessed via the [Instance Audit Events API](../api/audit_events.md#instance-audit-events).

View File

@ -10,7 +10,7 @@ type: reference
This document describes a feature that allows you to disable some important but computationally
expensive parts of the application to relieve stress on the database during an ongoing downtime.
## `ci_queueing_disaster_recovery`
## `ci_queueing_disaster_recovery_disable_fair_scheduling`
This feature flag, if temporarily enabled, disables fair scheduling on shared runners.
This can help to reduce system resource usage on the `jobs/request` endpoint
@ -20,6 +20,16 @@ Side effects:
- In case of a large backlog of jobs, the jobs are processed in the order
they were put in the system, instead of balancing the jobs across many projects.
## `ci_queueing_disaster_recovery_disable_quota`
This feature flag, if temporarily enabled, disables enforcing CI minutes quota
on shared runners. This can help to reduce system resource usage on the
`jobs/request` endpoint by significantly reducing the computations being
performed.
Side effects:
- Projects which are out of quota will be run. This affects
only jobs created during the last hour, as prior jobs are canceled
by a periodic background worker (`StuckCiJobsWorker`).

View File

@ -780,6 +780,26 @@ Input type: `CiCdSettingsUpdateInput`
| <a id="mutationcicdsettingsupdateclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcicdsettingsupdateerrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.ciJobTokenScopeAddProject`
Input type: `CiJobTokenScopeAddProjectInput`
#### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationcijobtokenscopeaddprojectclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcijobtokenscopeaddprojectprojectpath"></a>`projectPath` | [`ID!`](#id) | The project that the CI job token scope belongs to. |
| <a id="mutationcijobtokenscopeaddprojecttargetprojectpath"></a>`targetProjectPath` | [`ID!`](#id) | The project to be added to the CI job token scope. |
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="mutationcijobtokenscopeaddprojectcijobtokenscope"></a>`ciJobTokenScope` | [`CiJobTokenScopeType`](#cijobtokenscopetype) | The CI job token's scope of access. |
| <a id="mutationcijobtokenscopeaddprojectclientmutationid"></a>`clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| <a id="mutationcijobtokenscopeaddprojecterrors"></a>`errors` | [`[String!]!`](#string) | Errors encountered during execution of the mutation. |
### `Mutation.clusterAgentDelete`
Input type: `ClusterAgentDeleteInput`

View File

@ -139,18 +139,8 @@ always take the latest Secret Detection artifact available.
### Enable Secret Detection via an automatic merge request **(ULTIMATE SELF)**
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4496) in GitLab 13.11.
> - [Deployed behind a feature flag](../../../user/feature_flags.md), enabled by default.
> - Enabled on GitLab.com.
> - Recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-configure-secret-detection-via-a-merge-request). **(ULTIMATE SELF)**
WARNING:
This feature might not be available to you. Check the **version history** note above for details.
There can be
[risks when disabling released features](../../../user/feature_flags.md#risks-when-disabling-released-features).
Refer to this feature's version history for more details.
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4496) in GitLab 13.11, behind a feature flag, enabled by default.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/329886) in GitLab 14.1.
To enable Secret Detection in a project, you can create a merge request
from the Security Configuration page.
@ -409,22 +399,3 @@ secret_detection:
variables:
GIT_DEPTH: 100
```
### Enable or disable Configure Secret Detection via a Merge Request
Configure Secret Detection via a Merge Request is under development but ready for production use.
It is deployed behind a feature flag that is **enabled by default**.
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
can opt to disable it.
To enable it:
```ruby
Feature.enable(:sec_secret_detection_ui_enable)
```
To disable it:
```ruby
Feature.disable(:sec_secret_detection_ui_enable)
```

View File

@ -48,6 +48,10 @@ Iteration cadences automate some common iteration tasks. They can be used to
automatically create iterations every 1, 2, 3, 4, or 6 weeks. They can also
be configured to automatically roll over incomplete issues to the next iteration.
With iteration cadences enabled, you must first
[create an iteration cadence](#create-an-iteration-cadence) before you can
[create an iteration](#create-an-iteration).
### Create an iteration cadence
Prerequisites:
@ -94,7 +98,7 @@ To create an iteration:
1. On the top bar, select **Menu > Groups** and find your group.
1. On the left sidebar, select **Issues > Iterations**.
1. Select the three-dot menu (**{ellipsis_v}**) > **Add iteration** for the cadence you want to add to.
1. Select **New iteration**.
1. Enter the title, a description (optional), a start date, and a due date.
1. Select **Create iteration**. The iteration details page opens.
@ -191,13 +195,13 @@ can enable it.
To enable it:
```ruby
Feature.enable(:iterations_cadences)
Feature.enable(:iteration_cadences)
```
To disable it:
```ruby
Feature.disable(:iterations_cadences)
Feature.disable(:iteration_cadences)
```
<!-- ## Troubleshooting

View File

@ -0,0 +1,32 @@
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Background migration for deleting orphaned deployments.
class DeleteOrphanedDeployments
include Database::MigrationHelpers
def perform(start_id, end_id)
orphaned_deployments
.where(id: start_id..end_id)
.delete_all
mark_job_as_succeeded(start_id, end_id)
end
def orphaned_deployments
define_batchable_model('deployments')
.where('NOT EXISTS (SELECT 1 FROM environments WHERE deployments.environment_id = environments.id)')
end
private
def mark_job_as_succeeded(*arguments)
Gitlab::Database::BackgroundMigrationJob.mark_all_as_succeeded(
self.class.name.demodulize,
arguments
)
end
end
end
end

View File

@ -189,7 +189,7 @@ module Gitlab
raise ArchiveError, 'Job is not finished yet' unless job.complete?
if trace_artifact
unsafe_trace_cleanup! if Feature.enabled?(:erase_traces_from_already_archived_jobs_when_archiving_again, job.project, default_enabled: :yaml)
unsafe_trace_cleanup!
raise AlreadyArchivedError, 'Could not archive again'
end

View File

@ -27,21 +27,18 @@ module Gitlab
# connect with Spamcheck
@endpoint_url = @endpoint_url.gsub(%r(^grpc:\/\/), '')
creds =
@creds =
if Rails.env.development? || Rails.env.test?
:this_channel_is_insecure
else
GRPC::Core::ChannelCredentials.new
end
@stub = ::Spamcheck::SpamcheckService::Stub.new(@endpoint_url, creds,
timeout: DEFAULT_TIMEOUT_SECS)
end
def issue_spam?(spam_issue:, user:, context: {})
issue = build_issue_protobuf(issue: spam_issue, user: user, context: context)
response = @stub.check_for_spam_issue(issue,
response = grpc_client.check_for_spam_issue(issue,
metadata: { 'authorization' =>
Gitlab::CurrentSettings.spam_check_api_key })
verdict = convert_verdict_to_gitlab_constant(response.verdict)
@ -100,6 +97,16 @@ module Gitlab
Google::Protobuf::Timestamp.new(seconds: ar_timestamp.to_time.to_i,
nanos: ar_timestamp.to_time.nsec)
end
def grpc_client
@grpc_client ||= ::Spamcheck::SpamcheckService::Stub.new(@endpoint_url, @creds,
interceptors: interceptors,
timeout: DEFAULT_TIMEOUT_SECS)
end
def interceptors
[Labkit::Correlation::GRPC::ClientInterceptor.instance]
end
end
end
end

View File

@ -169,6 +169,16 @@ module Gitlab
end
end
def deep_symbolized_access(data)
if data.is_a?(Array)
data.map(&method(:deep_symbolized_access))
elsif data.is_a?(Hash)
data.deep_symbolize_keys
else
data
end
end
def string_to_ip_object(str)
return unless str

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
module Serializers
# Make the resulting hash have deep symbolized keys
class SymbolizedJson
class << self
def dump(obj)
obj
end
def load(data)
return if data.nil?
Gitlab::Utils.deep_symbolized_access(data)
end
end
end
end

View File

@ -18403,9 +18403,15 @@ msgstr ""
msgid "JiraService|Events for %{noteable_model_name} are disabled."
msgstr ""
msgid "JiraService|Failed to load Jira issue statuses. View the issue in Jira, or reload the page."
msgstr ""
msgid "JiraService|Failed to load Jira issue. View the issue in Jira, or reload the page."
msgstr ""
msgid "JiraService|Failed to update Jira issue status. View the issue in Jira, or reload the page."
msgstr ""
msgid "JiraService|Fetch issue types for this Jira project"
msgstr ""
@ -18454,6 +18460,9 @@ msgstr ""
msgid "JiraService|Move to Done"
msgstr ""
msgid "JiraService|No available statuses"
msgstr ""
msgid "JiraService|Not all data may be displayed here. To view more details or make changes to this issue, go to %{linkStart}Jira%{linkEnd}."
msgstr ""
@ -28829,6 +28838,12 @@ msgstr ""
msgid "SecurityPolicies|Latest scan"
msgstr ""
msgid "SecurityPolicies|Network"
msgstr ""
msgid "SecurityPolicies|Policy type"
msgstr ""
msgid "SecurityPolicies|Scan execution"
msgstr ""

View File

@ -60,6 +60,7 @@ module DeprecationToolkitEnv
activerecord-6.0.3.7/lib/active_record/relation.rb
asciidoctor-2.0.12/lib/asciidoctor/extensions.rb
attr_encrypted-3.1.0/lib/attr_encrypted/adapters/active_record.rb
gitlab-labkit-0.18.0/lib/labkit/correlation/grpc/client_interceptor.rb
]
end

View File

@ -309,65 +309,6 @@ RSpec.describe 'Admin updates settings' do
end
end
context 'when Service Templates are enabled' do
before do
stub_feature_flags(disable_service_templates: false)
visit general_admin_application_settings_path
end
it 'shows Service Templates link' do
expect(page).to have_link('Service Templates')
end
context 'when the Slack Notifications Service template is active' do
before do
create(:service, :template, type: 'SlackService', active: true)
visit general_admin_application_settings_path
end
it 'change Slack Notifications Service template settings', :js do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
fill_in 'Webhook', with: 'http://localhost'
fill_in 'Username', with: 'test_user'
fill_in 'service[push_channel]', with: '#test_channel'
page.check('Notify only broken pipelines')
page.select 'All branches', from: 'Branches to be notified'
page.select 'Match any of the labels', from: 'Labels to be notified behavior'
check_all_events
click_button 'Save changes'
expect(page).to have_content 'Application settings saved successfully'
click_link 'Slack notifications'
expect(page.all('input[type=checkbox]')).to all(be_checked)
expect(find_field('Webhook').value).to eq 'http://localhost'
expect(find_field('Username').value).to eq 'test_user'
expect(find('[name="service[push_channel]"]').value).to eq '#test_channel'
end
it 'defaults Deployment events to false for chat notification template settings', :js do
first(:link, 'Service Templates').click
click_link 'Slack notifications'
expect(find_field('Deployment')).not_to be_checked
end
end
end
context 'When Service templates are disabled' do
before do
stub_feature_flags(disable_service_templates: true)
end
it 'does not show Service Templates link' do
expect(page).not_to have_link('Service Templates')
end
end
context 'Integration page', :js do
before do
visit integrations_admin_application_settings_path

View File

@ -31,10 +31,6 @@ RSpec.describe ProjectsFinder do
let(:use_cte) { true }
let(:finder) { described_class.new(params: params.merge(use_cte: use_cte), current_user: current_user, project_ids_relation: project_ids_relation) }
before do
stub_feature_flags(project_finder_similarity_sort: false)
end
subject { finder.execute }
shared_examples 'ProjectFinder#execute examples' do
@ -368,32 +364,28 @@ RSpec.describe ProjectsFinder do
end
describe 'sorting' do
let_it_be(:more_projects) do
[
create(:project, :internal, group: group, name: 'projA', path: 'projA'),
create(:project, :internal, group: group, name: 'projABC', path: 'projABC'),
create(:project, :internal, group: group, name: 'projAB', path: 'projAB')
]
end
context 'when sorting by a field' do
let(:params) { { sort: 'name_asc' } }
it { is_expected.to eq([internal_project, public_project]) }
it { is_expected.to eq(([internal_project, public_project] + more_projects).sort_by { |p| p[:name] }) }
end
context 'when sorting by similarity' do
let(:params) { { sort: 'similarity', search: 'pro' } }
let_it_be(:internal_project2) do
create(:project, :internal, group: group, name: 'projA', path: 'projA')
end
it { is_expected.to eq([more_projects[0], more_projects[2], more_projects[1]]) }
end
let_it_be(:internal_project3) do
create(:project, :internal, group: group, name: 'projABC', path: 'projABC')
end
let_it_be(:internal_project4) do
create(:project, :internal, group: group, name: 'projAB', path: 'projAB')
end
before do
stub_feature_flags(project_finder_similarity_sort: current_user)
end
it { is_expected.to eq([internal_project2, internal_project4, internal_project3]) }
context 'when no sort is provided' do
it { is_expected.to eq(([internal_project, public_project] + more_projects).sort_by { |p| p[:id] }.reverse) }
end
end

View File

@ -37,8 +37,6 @@
"hostname": { "type": ["string", "null"] },
"email": { "type": ["string", "null"] },
"stack": { "type": ["string", "null"] },
"modsecurity_enabled": { "type": ["boolean", "null"] },
"modsecurity_mode": {"type": ["integer", "0"]},
"host": {"type": ["string", "null"]},
"port": {"type": ["integer", "514"]},
"protocol": {"type": ["integer", "0"]},

View File

@ -0,0 +1,62 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Mutations::Ci::JobTokenScope::AddProject do
let(:mutation) do
described_class.new(object: nil, context: { current_user: current_user }, field: nil)
end
describe '#resolve' do
let_it_be(:project) { create(:project) }
let_it_be(:target_project) { create(:project) }
let(:target_project_path) { target_project.full_path }
subject do
mutation.resolve(project_path: project.full_path, target_project_path: target_project_path)
end
context 'when user is not logged in' do
let(:current_user) { nil }
it 'raises error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when user is logged in' do
let(:current_user) { create(:user) }
context 'when user does not have permissions to admin project' do
it 'raises error' do
expect { subject }.to raise_error(Gitlab::Graphql::Errors::ResourceNotAvailable)
end
end
context 'when user has permissions to admin project and read target project' do
before do
project.add_maintainer(current_user)
target_project.add_guest(current_user)
end
it 'adds target project to the job token scope' do
expect do
expect(subject).to include(ci_job_token_scope: be_present, errors: be_empty)
end.to change { Ci::JobToken::ProjectScopeLink.count }.by(1)
end
context 'when the service returns an error' do
let(:service) { double(:service) }
it 'returns an error response' do
expect(::Ci::JobTokenScope::AddProjectService).to receive(:new).with(project, current_user).and_return(service)
expect(service).to receive(:execute).with(target_project).and_return(ServiceResponse.error(message: 'The error message'))
expect(subject.fetch(:ci_job_token_scope)).to be_nil
expect(subject.fetch(:errors)).to include("The error message")
end
end
end
end
end
end

View File

@ -27,10 +27,6 @@ RSpec.describe Resolvers::ProjectsResolver do
private_group.add_developer(user)
end
before do
stub_feature_flags(project_finder_similarity_sort: false)
end
context 'when user is not logged in' do
let(:current_user) { nil }
@ -83,6 +79,7 @@ RSpec.describe Resolvers::ProjectsResolver do
context 'when user is logged in' do
let(:current_user) { user }
let(:visible_projecs) { [project, other_project, group_project, private_project, private_group_project] }
context 'when no filters are applied' do
it 'returns all visible projects for the user' do
@ -129,21 +126,24 @@ RSpec.describe Resolvers::ProjectsResolver do
end
end
context 'when sort is similarity' do
context 'when sorting' do
let_it_be(:named_project1) { create(:project, :public, name: 'projAB', path: 'projAB') }
let_it_be(:named_project2) { create(:project, :public, name: 'projABC', path: 'projABC') }
let_it_be(:named_project3) { create(:project, :public, name: 'projA', path: 'projA') }
let_it_be(:named_projects) { [named_project1, named_project2, named_project3] }
let(:filters) { { search: 'projA', sort: 'similarity' } }
context 'when sorting by similarity' do
let(:filters) { { search: 'projA', sort: 'similarity' } }
it 'returns projects in order of similarity to search' do
stub_feature_flags(project_finder_similarity_sort: current_user)
is_expected.to eq([named_project3, named_project1, named_project2])
it 'returns projects in order of similarity to search' do
is_expected.to eq([named_project3, named_project1, named_project2])
end
end
it 'returns projects in any order if flag is off' do
is_expected.to match_array([named_project3, named_project1, named_project2])
context 'when no sort is provided' do
it 'returns projects in descending order by id' do
is_expected.to match_array((visible_projecs + named_projects).sort_by { |p| p[:id]}.reverse )
end
end
end

View File

@ -0,0 +1,63 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::DeleteOrphanedDeployments, :migration, schema: 20210617161348 do
let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
let!(:environment) { table(:environments).create!(name: 'production', slug: 'production', project_id: project.id) }
let(:background_migration_jobs) { table(:background_migration_jobs) }
before do
create_deployment!(environment.id, project.id)
create_deployment!(non_existing_record_id, project.id)
end
it 'deletes only orphaned deployments' do
expect(valid_deployments.pluck(:id)).not_to be_empty
expect(orphaned_deployments.pluck(:id)).not_to be_empty
subject.perform(table(:deployments).minimum(:id), table(:deployments).maximum(:id))
expect(valid_deployments.pluck(:id)).not_to be_empty
expect(orphaned_deployments.pluck(:id)).to be_empty
end
it 'marks jobs as done' do
first_job = background_migration_jobs.create!(
class_name: 'DeleteOrphanedDeployments',
arguments: [table(:deployments).minimum(:id), table(:deployments).minimum(:id)]
)
second_job = background_migration_jobs.create!(
class_name: 'DeleteOrphanedDeployments',
arguments: [table(:deployments).maximum(:id), table(:deployments).maximum(:id)]
)
subject.perform(table(:deployments).minimum(:id), table(:deployments).minimum(:id))
expect(first_job.reload.status).to eq(Gitlab::Database::BackgroundMigrationJob.statuses[:succeeded])
expect(second_job.reload.status).to eq(Gitlab::Database::BackgroundMigrationJob.statuses[:pending])
end
private
def valid_deployments
table(:deployments).where('EXISTS (SELECT 1 FROM environments WHERE deployments.environment_id = environments.id)')
end
def orphaned_deployments
table(:deployments).where('NOT EXISTS (SELECT 1 FROM environments WHERE deployments.environment_id = environments.id)')
end
def create_deployment!(environment_id, project_id)
table(:deployments).create!(
environment_id: environment_id,
project_id: project_id,
ref: 'master',
tag: false,
sha: 'x',
status: 1,
iid: table(:deployments).count + 1)
end
end

View File

@ -7,7 +7,7 @@ RSpec.describe Gitlab::Spamcheck::Client do
let(:endpoint) { 'grpc://grpc.test.url' }
let_it_be(:user) { create(:user, organization: 'GitLab') }
let(:verdict_value) { nil }
let(:verdict_value) { ::Spamcheck::SpamVerdict::Verdict::ALLOW }
let(:error_value) { "" }
let(:attribs_value) do
@ -56,6 +56,13 @@ RSpec.describe Gitlab::Spamcheck::Client do
expect(subject).to eq([expected, { "monitorMode" => "false" }, ""])
end
end
it 'includes interceptors' do
expect_next_instance_of(::Gitlab::Spamcheck::Client) do |client|
expect(client).to receive(:interceptors).and_call_original
end
subject
end
end
describe "#build_issue_protobuf", :aggregate_failures do

View File

@ -351,6 +351,22 @@ RSpec.describe Gitlab::Utils do
end
end
describe '.deep_symbolized_access' do
let(:hash) do
{ "variables" => [{ "key" => "VAR1", "value" => "VALUE2" }] }
end
subject { described_class.deep_symbolized_access(hash) }
it 'allows to access hash keys with symbols' do
expect(subject[:variables]).to be_a(Array)
end
it 'allows to access array keys with symbols' do
expect(subject[:variables].first[:key]).to eq('VAR1')
end
end
describe '.try_megabytes_to_bytes' do
context 'when the size can be converted to megabytes' do
it 'returns the size in megabytes' do

View File

@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'fast_spec_helper'
RSpec.describe Serializers::SymbolizedJson do
describe '.dump' do
let(:obj) { { key: "value" } }
subject { described_class.dump(obj) }
it 'returns a hash' do
is_expected.to eq(obj)
end
end
describe '.load' do
let(:data_string) { '{"key":"value","variables":[{"key":"VAR1","value":"VALUE1"}]}' }
let(:data_hash) { Gitlab::Json.parse(data_string) }
context 'when loading a hash' do
subject { described_class.load(data_hash) }
it 'decodes a string' do
is_expected.to be_a(Hash)
end
it 'allows to access with symbols' do
expect(subject[:key]).to eq('value')
expect(subject[:variables].first[:key]).to eq('VAR1')
end
end
context 'when loading a nil' do
subject { described_class.load(nil) }
it 'returns nil' do
is_expected.to be_nil
end
end
end
end

View File

@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe ScheduleDeleteOrphanedDeployments, :sidekiq, schema: 20210617161348 do
let!(:namespace) { table(:namespaces).create!(name: 'user', path: 'user') }
let!(:project) { table(:projects).create!(namespace_id: namespace.id) }
let!(:environment) { table(:environments).create!(name: 'production', slug: 'production', project_id: project.id) }
let(:background_migration_jobs) { table(:background_migration_jobs) }
before do
create_deployment!(environment.id, project.id)
create_deployment!(environment.id, project.id)
create_deployment!(environment.id, project.id)
create_deployment!(non_existing_record_id, project.id)
create_deployment!(non_existing_record_id, project.id)
create_deployment!(non_existing_record_id, project.id)
create_deployment!(non_existing_record_id, project.id)
stub_const("#{described_class}::BATCH_SIZE", 1)
end
it 'schedules DeleteOrphanedDeployments background jobs' do
Sidekiq::Testing.fake! do
freeze_time do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(7)
table(:deployments).find_each do |deployment|
expect(described_class::MIGRATION).to be_scheduled_migration(deployment.id, deployment.id)
end
end
end
end
def create_deployment!(environment_id, project_id)
table(:deployments).create!(
environment_id: environment_id,
project_id: project_id,
ref: 'master',
tag: false,
sha: 'x',
status: 1,
iid: table(:deployments).count + 1)
end
end

View File

@ -2172,15 +2172,15 @@ RSpec.describe Ci::Build do
end
it 'contains options' do
expect(build.options).to eq(options.stringify_keys)
expect(build.options).to eq(options.symbolize_keys)
end
it 'allows to access with keys' do
it 'allows to access with symbolized keys' do
expect(build.options[:image]).to eq('ruby:2.7')
end
it 'allows to access with strings' do
expect(build.options['image']).to eq('ruby:2.7')
it 'rejects access with string keys' do
expect(build.options['image']).to be_nil
end
context 'when ci_build_metadata_config is set' do
@ -2189,7 +2189,7 @@ RSpec.describe Ci::Build do
end
it 'persist data in build metadata' do
expect(build.metadata.read_attribute(:config_options)).to eq(options.stringify_keys)
expect(build.metadata.read_attribute(:config_options)).to eq(options.symbolize_keys)
end
it 'does not persist data in build' do
@ -4715,9 +4715,9 @@ RSpec.describe Ci::Build do
describe '#read_metadata_attribute' do
let(:build) { create(:ci_build, :degenerated) }
let(:build_options) { { "key" => "build" } }
let(:metadata_options) { { "key" => "metadata" } }
let(:default_options) { { "key" => "default" } }
let(:build_options) { { key: "build" } }
let(:metadata_options) { { key: "metadata" } }
let(:default_options) { { key: "default" } }
subject { build.send(:read_metadata_attribute, :options, :config_options, default_options) }
@ -4752,8 +4752,8 @@ RSpec.describe Ci::Build do
describe '#write_metadata_attribute' do
let(:build) { create(:ci_build, :degenerated) }
let(:options) { { "key" => "new options" } }
let(:existing_options) { { "key" => "existing options" } }
let(:options) { { key: "new options" } }
let(:existing_options) { { key: "existing options" } }
subject { build.send(:write_metadata_attribute, :options, :config_options, options) }

View File

@ -29,7 +29,7 @@ RSpec.describe Ci::JobToken::Scope do
end
end
describe 'includes?' do
describe '#includes?' do
subject { scope.includes?(target_project) }
context 'when param is the project defining the scope' do

View File

@ -0,0 +1,78 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe 'CiJobTokenScopeAddProject' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:target_project) { create(:project) }
let(:variables) do
{
project_path: project.full_path,
target_project_path: target_project.full_path
}
end
let(:mutation) do
graphql_mutation(:ci_job_token_scope_add_project, variables) do
<<~QL
errors
ciJobTokenScope {
projects {
nodes {
path
}
}
}
QL
end
end
let(:mutation_response) { graphql_mutation_response(:ci_job_token_scope_add_project) }
context 'when unauthorized' do
let(:current_user) { create(:user) }
context 'when not a maintainer' do
before do
project.add_developer(current_user)
end
it 'has graphql errors' do
post_graphql_mutation(mutation, current_user: current_user)
expect(graphql_errors).not_to be_empty
end
end
end
context 'when authorized' do
let_it_be(:current_user) { project.owner }
before do
target_project.add_developer(current_user)
end
it 'adds the target project to the job token scope' do
expect do
post_graphql_mutation(mutation, current_user: current_user)
expect(response).to have_gitlab_http_status(:success)
expect(mutation_response.dig('ciJobTokenScope', 'projects', 'nodes')).not_to be_empty
end.to change { Ci::JobToken::Scope.new(project).includes?(target_project) }.from(false).to(true)
end
context 'when invalid target project is provided' do
before do
variables[:target_project_path] = 'unknown/project'
end
it 'has mutation errors' do
post_graphql_mutation(mutation, current_user: current_user)
expect(mutation_response['errors']).to contain_exactly(Ci::JobTokenScope::AddProjectService::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND)
end
end
end
end

View File

@ -30,43 +30,17 @@ RSpec.describe Ci::ArchiveTraceService, '#execute' do
create(:ci_build_trace_chunk, build: job)
end
context 'when the feature flag `erase_traces_from_already_archived_jobs_when_archiving_again` is enabled' do
before do
stub_feature_flags(erase_traces_from_already_archived_jobs_when_archiving_again: true)
end
it 'removes the trace chunks' do
expect { subject }.to change { job.trace_chunks.count }.to(0)
end
context 'when associated data does not exist' do
before do
job.job_artifacts_trace.file.remove!
end
it 'removes the trace artifact' do
expect { subject }.to change { job.reload.job_artifacts_trace }.to(nil)
end
end
it 'removes the trace chunks' do
expect { subject }.to change { job.trace_chunks.count }.to(0)
end
context 'when the feature flag `erase_traces_from_already_archived_jobs_when_archiving_again` is disabled' do
context 'when associated data does not exist' do
before do
stub_feature_flags(erase_traces_from_already_archived_jobs_when_archiving_again: false)
job.job_artifacts_trace.file.remove!
end
it 'does not remove the trace chunks' do
expect { subject }.not_to change { job.trace_chunks.count }
end
context 'when associated data does not exist' do
before do
job.job_artifacts_trace.file.remove!
end
it 'does not remove the trace artifact' do
expect { subject }.not_to change { job.reload.job_artifacts_trace }
end
it 'removes the trace artifact' do
expect { subject }.to change { job.reload.job_artifacts_trace }.to(nil)
end
end
end

View File

@ -33,11 +33,11 @@ RSpec.describe Ci::CreatePipelineService do
it 'uses the provided key' do
expected = {
'key' => 'a-key',
'paths' => ['logs/', 'binaries/'],
'policy' => 'pull-push',
'untracked' => true,
'when' => 'on_success'
key: 'a-key',
paths: ['logs/', 'binaries/'],
policy: 'pull-push',
untracked: true,
when: 'on_success'
}
expect(pipeline).to be_persisted
@ -66,10 +66,10 @@ RSpec.describe Ci::CreatePipelineService do
it 'builds a cache key' do
expected = {
'key' => /[a-f0-9]{40}/,
'paths' => ['logs/'],
'policy' => 'pull-push',
'when' => 'on_success'
key: /[a-f0-9]{40}/,
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success'
}
expect(pipeline).to be_persisted
@ -82,10 +82,10 @@ RSpec.describe Ci::CreatePipelineService do
it 'uses default cache key' do
expected = {
'key' => /default/,
'paths' => ['logs/'],
'policy' => 'pull-push',
'when' => 'on_success'
key: /default/,
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success'
}
expect(pipeline).to be_persisted
@ -115,10 +115,10 @@ RSpec.describe Ci::CreatePipelineService do
it 'builds a cache key' do
expected = {
'key' => /\$ENV_VAR-[a-f0-9]{40}/,
'paths' => ['logs/'],
'policy' => 'pull-push',
'when' => 'on_success'
key: /\$ENV_VAR-[a-f0-9]{40}/,
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success'
}
expect(pipeline).to be_persisted
@ -131,10 +131,10 @@ RSpec.describe Ci::CreatePipelineService do
it 'uses default cache key' do
expected = {
'key' => /\$ENV_VAR-default/,
'paths' => ['logs/'],
'policy' => 'pull-push',
'when' => 'on_success'
key: /\$ENV_VAR-default/,
paths: ['logs/'],
policy: 'pull-push',
when: 'on_success'
}
expect(pipeline).to be_persisted

View File

@ -39,8 +39,8 @@ RSpec.describe Ci::CreatePipelineService do
it 'creates a pipeline' do
expect(pipeline).to be_persisted
expect(pipeline.builds.first.options).to match(a_hash_including({
'before_script' => ['ls'],
'script' => [
before_script: ['ls'],
script: [
'echo doing my first step',
'echo doing step 1 of job 1',
'echo doing my last step'

View File

@ -104,7 +104,7 @@ RSpec.describe Ci::CreatePipelineService do
it 'saves dependencies' do
expect(test_a_build.options)
.to match(a_hash_including('dependencies' => ['build_a']))
.to match(a_hash_including(dependencies: ['build_a']))
end
it 'artifacts default to true' do

View File

@ -69,9 +69,9 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
it_behaves_like 'successful creation' do
let(:expected_bridge_options) do
{
'trigger' => {
'include' => [
{ 'local' => 'path/to/child.yml' }
trigger: {
include: [
{ local: 'path/to/child.yml' }
]
}
}
@ -149,9 +149,9 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
it_behaves_like 'successful creation' do
let(:expected_bridge_options) do
{
'trigger' => {
'include' => [
{ 'local' => 'path/to/child.yml' }
trigger: {
include: [
{ local: 'path/to/child.yml' }
]
}
}
@ -175,8 +175,8 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
it_behaves_like 'successful creation' do
let(:expected_bridge_options) do
{
'trigger' => {
'include' => 'path/to/child.yml'
trigger: {
include: 'path/to/child.yml'
}
}
end
@ -202,8 +202,8 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
it_behaves_like 'successful creation' do
let(:expected_bridge_options) do
{
'trigger' => {
'include' => ['path/to/child.yml', 'path/to/child2.yml']
trigger: {
include: ['path/to/child.yml', 'path/to/child2.yml']
}
}
end
@ -295,12 +295,12 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
it_behaves_like 'successful creation' do
let(:expected_bridge_options) do
{
'trigger' => {
'include' => [
trigger: {
include: [
{
'file' => 'path/to/child.yml',
'project' => 'my-namespace/my-project',
'ref' => 'master'
file: 'path/to/child.yml',
project: 'my-namespace/my-project',
ref: 'master'
}
]
}
@ -353,11 +353,11 @@ RSpec.describe Ci::CreatePipelineService, '#execute' do
it_behaves_like 'successful creation' do
let(:expected_bridge_options) do
{
'trigger' => {
'include' => [
trigger: {
include: [
{
'file' => ["path/to/child1.yml", "path/to/child2.yml"],
'project' => 'my-namespace/my-project'
file: ["path/to/child1.yml", "path/to/child2.yml"],
project: 'my-namespace/my-project'
}
]
}

View File

@ -1001,7 +1001,7 @@ RSpec.describe Ci::CreatePipelineService do
expect(pipeline.yaml_errors).not_to be_present
expect(pipeline).to be_persisted
expect(build).to be_kind_of(Ci::Build)
expect(build.options).to eq(config[:release].except(:stage, :only).with_indifferent_access)
expect(build.options).to eq(config[:release].except(:stage, :only))
expect(build).to be_persisted
end
end

View File

@ -0,0 +1,79 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Ci::JobTokenScope::AddProjectService do
let(:service) { described_class.new(project, current_user) }
let_it_be(:project) { create(:project) }
let_it_be(:target_project) { create(:project) }
let_it_be(:current_user) { create(:user) }
describe '#execute' do
subject(:result) { service.execute(target_project) }
shared_examples 'returns error' do |error|
it 'returns an error response', :aggregate_failures do
expect(result).to be_error
expect(result.message).to eq(error)
end
end
context 'when job token scope is disabled for the given project' do
before do
allow(project).to receive(:ci_job_token_scope_enabled?).and_return(false)
end
it_behaves_like 'returns error', 'Job token scope is disabled for this project'
end
context 'when user does not have permissions to edit the job token scope' do
it_behaves_like 'returns error', 'Insufficient permissions to modify the job token scope'
end
context 'when user has permissions to edit the job token scope' do
before do
project.add_maintainer(current_user)
end
context 'when target project is not provided' do
let(:target_project) { nil }
it_behaves_like 'returns error', Ci::JobTokenScope::AddProjectService::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND
end
context 'when target project is provided' do
context 'when user does not have permissions to read the target project' do
it_behaves_like 'returns error', Ci::JobTokenScope::AddProjectService::TARGET_PROJECT_UNAUTHORIZED_OR_UNFOUND
end
context 'when user has permissions to read the target project' do
before do
target_project.add_guest(current_user)
end
it 'adds the project to the scope' do
expect do
expect(result).to be_success
end.to change { Ci::JobToken::ProjectScopeLink.count }.by(1)
end
context 'when target project is already in scope' do
before do
create(:ci_job_token_project_scope_link,
source_project: project,
target_project: target_project)
end
it_behaves_like 'returns error', "Target project is already in the job token scope"
end
end
context 'when target project is same as the source project' do
let(:target_project) { project }
it_behaves_like 'returns error', "Validation failed: Target project can't be the same as the source project"
end
end
end
end
end

View File

@ -145,7 +145,7 @@ module Ci
context 'when using DEFCON mode that disables fair scheduling' do
before do
stub_feature_flags(ci_queueing_disaster_recovery: true)
stub_feature_flags(ci_queueing_disaster_recovery_disable_fair_scheduling: true)
end
context 'when all builds are pending' do

View File

@ -260,8 +260,9 @@ RSpec.configure do |config|
# tests, until we introduce it in user settings
stub_feature_flags(forti_token_cloud: false)
# This feature flag is by default disabled and used in disaster recovery mode
stub_feature_flags(ci_queueing_disaster_recovery: false)
# These feature flag are by default disabled and used in disaster recovery mode
stub_feature_flags(ci_queueing_disaster_recovery_disable_fair_scheduling: false)
stub_feature_flags(ci_queueing_disaster_recovery_disable_quota: false)
enable_rugged = example.metadata[:enable_rugged].present?