Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-06-01 12:09:36 +00:00
parent 8ef0366928
commit 8c438dd7a6
40 changed files with 753 additions and 90 deletions

View File

@ -144,18 +144,20 @@ review-stop:
.allure-report-base:
image:
name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.0.6
name: ${GITLAB_DEPENDENCY_PROXY}andrcuns/allure-report-publisher:0.3.1
entrypoint: [""]
stage: post-qa
variables:
GIT_STRATEGY: none
STORAGE_CREDENTIALS: $QA_ALLURE_REPORT_GCS_CREDENTIALS
GITLAB_AUTH_TOKEN: $GITLAB_QA_MR_ALLURE_REPORT_TOKEN
script:
- |
allure-report-publisher upload gcs \
--results-glob="qa/gitlab-qa-run-*/**/allure-results/*" \
--bucket="gitlab-qa-allure-reports" \
--prefix="$ALLURE_REPORT_PATH_PREFIX/$CI_COMMIT_REF_SLUG" \
--update-pr="comment" \
--copy-latest \
--color
@ -231,18 +233,20 @@ parallel-spec-reports:
allure-report-qa-smoke:
extends:
- .allure-report-base
- .review:rules:review-qa-smoke
- .review:rules:review-qa-smoke-report
needs: ["review-qa-smoke"]
variables:
ALLURE_REPORT_PATH_PREFIX: gitlab-review-smoke
ALLURE_JOB_NAME: review-qa-smoke
allure-report-qa-all:
extends:
- .allure-report-base
- .review:rules:review-qa-all
- .review:rules:review-qa-all-report
needs: ["review-qa-all"]
variables:
ALLURE_REPORT_PATH_PREFIX: gitlab-review-all
ALLURE_JOB_NAME: review-qa-all
danger-review:
extends:

View File

@ -1130,6 +1130,22 @@
changes: *code-qa-patterns
allow_failure: true
.review:rules:review-qa-smoke-report:
rules:
- <<: *if-not-ee
when: never
- <<: *if-dot-com-gitlab-org-merge-request
changes: *ci-review-patterns
when: always
- <<: *if-dot-com-gitlab-org-merge-request
changes: *frontend-patterns
allow_failure: true
when: always
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-qa-patterns
allow_failure: true
when: always
.review:rules:review-qa-all:
rules:
- <<: *if-not-ee
@ -1142,6 +1158,19 @@
changes: *qa-patterns
allow_failure: true
.review:rules:review-qa-all-report:
rules:
- <<: *if-not-ee
when: never
- <<: *if-dot-com-gitlab-org-merge-request
changes: *code-patterns
when: manual
allow_failure: true
- <<: *if-dot-com-gitlab-org-merge-request
changes: *qa-patterns
allow_failure: true
when: always
.review:rules:review-cleanup:
rules:
- <<: *if-not-ee

View File

@ -18,18 +18,27 @@ import {
* Translations & helpPagePaths for Static Security Configuration Page
*/
export const SAST_NAME = __('Static Application Security Testing (SAST)');
export const SAST_SHORT_NAME = s__('ciReport|SAST');
export const SAST_DESCRIPTION = __('Analyze your source code for known vulnerabilities.');
export const SAST_HELP_PATH = helpPagePath('user/application_security/sast/index');
export const SAST_CONFIG_HELP_PATH = helpPagePath('user/application_security/sast/index', {
anchor: 'configuration',
});
export const DAST_NAME = __('Dynamic Application Security Testing (DAST)');
export const DAST_SHORT_NAME = s__('ciReport|DAST');
export const DAST_DESCRIPTION = __('Analyze a review version of your web application.');
export const DAST_HELP_PATH = helpPagePath('user/application_security/dast/index');
export const DAST_CONFIG_HELP_PATH = helpPagePath('user/application_security/dast/index', {
anchor: 'enable-dast',
});
export const DAST_PROFILES_NAME = __('DAST Scans');
export const DAST_PROFILES_DESCRIPTION = __(
'Saved scan settings and target site settings which are reusable.',
);
export const DAST_PROFILES_HELP_PATH = helpPagePath('user/application_security/dast/index');
export const DAST_PROFILES_CONFIG_TEXT = s__('SecurityConfiguration|Manage scans');
export const SECRET_DETECTION_NAME = __('Secret Detection');
export const SECRET_DETECTION_DESCRIPTION = __(
@ -38,6 +47,10 @@ export const SECRET_DETECTION_DESCRIPTION = __(
export const SECRET_DETECTION_HELP_PATH = helpPagePath(
'user/application_security/secret_detection/index',
);
export const SECRET_DETECTION_CONFIG_HELP_PATH = helpPagePath(
'user/application_security/secret_detection/index',
{ anchor: 'configuration' },
);
export const DEPENDENCY_SCANNING_NAME = __('Dependency Scanning');
export const DEPENDENCY_SCANNING_DESCRIPTION = __(
@ -46,6 +59,10 @@ export const DEPENDENCY_SCANNING_DESCRIPTION = __(
export const DEPENDENCY_SCANNING_HELP_PATH = helpPagePath(
'user/application_security/dependency_scanning/index',
);
export const DEPENDENCY_SCANNING_CONFIG_HELP_PATH = helpPagePath(
'user/application_security/dependency_scanning/index',
{ anchor: 'configuration' },
);
export const CONTAINER_SCANNING_NAME = __('Container Scanning');
export const CONTAINER_SCANNING_DESCRIPTION = __(
@ -54,6 +71,10 @@ export const CONTAINER_SCANNING_DESCRIPTION = __(
export const CONTAINER_SCANNING_HELP_PATH = helpPagePath(
'user/application_security/container_scanning/index',
);
export const CONTAINER_SCANNING_CONFIG_HELP_PATH = helpPagePath(
'user/application_security/container_scanning/index',
{ anchor: 'configuration' },
);
export const COVERAGE_FUZZING_NAME = __('Coverage Fuzzing');
export const COVERAGE_FUZZING_DESCRIPTION = __(
@ -136,6 +157,83 @@ export const scanners = [
},
];
export const securityFeatures = [
{
name: SAST_NAME,
shortName: SAST_SHORT_NAME,
description: SAST_DESCRIPTION,
helpPath: SAST_HELP_PATH,
configurationHelpPath: SAST_CONFIG_HELP_PATH,
type: REPORT_TYPE_SAST,
// This field is currently hardcoded because SAST is always available.
// It will eventually come from the Backend, the progress is tracked in
// https://gitlab.com/gitlab-org/gitlab/-/issues/331622
available: true,
// This field is currently hardcoded because SAST can always be enabled via MR
// It will eventually come from the Backend, the progress is tracked in
// https://gitlab.com/gitlab-org/gitlab/-/issues/331621
canEnableByMergeRequest: true,
},
{
name: DAST_NAME,
shortName: DAST_SHORT_NAME,
description: DAST_DESCRIPTION,
helpPath: DAST_HELP_PATH,
configurationHelpPath: DAST_CONFIG_HELP_PATH,
type: REPORT_TYPE_DAST,
secondary: {
type: REPORT_TYPE_DAST_PROFILES,
name: DAST_PROFILES_NAME,
description: DAST_PROFILES_DESCRIPTION,
configurationText: DAST_PROFILES_CONFIG_TEXT,
},
},
{
name: DEPENDENCY_SCANNING_NAME,
description: DEPENDENCY_SCANNING_DESCRIPTION,
helpPath: DEPENDENCY_SCANNING_HELP_PATH,
configurationHelpPath: DEPENDENCY_SCANNING_CONFIG_HELP_PATH,
type: REPORT_TYPE_DEPENDENCY_SCANNING,
},
{
name: CONTAINER_SCANNING_NAME,
description: CONTAINER_SCANNING_DESCRIPTION,
helpPath: CONTAINER_SCANNING_HELP_PATH,
configurationHelpPath: CONTAINER_SCANNING_CONFIG_HELP_PATH,
type: REPORT_TYPE_CONTAINER_SCANNING,
},
{
name: SECRET_DETECTION_NAME,
description: SECRET_DETECTION_DESCRIPTION,
helpPath: SECRET_DETECTION_HELP_PATH,
configurationHelpPath: SECRET_DETECTION_CONFIG_HELP_PATH,
type: REPORT_TYPE_SECRET_DETECTION,
available: true,
},
{
name: API_FUZZING_NAME,
description: API_FUZZING_DESCRIPTION,
helpPath: API_FUZZING_HELP_PATH,
type: REPORT_TYPE_API_FUZZING,
},
{
name: COVERAGE_FUZZING_NAME,
description: COVERAGE_FUZZING_DESCRIPTION,
helpPath: COVERAGE_FUZZING_HELP_PATH,
type: REPORT_TYPE_COVERAGE_FUZZING,
},
];
export const complianceFeatures = [
{
name: LICENSE_COMPLIANCE_NAME,
description: LICENSE_COMPLIANCE_DESCRIPTION,
helpPath: LICENSE_COMPLIANCE_HELP_PATH,
type: REPORT_TYPE_LICENSE_COMPLIANCE,
},
];
export const featureToMutationMap = {
[REPORT_TYPE_SAST]: {
mutationId: 'configureSast',

View File

@ -0,0 +1,76 @@
<script>
import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import FeatureCard from './feature_card.vue';
export const i18n = {
compliance: s__('SecurityConfiguration|Compliance'),
securityTesting: s__('SecurityConfiguration|Security testing'),
securityTestingDescription: s__(
`SecurityConfiguration|The status of the tools only applies to the
default branch and is based on the %{linkStart}latest pipeline%{linkEnd}.
Once you've enabled a scan for the default branch, any subsequent feature
branch you create will include the scan.`,
),
securityConfiguration: __('Security Configuration'),
};
export default {
i18n,
components: {
GlTab,
GlLink,
GlTabs,
GlSprintf,
FeatureCard,
},
props: {
augmentedSecurityFeatures: {
type: Array,
required: true,
},
latestPipelinePath: {
type: String,
required: false,
default: '',
},
},
};
</script>
<template>
<article>
<header>
<h1 class="gl-font-size-h1">{{ $options.i18n.securityConfiguration }}</h1>
</header>
<gl-tabs content-class="gl-pt-6">
<gl-tab data-testid="security-testing-tab" :title="$options.i18n.securityTesting">
<div class="row">
<div class="col-lg-5">
<h2 class="gl-font-size-h2 gl-mt-0">{{ $options.i18n.securityTesting }}</h2>
<p
v-if="latestPipelinePath"
data-testid="latest-pipeline-info"
class="gl-line-height-20"
>
<gl-sprintf :message="$options.i18n.securityTestingDescription">
<template #link="{ content }">
<gl-link :href="latestPipelinePath">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</div>
<div class="col-lg-7">
<feature-card
v-for="feature in augmentedSecurityFeatures"
:key="feature.type"
:feature="feature"
class="gl-mb-6"
/>
</div>
</div>
</gl-tab>
</gl-tabs>
</article>
</template>

View File

@ -2,6 +2,9 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import SecurityConfigurationApp from './components/app.vue';
import { securityFeatures, complianceFeatures } from './components/constants';
import RedesignedSecurityConfigurationApp from './components/redesigned_app.vue';
import { augmentFeatures } from './utils';
export const initStaticSecurityConfiguration = (el) => {
if (!el) {
@ -14,8 +17,32 @@ export const initStaticSecurityConfiguration = (el) => {
defaultClient: createDefaultClient(),
});
const { projectPath, upgradePath } = el.dataset;
const { projectPath, upgradePath, features, latestPipelinePath } = el.dataset;
if (gon.features.securityConfigurationRedesign) {
const { augmentedSecurityFeatures } = augmentFeatures(
securityFeatures,
complianceFeatures,
features ? JSON.parse(features) : [],
);
return new Vue({
el,
apolloProvider,
provide: {
projectPath,
upgradePath,
},
render(createElement) {
return createElement(RedesignedSecurityConfigurationApp, {
props: {
augmentedSecurityFeatures,
latestPipelinePath,
},
});
},
});
}
return new Vue({
el,
apolloProvider,

View File

@ -0,0 +1,24 @@
export const augmentFeatures = (securityFeatures, complianceFeatures, features = []) => {
const featuresByType = features.reduce((acc, feature) => {
acc[feature.type] = feature;
return acc;
}, {});
const augmentFeature = (feature) => {
const augmented = {
...feature,
...featuresByType[feature.type],
};
if (augmented.secondary) {
augmented.secondary = { ...augmented.secondary, ...featuresByType[feature.secondary.type] };
}
return augmented;
};
return {
augmentedSecurityFeatures: securityFeatures.map((feature) => augmentFeature(feature)),
augmentedComplianceFeatures: complianceFeatures.map((feature) => augmentFeature(feature)),
};
};

View File

@ -435,9 +435,7 @@ export default {
</script>
<template>
<div v-if="isLoaded" class="mr-state-widget gl-mt-3">
<header
class="gl-overflow-hidden gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100"
>
<header class="gl-rounded-base gl-border-solid gl-border-1 gl-border-gray-100">
<mr-widget-alert-message v-if="shouldRenderCollaborationStatus" type="info">
{{ s__('mrWidget|Members who can merge are allowed to add commits.') }}
</mr-widget-alert-message>

View File

@ -7,6 +7,10 @@ module Projects
feature_category :static_application_security_testing
before_action only: [:show] do
push_frontend_feature_flag(:security_configuration_redesign, project, default_enabled: :yaml)
end
def show
render_403 unless can?(current_user, :read_security_configuration, project)
end

View File

@ -15,7 +15,7 @@ module Security
attr_reader :pipeline
def self.allowed_job_types
# Example return: [:sast, :dast, :dependency_scanning, :container_scanning, :license_management, :coverage_fuzzing]
# Example return: [:sast, :dast, :dependency_scanning, :container_scanning, :license_scanning, :coverage_fuzzing]
raise NotImplementedError, 'allowed_job_types must be overwritten to return an array of job types'
end

View File

@ -17,7 +17,7 @@
= link_to _('Forgot your password?'), new_password_path(:user)
%div
- if captcha_enabled? || captcha_on_login_required?
= recaptcha_tags
= recaptcha_tags nonce: content_security_policy_nonce
.submit-container.move-submit-down
= f.submit _('Sign in'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'sign_in_button' }

View File

@ -9,7 +9,7 @@
.devise-errors
= render 'devise/shared/error_messages', resource: resource
- if Gitlab::CurrentSettings.invisible_captcha_enabled
= invisible_captcha
= invisible_captcha nonce: true
.name.form-row
.col.form-group
= f.label :first_name, _('First name'), for: 'new_user_first_name', class: 'label-bold'
@ -59,7 +59,7 @@
%p.gl-field-hint.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length }
%div
- if show_recaptcha_sign_up?
= recaptcha_tags
= recaptcha_tags nonce: content_security_policy_nonce
.submit-container
= f.submit button_text, class: 'btn gl-button btn-confirm', data: { qa_selector: 'new_user_register_button' }
= render 'devise/shared/terms_of_service_notice', button_text: button_text

View File

@ -20,7 +20,7 @@
- if captcha_required?
.row.recaptcha
.col-sm-4
= recaptcha_tags
= recaptcha_tags nonce: content_security_policy_nonce
.row
.col-sm-12
= f.submit _('Create group'), class: "btn gl-button btn-confirm"

View File

@ -10,7 +10,7 @@
= hidden_field(resource_name, field, value: value)
= hidden_field_tag(:spam_log_id, spammable.spam_log.id)
-# The reCAPTCHA response value will be returned in the 'g-recaptcha-response' field
= recaptcha_tags script: script, callback: 'recaptchaDialogCallback' unless Rails.env.test?
= recaptcha_tags script: script, callback: 'recaptchaDialogCallback', nonce: content_security_policy_nonce unless Rails.env.test?
-# Fake the 'g-recaptcha-response' field in the test environment, so that the feature spec
-# can get to the (mocked) SpamVerdictService check.
= hidden_field_tag('g-recaptcha-response', 'abc123') if Rails.env.test?

View File

@ -40,7 +40,9 @@ module AuthorizedProjectUpdate
private
def use_primary_database
# no-op in CE, overriden in EE
if ::Gitlab::Database::LoadBalancing.enable?
::Gitlab::Database::LoadBalancing::Session.current.use_primary!
end
end
def project_authorizations_needs_refresh?(user)
@ -54,5 +56,3 @@ module AuthorizedProjectUpdate
end
end
end
AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker.prepend_mod_with('AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker')

View File

@ -15,10 +15,8 @@
- audit_reports
- authentication_and_authorization
- auto_devops
- auto_portfolio_mgmt
- backup_restore
- boards
- browser_performance
- chatops
- cloud_native_installation
- cluster_cost_management
@ -78,7 +76,6 @@
- license
- license_compliance
- live_preview
- load_testing
- logging
- memory
- merge_trains
@ -87,9 +84,11 @@
- mobile_signing_deployment
- navigation
- omnibus_package
- on_call_schedule_management
- onboarding
- package_registry
- pages
- performance_testing
- pipeline_authoring
- planning_analytics
- privacy_control_center
@ -111,6 +110,7 @@
- self_monitoring
- serverless
- service_desk
- sharding
- snippets
- source_code_management
- static_application_security_testing
@ -130,3 +130,4 @@
- web_firewall
- web_ide
- wiki
- workflow_automation

View File

@ -0,0 +1,8 @@
---
name: security_configuration_redesign
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62285
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/331614
milestone: '14.0'
type: development
group: group::static analysis
default_enabled: false

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
class ScheduleUpdateJiraTrackerDataDeploymentTypeBasedOnUrl < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'UpdateJiraTrackerDataDeploymentTypeBasedOnUrl'
DELAY_INTERVAL = 2.minutes.to_i
BATCH_SIZE = 2_500
disable_ddl_transaction!
def up
say "Scheduling #{MIGRATION} jobs"
queue_background_migration_jobs_by_range_at_intervals(
define_batchable_model('jira_tracker_data'),
MIGRATION,
DELAY_INTERVAL,
batch_size: BATCH_SIZE
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,26 @@
# frozen_string_literal: true
class RemoveSegmentSelectionsTable < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
drop_table :analytics_devops_adoption_segment_selections
end
def down
create_table :analytics_devops_adoption_segment_selections do |t|
t.references :segment, index: { name: 'index_on_segment_selections_segment_id' }, null: false, foreign_key: { to_table: :analytics_devops_adoption_segments, on_delete: :cascade }
t.bigint :group_id
t.bigint :project_id
t.index [:group_id, :segment_id], unique: true, name: 'index_on_segment_selections_group_id_segment_id'
t.index [:project_id, :segment_id], unique: true, name: 'index_on_segment_selections_project_id_segment_id'
t.timestamps_with_timezone
end
add_concurrent_foreign_key(:analytics_devops_adoption_segment_selections, :projects, column: :project_id, on_delete: :cascade)
add_concurrent_foreign_key(:analytics_devops_adoption_segment_selections, :namespaces, column: :group_id, on_delete: :cascade)
add_check_constraint :analytics_devops_adoption_segment_selections, '(project_id != NULL AND group_id IS NULL) OR (group_id != NULL AND project_id IS NULL)', 'segment_selection_project_id_or_group_id_required'
end
end

View File

@ -0,0 +1 @@
0f6019cc094481cafbf0c9bd42f53ae09034ea87e3f360b02f9ec03192caab9d

View File

@ -0,0 +1 @@
ee76ee2e2515c06b09fca23a77bdfb9532fa5d80fc3d5aba44a80d123b74cfa9

View File

@ -9110,25 +9110,6 @@ CREATE SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq
ALTER SEQUENCE analytics_cycle_analytics_project_value_streams_id_seq OWNED BY analytics_cycle_analytics_project_value_streams.id;
CREATE TABLE analytics_devops_adoption_segment_selections (
id bigint NOT NULL,
segment_id bigint NOT NULL,
group_id bigint,
project_id bigint,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
CONSTRAINT segment_selection_project_id_or_group_id_required CHECK ((((project_id <> NULL::bigint) AND (group_id IS NULL)) OR ((group_id <> NULL::bigint) AND (project_id IS NULL))))
);
CREATE SEQUENCE analytics_devops_adoption_segment_selections_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE analytics_devops_adoption_segment_selections_id_seq OWNED BY analytics_devops_adoption_segment_selections.id;
CREATE TABLE analytics_devops_adoption_segments (
id bigint NOT NULL,
last_recorded_at timestamp with time zone,
@ -19520,8 +19501,6 @@ ALTER TABLE ONLY analytics_cycle_analytics_project_stages ALTER COLUMN id SET DE
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams ALTER COLUMN id SET DEFAULT nextval('analytics_cycle_analytics_project_value_streams_id_seq'::regclass);
ALTER TABLE ONLY analytics_devops_adoption_segment_selections ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segment_selections_id_seq'::regclass);
ALTER TABLE ONLY analytics_devops_adoption_segments ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_segments_id_seq'::regclass);
ALTER TABLE ONLY analytics_devops_adoption_snapshots ALTER COLUMN id SET DEFAULT nextval('analytics_devops_adoption_snapshots_id_seq'::regclass);
@ -20611,9 +20590,6 @@ ALTER TABLE ONLY analytics_cycle_analytics_project_stages
ALTER TABLE ONLY analytics_cycle_analytics_project_value_streams
ADD CONSTRAINT analytics_cycle_analytics_project_value_streams_pkey PRIMARY KEY (id);
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
ADD CONSTRAINT analytics_devops_adoption_segment_selections_pkey PRIMARY KEY (id);
ALTER TABLE ONLY analytics_devops_adoption_segments
ADD CONSTRAINT analytics_devops_adoption_segments_pkey PRIMARY KEY (id);
@ -23899,12 +23875,6 @@ CREATE INDEX index_on_projects_lower_path ON projects USING btree (lower((path):
CREATE INDEX index_on_routes_lower_path ON routes USING btree (lower((path)::text));
CREATE UNIQUE INDEX index_on_segment_selections_group_id_segment_id ON analytics_devops_adoption_segment_selections USING btree (group_id, segment_id);
CREATE UNIQUE INDEX index_on_segment_selections_project_id_segment_id ON analytics_devops_adoption_segment_selections USING btree (project_id, segment_id);
CREATE INDEX index_on_segment_selections_segment_id ON analytics_devops_adoption_segment_selections USING btree (segment_id);
CREATE INDEX index_on_snapshots_segment_id_end_time ON analytics_devops_adoption_snapshots USING btree (segment_id, end_time);
CREATE INDEX index_on_snapshots_segment_id_recorded_at ON analytics_devops_adoption_snapshots USING btree (segment_id, recorded_at);
@ -25955,9 +25925,6 @@ ALTER TABLE ONLY project_group_links
ALTER TABLE ONLY epics
ADD CONSTRAINT fk_dccd3f98fc FOREIGN KEY (assignee_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
ADD CONSTRAINT fk_ded7fe0344 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY issues
ADD CONSTRAINT fk_df75a7c8b8 FOREIGN KEY (promoted_to_epic_id) REFERENCES epics(id) ON DELETE SET NULL;
@ -26027,9 +25994,6 @@ ALTER TABLE ONLY vulnerability_external_issue_links
ALTER TABLE ONLY epics
ADD CONSTRAINT fk_f081aa4489 FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
ADD CONSTRAINT fk_f1472b95f3 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY boards
ADD CONSTRAINT fk_f15266b5f9 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
@ -26120,9 +26084,6 @@ ALTER TABLE ONLY ip_restrictions
ALTER TABLE ONLY terraform_state_versions
ADD CONSTRAINT fk_rails_04f176e239 FOREIGN KEY (terraform_state_id) REFERENCES terraform_states(id) ON DELETE CASCADE;
ALTER TABLE ONLY analytics_devops_adoption_segment_selections
ADD CONSTRAINT fk_rails_053f00a9da FOREIGN KEY (segment_id) REFERENCES analytics_devops_adoption_segments(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_build_report_results
ADD CONSTRAINT fk_rails_056d298d48 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;

View File

@ -21,9 +21,8 @@ GitLab has several features based on receiving incoming emails:
## Requirements
It is **not** recommended to use an email address that receives any
messages not intended for the GitLab instance. Any incoming emails not intended
for GitLab receive a reject notice.
We recommend using an email address that receives **only** messages that are intended for
the GitLab instance. Any incoming emails not intended for GitLab receive a reject notice.
Handling incoming emails requires an [IMAP](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol)-enabled
email account. GitLab requires one of the following three strategies:
@ -131,6 +130,9 @@ list.
```shell
sudo gitlab-ctl reconfigure
# Needed when enabling or disabling for the first time but not for password changes.
# See https://gitlab.com/gitlab-org/gitlab-foss/-/issues/23560#note_61966788
sudo gitlab-ctl restart
```

View File

@ -21,7 +21,7 @@ The following are the requirements for providing your own Redis instance:
[requirements page](../../install/requirements.md).
- Standalone Redis or Redis high availability with Sentinel are supported. Redis
Cluster is not supported.
- Managed Redis from cloud providers such as AWS ElastiCache will work. If these
- Managed Redis from cloud providers such as AWS ElastiCache works fine. If these
services support high availability, be sure it is **not** the Redis Cluster type.
Note the Redis node's IP address or hostname, port, and password (if required).
@ -53,7 +53,7 @@ Note the Redis node's IP address or hostname, port, and password (if required).
This is the documentation for configuring a scalable Redis setup when
you have installed Redis all by yourself and not using the bundled one that
comes with the Omnibus packages, although using the Omnibus GitLab packages is
highly recommend as we optimize them specifically for GitLab, and we will take
highly recommend as we optimize them specifically for GitLab, and we take
care of upgrading Redis to the latest supported version.
Note also that you may elect to override all references to
@ -76,7 +76,7 @@ requirements:
(e.g., one from an internal network).
- Since Redis 3.2, you must define a password to receive external connections
(`requirepass`).
- If you are using Redis with Sentinel, you will also need to define the same
- If you are using Redis with Sentinel, you also need to define the same
password for the replica password definition (`masterauth`) in the same instance.
In addition, read the prerequisites as described in the
@ -176,7 +176,7 @@ primary with IP `10.0.0.1` (some settings might overlap with the primary):
sentinel monitor gitlab-redis 10.0.0.1 6379 2
## Define with `sentinel down-after-milliseconds` the time in `ms`
## that an unresponsive server will be considered down.
## that an unresponsive server is considered down.
sentinel down-after-milliseconds gitlab-redis 10000
## Define a value for `sentinel failover_timeout` in `ms`. This has multiple
@ -197,7 +197,7 @@ primary with IP `10.0.0.1` (some settings might overlap with the primary):
##
## * The maximum time a failover in progress waits for all the replicas to be
## reconfigured as replicas of the new primary. However even after this time
## the replicas will be reconfigured by the Sentinels anyway, but not with
## the replicas are reconfigured by the Sentinels anyway, but not with
## the exact parallel-syncs progression as specified.
sentinel failover_timeout 30000
```
@ -249,7 +249,7 @@ In a real world usage, you would also set up firewall rules to prevent
unauthorized access from other machines, and block traffic from the
outside ([Internet](https://gitlab.com/gitlab-org/gitlab-foss/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png)).
For this example, **Sentinel 1** will be configured in the same machine as the
For this example, **Sentinel 1** is configured in the same machine as the
**Redis Primary**, **Sentinel 2** and **Sentinel 3** in the same machines as the
**Replica 1** and **Replica 2** respectively.
@ -261,11 +261,11 @@ Here is a list and description of each **machine** and the assigned **IP**:
- `10.0.0.4`: GitLab application
Please note that after the initial configuration, if a failover is initiated
by the Sentinel nodes, the Redis nodes will be reconfigured and the **Primary**
will change permanently (including in `redis.conf`) from one node to the other,
by the Sentinel nodes, the Redis nodes are reconfigured and the **Primary**
changes permanently (including in `redis.conf`) from one node to the other,
until a new failover is initiated again.
The same thing will happen with `sentinel.conf` that will be overridden after the
The same thing happens with `sentinel.conf` that is overridden after the
initial execution, after any new sentinel node starts watching the **Primary**,
or a failover promotes a different **Primary** node.

View File

@ -6,6 +6,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Create an A/B test with `Experimentation Module`
NOTE:
We recommend using [GLEX](gitlab_experiment.md) for new experiments.
## Implement the experiment
1. Add the experiment to the `Gitlab::Experimentation::EXPERIMENTS` hash in

View File

@ -46,16 +46,10 @@ One is built into GitLab directly and has been around for a while (this is calle
to as `Gitlab::Experiment` -- GLEX for short.
Both approaches use [experiment](../feature_flags/index.md#experiment-type)
feature flags, and there is currently no strong suggestion to use one over the other.
feature flags. We recommend using GLEX rather than `Experimentation Module` for new experiments.
| Feature | `Experimentation Module` | GLEX |
| -------------------- |------------------------- | ---- |
| Record user grouping | Yes | No |
| Uses feature flags | Yes | Yes |
| Multivariate (A/B/n) | No | Yes |
- [Implementing an A/B experiment using `Experimentation Module`](experimentation.md)
- [Implementing an A/B/n experiment using GLEX](gitlab_experiment.md)
- [Implementing an A/B experiment using `Experimentation Module`](experimentation.md)
Historical Context: `Experimentation Module` was built iteratively with the needs that
appeared while implementing Growth sub-department experiments, while GLEX was built

View File

@ -105,6 +105,12 @@ Default client accepts two parameters: `resolvers` and `config`.
- `baseUrl` allows us to pass a URL for GraphQL endpoint different from our main endpoint (for example, `${gon.relative_url_root}/api/graphql`)
- `assumeImmutableResults` (set to `false` by default) - this setting, when set to `true`, assumes that every single operation on updating Apollo Cache is immutable. It also sets `freezeResults` to `true`, so any attempt on mutating Apollo Cache throws a console warning in development environment. Please ensure you're following the immutability pattern on cache update operations before setting this option to `true`.
- `fetchPolicy` determines how you want your component to interact with the Apollo cache. Defaults to "cache-first".
### Multiple client queries for the same object
If you are make multiple queries to the same Apollo client object you might encounter the following error: "Store error: the application attempted to write an object with no provided ID but the store already contains an ID of SomeEntity". [This error only should occur when you have made a query with an ID field for a portion, then made another that returns what would be the same object, but is missing the ID field.](https://github.com/apollographql/apollo-client/issues/2510#issue-271829009)
Please note this is being tracked in [this issue](https://gitlab.com/gitlab-org/gitlab/-/issues/326101) and the documentation will be updated when this issue is resolved.
## GraphQL Queries

View File

@ -132,6 +132,9 @@ the QA smoke suite.
You can also manually start the `review-qa-all`: it runs the full QA suite.
After the end-to-end test runs have finished, [Allure reports](https://github.com/allure-framework/allure2) are generated and published by
the `allure-report-qa-smoke` and `allure-report-qa-all` jobs. A comment with links to the reports are added to the merge request.
## Performance Metrics
On every [pipeline](https://gitlab.com/gitlab-org/gitlab/pipelines/125315730) in the `qa` stage, the

View File

@ -0,0 +1,42 @@
# frozen_string_literal: true
# rubocop: disable Style/Documentation
class Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl
# rubocop: disable Gitlab/NamespacedClass
class JiraTrackerData < ActiveRecord::Base
self.table_name = "jira_tracker_data"
self.inheritance_column = :_type_disabled
include ::Integrations::BaseDataFields
attr_encrypted :url, encryption_options
attr_encrypted :api_url, encryption_options
enum deployment_type: { unknown: 0, server: 1, cloud: 2 }, _prefix: :deployment
end
# rubocop: enable Gitlab/NamespacedClass
# https://rubular.com/r/uwgK7k9KH23efa
JIRA_CLOUD_REGEX = %r{^https?://[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.atlassian\.net$}ix.freeze
# rubocop: disable CodeReuse/ActiveRecord
def perform(start_id, end_id)
trackers_data = JiraTrackerData
.where(deployment_type: 'unknown')
.where(id: start_id..end_id)
cloud, server = trackers_data.partition { |tracker_data| tracker_data.url.match?(JIRA_CLOUD_REGEX) }
cloud_mappings = cloud.each_with_object({}) do |tracker_data, hash|
hash[tracker_data] = { deployment_type: 2 }
end
server_mapppings = server.each_with_object({}) do |tracker_data, hash|
hash[tracker_data] = { deployment_type: 1 }
end
mappings = cloud_mappings.merge(server_mapppings)
::Gitlab::Database::BulkUpdate.execute(%i[deployment_type], mappings)
end
# rubocop: enable CodeReuse/ActiveRecord
end

View File

@ -77,7 +77,7 @@ module Gitlab
end
def set_section_duration(duration)
@section_duration = Time.at(duration.to_i).strftime('%M:%S')
@section_duration = Time.at(duration.to_i).utc.strftime('%M:%S')
end
def flush_current_segment!

View File

@ -18,11 +18,11 @@ module Gitlab
'font_src' => "'self'",
'form_action' => "'self' https: http:",
'frame_ancestors' => "'self'",
'frame_src' => "'self' https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com",
'frame_src' => "'self' https://www.google.com/recaptcha/ https://www.recaptcha.net/ https://content.googleapis.com https://content-compute.googleapis.com https://content-cloudbilling.googleapis.com https://content-cloudresourcemanager.googleapis.com",
'img_src' => "'self' data: blob: http: https:",
'manifest_src' => "'self'",
'media_src' => "'self'",
'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.recaptcha.net https://apis.google.com",
'script_src' => "'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com",
'style_src' => "'self' 'unsafe-inline'",
'worker_src' => "'self'",
'object_src' => "'none'",

View File

@ -28955,6 +28955,9 @@ msgstr ""
msgid "SecurityConfiguration|By default, all analyzers are applied in order to cover all languages across your project, and only run if the language is detected in the Merge Request."
msgstr ""
msgid "SecurityConfiguration|Compliance"
msgstr ""
msgid "SecurityConfiguration|Configuration guide"
msgstr ""
@ -28994,6 +28997,9 @@ msgstr ""
msgid "SecurityConfiguration|Manage"
msgstr ""
msgid "SecurityConfiguration|Manage scans"
msgstr ""
msgid "SecurityConfiguration|More information"
msgstr ""
@ -29009,12 +29015,18 @@ msgstr ""
msgid "SecurityConfiguration|Security Control"
msgstr ""
msgid "SecurityConfiguration|Security testing"
msgstr ""
msgid "SecurityConfiguration|Status"
msgstr ""
msgid "SecurityConfiguration|Testing & Compliance"
msgstr ""
msgid "SecurityConfiguration|The status of the tools only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
msgstr ""
msgid "SecurityConfiguration|Using custom settings. You won't receive automatic updates on this variable. %{anchorStart}Restore to default%{anchorEnd}"
msgstr ""

View File

@ -28,19 +28,19 @@ RSpec.describe Security::SecurityJobsFinder do
end
end
context 'with combination of security jobs and license management jobs' do
context 'with combination of security jobs and license scanning jobs' do
let!(:sast_build) { create(:ci_build, :sast, pipeline: pipeline) }
let!(:container_scanning_build) { create(:ci_build, :container_scanning, pipeline: pipeline) }
let!(:dast_build) { create(:ci_build, :dast, pipeline: pipeline) }
let!(:secret_detection_build) { create(:ci_build, :secret_detection, pipeline: pipeline) }
let!(:license_management_build) { create(:ci_build, :license_management, pipeline: pipeline) }
let!(:license_scanning_build) { create(:ci_build, :license_scanning, pipeline: pipeline) }
it 'returns only the security jobs' do
is_expected.to include(sast_build)
is_expected.to include(container_scanning_build)
is_expected.to include(dast_build)
is_expected.to include(secret_detection_build)
is_expected.not_to include(license_management_build)
is_expected.not_to include(license_scanning_build)
end
end
end

View File

@ -0,0 +1,109 @@
import { GlTab, GlTabs } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import {
SAST_NAME,
SAST_SHORT_NAME,
SAST_DESCRIPTION,
SAST_HELP_PATH,
SAST_CONFIG_HELP_PATH,
} from '~/security_configuration/components/constants';
import FeatureCard from '~/security_configuration/components/feature_card.vue';
import RedesignedSecurityConfigurationApp, {
i18n,
} from '~/security_configuration/components/redesigned_app.vue';
import { REPORT_TYPE_SAST } from '~/vue_shared/security_reports/constants';
describe('NewApp component', () => {
let wrapper;
const createComponent = (propsData) => {
wrapper = extendedWrapper(
mount(RedesignedSecurityConfigurationApp, {
propsData,
}),
);
};
const findMainHeading = () => wrapper.find('h1');
const findSubHeading = () => wrapper.find('h2');
const findTab = () => wrapper.findComponent(GlTab);
const findTabs = () => wrapper.findAllComponents(GlTabs);
const findByTestId = (id) => wrapper.findByTestId(id);
const findFeatureCards = () => wrapper.findAllComponents(FeatureCard);
const securityFeaturesMock = [
{
name: SAST_NAME,
shortName: SAST_SHORT_NAME,
description: SAST_DESCRIPTION,
helpPath: SAST_HELP_PATH,
configurationHelpPath: SAST_CONFIG_HELP_PATH,
type: REPORT_TYPE_SAST,
available: true,
},
];
afterEach(() => {
wrapper.destroy();
});
describe('basic structure', () => {
beforeEach(() => {
createComponent({
augmentedSecurityFeatures: securityFeaturesMock,
});
});
it('renders main-heading with correct text', () => {
const mainHeading = findMainHeading();
expect(mainHeading).toExist();
expect(mainHeading.text()).toContain('Security Configuration');
});
it('renders GlTab Component ', () => {
expect(findTab()).toExist();
});
it('renders right amount of tabs with correct title ', () => {
expect(findTabs().length).toEqual(1);
});
it('renders security-testing tab', () => {
expect(findByTestId('security-testing-tab')).toExist();
});
it('renders sub-heading with correct text', () => {
const subHeading = findSubHeading();
expect(subHeading).toExist();
expect(subHeading.text()).toContain(i18n.securityTesting);
});
it('renders right amount of feature cards for given props with correct props', () => {
const cards = findFeatureCards();
expect(cards.length).toEqual(1);
expect(cards.at(0).props()).toEqual({ feature: securityFeaturesMock[0] });
});
it('should not show latest pipeline link when latestPipelinePath is not defined', () => {
expect(findByTestId('latest-pipeline-info').exists()).toBe(false);
});
});
describe('when given latestPipelinePath props', () => {
beforeEach(() => {
createComponent({
augmentedSecurityFeatures: securityFeaturesMock,
latestPipelinePath: 'test/path',
});
});
it('should show latest pipeline info with correct link when latestPipelinePath is defined', () => {
expect(findByTestId('latest-pipeline-info').exists()).toBe(true);
expect(findByTestId('latest-pipeline-info').text()).toMatchInterpolatedText(
i18n.securityTestingDescription,
);
expect(findByTestId('latest-pipeline-info').find('a').attributes('href')).toBe('test/path');
});
});
});

View File

@ -0,0 +1,81 @@
import { augmentFeatures } from '~/security_configuration/utils';
const mockSecurityFeatures = [
{
name: 'SAST',
type: 'SAST',
},
];
const mockComplianceFeatures = [
{
name: 'LICENSE_COMPLIANCE',
type: 'LICENSE_COMPLIANCE',
},
];
const mockFeaturesWithSecondary = [
{
name: 'DAST',
type: 'DAST',
secondary: {
type: 'DAST PROFILES',
name: 'DAST PROFILES',
},
},
];
const mockInvalidCustomFeature = [
{
foo: 'bar',
},
];
const mockValidCustomFeature = [
{
name: 'SAST',
type: 'SAST',
customfield: 'customvalue',
},
];
const expectedOutputDefault = {
augmentedSecurityFeatures: mockSecurityFeatures,
augmentedComplianceFeatures: mockComplianceFeatures,
};
const expectedOutputSecondary = {
augmentedSecurityFeatures: mockSecurityFeatures,
augmentedComplianceFeatures: mockFeaturesWithSecondary,
};
const expectedOutputCustomFeature = {
augmentedSecurityFeatures: mockValidCustomFeature,
augmentedComplianceFeatures: mockComplianceFeatures,
};
describe('returns an object with augmentedSecurityFeatures and augmentedComplianceFeatures when', () => {
it('given an empty array', () => {
expect(augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, [])).toEqual(
expectedOutputDefault,
);
});
it('given an invalid populated array', () => {
expect(
augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockInvalidCustomFeature),
).toEqual(expectedOutputDefault);
});
it('features have secondary key', () => {
expect(augmentFeatures(mockSecurityFeatures, mockFeaturesWithSecondary, [])).toEqual(
expectedOutputSecondary,
);
});
it('given a valid populated array', () => {
expect(
augmentFeatures(mockSecurityFeatures, mockComplianceFeatures, mockValidCustomFeature),
).toEqual(expectedOutputCustomFeature);
});
});

View File

@ -66,14 +66,16 @@ RSpec.describe Mutations::Ci::Runner::Update do
context 'with valid arguments' do
it 'updates runner with correct values' do
expected_attributes = mutation_params.except(:id)
expected_attributes = mutation_params.except(:id, :tag_list)
subject
expect(subject[:errors]).to be_empty
expect(subject[:runner]).to be_an_instance_of(Ci::Runner)
expect(subject[:runner]).to have_attributes(expected_attributes)
expect(subject[:runner].tag_list).to contain_exactly(*mutation_params[:tag_list])
expect(runner.reload).to have_attributes(expected_attributes)
expect(runner.tag_list).to contain_exactly(*mutation_params[:tag_list])
end
end

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::UpdateJiraTrackerDataDeploymentTypeBasedOnUrl do
let(:services_table) { table(:services) }
let(:service_jira_cloud) { services_table.create!(id: 1, type: 'JiraService') }
let(:service_jira_server) { services_table.create!(id: 2, type: 'JiraService') }
before do
jira_tracker_data = Class.new(ApplicationRecord) do
self.table_name = 'jira_tracker_data'
def self.encryption_options
{
key: Settings.attr_encrypted_db_key_base_32,
encode: true,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm'
}
end
attr_encrypted :url, encryption_options
attr_encrypted :api_url, encryption_options
attr_encrypted :username, encryption_options
attr_encrypted :password, encryption_options
end
stub_const('JiraTrackerData', jira_tracker_data)
end
let!(:tracker_data_cloud) { JiraTrackerData.create!(id: 1, service_id: service_jira_cloud.id, url: "https://test-domain.atlassian.net", deployment_type: 0) }
let!(:tracker_data_server) { JiraTrackerData.create!(id: 2, service_id: service_jira_server.id, url: "http://totally-not-jira-server.company.org", deployment_type: 0) }
subject { described_class.new.perform(tracker_data_cloud.id, tracker_data_server.id) }
it "changes unknown deployment_types based on URL" do
expect(JiraTrackerData.pluck(:deployment_type)).to eq([0, 0])
subject
expect(JiraTrackerData.pluck(:deployment_type)).to eq([2, 1])
end
end

View File

@ -76,10 +76,30 @@ RSpec.describe Gitlab::Ci::Ansi2json::Line do
end
describe '#set_section_duration' do
it 'sets and formats the section_duration' do
subject.set_section_duration(75)
shared_examples 'set_section_duration' do
it 'sets and formats the section_duration' do
subject.set_section_duration(75)
expect(subject.section_duration).to eq('01:15')
expect(subject.section_duration).to eq('01:15')
end
end
context 'with default timezone' do
it_behaves_like 'set_section_duration'
end
context 'with a timezone carrying minutes offset' do
before do
# The actual call by does use Time.at(...).utc that the following
# rubocop rule (Rails/TimeZone) suggests, but for this specific
# test's purposes we needed to mock at the Time.at call point.
# rubocop:disable Rails/TimeZone
allow(Time).to receive(:at).with(75).and_return(Time.at(75, in: '+05:30'))
# rubocop:enable Rails/TimeZone
end
it_behaves_like 'set_section_duration'
end
end

View File

@ -47,7 +47,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader do
settings = described_class.default_settings_hash
directives = settings['directives']
expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.recaptcha.net https://apis.google.com https://example.com")
expect(directives['script_src']).to eq("'strict-dynamic' 'self' 'unsafe-inline' 'unsafe-eval' https://www.google.com/recaptcha/ https://www.recaptcha.net https://apis.google.com https://example.com")
expect(directives['style_src']).to eq("'self' 'unsafe-inline' https://example.com")
end
end

View File

@ -0,0 +1,48 @@
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20210421163509_schedule_update_jira_tracker_data_deployment_type_based_on_url.rb')
RSpec.describe ScheduleUpdateJiraTrackerDataDeploymentTypeBasedOnUrl, :migration do
let(:services_table) { table(:services) }
let(:service_jira_cloud) { services_table.create!(id: 1, type: 'JiraService') }
let(:service_jira_server) { services_table.create!(id: 2, type: 'JiraService') }
before do
jira_tracker_data = Class.new(ApplicationRecord) do
self.table_name = 'jira_tracker_data'
def self.encryption_options
{
key: Settings.attr_encrypted_db_key_base_32,
encode: true,
mode: :per_attribute_iv,
algorithm: 'aes-256-gcm'
}
end
attr_encrypted :url, encryption_options
attr_encrypted :api_url, encryption_options
attr_encrypted :username, encryption_options
attr_encrypted :password, encryption_options
end
stub_const('JiraTrackerData', jira_tracker_data)
stub_const("#{described_class}::BATCH_SIZE", 1)
end
let!(:tracker_data_cloud) { JiraTrackerData.create!(id: 1, service_id: service_jira_cloud.id, url: "https://test-domain.atlassian.net", deployment_type: 0) }
let!(:tracker_data_server) { JiraTrackerData.create!(id: 2, service_id: service_jira_server.id, url: "http://totally-not-jira-server.company.org", deployment_type: 0) }
around do |example|
freeze_time { Sidekiq::Testing.fake! { example.run } }
end
it 'schedules background migration' do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
expect(described_class::MIGRATION).to be_scheduled_migration(tracker_data_cloud.id, tracker_data_cloud.id)
expect(described_class::MIGRATION).to be_scheduled_migration(tracker_data_server.id, tracker_data_server.id)
end
end

View File

@ -3,7 +3,8 @@
require 'spec_helper'
RSpec.describe AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker do
let(:project) { create(:project) }
let_it_be(:project) { create(:project) }
let(:user) { project.namespace.owner }
let(:start_user_id) { user.id }
let(:end_user_id) { start_user_id }
@ -64,6 +65,18 @@ RSpec.describe AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker do
execute_worker
end
context 'when load balancing is enabled' do
before do
allow(Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true)
end
it 'reads from the primary database' do
expect(Gitlab::Database::LoadBalancing::Session.current).to receive(:use_primary!)
execute_worker
end
end
end
end
end