Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-06-21 09:10:07 +00:00
parent 38eb51cae9
commit 3ab7e70965
103 changed files with 615 additions and 745 deletions

View file

@ -18,7 +18,9 @@ import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create
import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import createGqClient, { fetchPolicies } from '~/lib/graphql';
import { convertObjectPropsToCamelCase, urlParamsToObject } from '~/lib/utils/common_utils';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
// eslint-disable-next-line import/no-deprecated
import { urlParamsToObject } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import {
formatBoardLists,
@ -74,6 +76,7 @@ export default {
performSearch({ dispatch }) {
dispatch(
'setFilters',
// eslint-disable-next-line import/no-deprecated
convertObjectPropsToCamelCase(urlParamsToObject(window.location.search)),
);

View file

@ -7,13 +7,9 @@ import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import createDefaultClient from '~/lib/graphql';
import axios from '~/lib/utils/axios_utils';
import {
urlParamsToObject,
getUrlParamsArray,
parseBoolean,
convertObjectPropsToCamelCase,
} from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { parseBoolean, convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
// eslint-disable-next-line import/no-deprecated
import { mergeUrlParams, urlParamsToObject, getUrlParamsArray } from '~/lib/utils/url_utility';
import { ListType, flashAnimationDuration } from '../constants';
import eventHub from '../eventhub';
import ListAssignee from '../models/assignee';
@ -601,6 +597,7 @@ const boardsStore = {
getListIssues(list, emptyIssues = true) {
const data = {
// eslint-disable-next-line import/no-deprecated
...urlParamsToObject(this.filter.path),
page: list.page,
};

View file

@ -1,7 +1,7 @@
import { last } from 'lodash';
import recentSearchesStorageKeys from 'ee_else_ce/filtered_search/recent_searches_storage_keys';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { getParameterByName, getUrlParamsArray } from '~/lib/utils/common_utils';
import { getParameterByName } from '~/lib/utils/common_utils';
import {
ENTER_KEY_CODE,
BACKSPACE_KEY_CODE,
@ -12,7 +12,7 @@ import {
import { __ } from '~/locale';
import createFlash from '../flash';
import { addClassIfElementExists } from '../lib/utils/dom_utils';
import { visitUrl } from '../lib/utils/url_utility';
import { visitUrl, getUrlParamsArray } from '../lib/utils/url_utility';
import FilteredSearchContainer from './container';
import DropdownUtils from './dropdown_utils';
import eventHub from './event_hub';

View file

@ -125,34 +125,8 @@ const createFlash = function createFlash({
return flashContainer;
};
/*
* Flash banner supports different types of Flash configurations
* along with ability to provide actionConfig which can be used to show
* additional action or link on banner next to message
*
* @param {String} message Flash message text
* @param {String} type Type of Flash, it can be `notice`, `success`, `warning` or `alert` (default)
* @param {Object} parent Reference to parent element under which Flash needs to appear
* @param {Object} actionConfig Map of config to show action on banner
* @param {String} href URL to which action config should point to (default: '#')
* @param {String} title Title of action
* @param {Function} clickHandler Method to call when action is clicked on
* @param {Boolean} fadeTransition Boolean to determine whether to fade the alert out
*/
const deprecatedCreateFlash = function deprecatedCreateFlash(
message,
type,
parent,
actionConfig,
fadeTransition,
addBodyClass,
) {
return createFlash({ message, type, parent, actionConfig, fadeTransition, addBodyClass });
};
export {
createFlash as default,
deprecatedCreateFlash,
createFlashEl,
createAction,
hideFlash,

View file

@ -8,13 +8,9 @@ import {
import { toNumber, omit } from 'lodash';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import {
scrollToElement,
urlParamsToObject,
historyPushState,
getParameterByName,
} from '~/lib/utils/common_utils';
import { setUrlParams } from '~/lib/utils/url_utility';
import { scrollToElement, historyPushState, getParameterByName } from '~/lib/utils/common_utils';
// eslint-disable-next-line import/no-deprecated
import { setUrlParams, urlParamsToObject } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import initManualOrdering from '~/manual_ordering';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
@ -271,6 +267,7 @@ export default {
});
},
getQueryObject() {
// eslint-disable-next-line import/no-deprecated
return urlParamsToObject(window.location.search);
},
onPaginate(newPage) {

View file

@ -162,53 +162,6 @@ export const parseUrlPathname = (url) => {
return parsedUrl.pathname.charAt(0) === '/' ? parsedUrl.pathname : `/${parsedUrl.pathname}`;
};
const splitPath = (path = '') => path.replace(/^\?/, '').split('&');
export const urlParamsToArray = (path = '') =>
splitPath(path)
.filter((param) => param.length > 0)
.map((param) => {
const split = param.split('=');
return [decodeURI(split[0]), split[1]].join('=');
});
export const getUrlParamsArray = () => urlParamsToArray(window.location.search);
/**
* Accepts encoding string which includes query params being
* sent to URL.
*
* @param {string} path Query param string
*
* @returns {object} Query params object containing key-value pairs
* with both key and values decoded into plain string.
*/
export const urlParamsToObject = (path = '') =>
splitPath(path).reduce((dataParam, filterParam) => {
if (filterParam === '') {
return dataParam;
}
const data = dataParam;
let [key, value] = filterParam.split('=');
key = /%\w+/g.test(key) ? decodeURIComponent(key) : key;
const isArray = key.includes('[]');
key = key.replace('[]', '');
value = decodeURIComponent(value.replace(/\+/g, ' '));
if (isArray) {
if (!data[key]) {
data[key] = [];
}
data[key].push(value);
} else {
data[key] = value;
}
return data;
}, {});
export const isMetaKey = (e) => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
// Identify following special clicks

View file

@ -409,6 +409,55 @@ export function getWebSocketUrl(path) {
return `${getWebSocketProtocol()}//${joinPaths(window.location.host, path)}`;
}
const splitPath = (path = '') => path.replace(/^\?/, '').split('&');
export const urlParamsToArray = (path = '') =>
splitPath(path)
.filter((param) => param.length > 0)
.map((param) => {
const split = param.split('=');
return [decodeURI(split[0]), split[1]].join('=');
});
export const getUrlParamsArray = () => urlParamsToArray(window.location.search);
/**
* Accepts encoding string which includes query params being
* sent to URL.
*
* @param {string} path Query param string
*
* @returns {object} Query params object containing key-value pairs
* with both key and values decoded into plain string.
*
* @deprecated Please use `queryToObject(query, { gatherArrays: true });` instead. See https://gitlab.com/gitlab-org/gitlab/-/issues/328845
*/
export const urlParamsToObject = (path = '') =>
splitPath(path).reduce((dataParam, filterParam) => {
if (filterParam === '') {
return dataParam;
}
const data = dataParam;
let [key, value] = filterParam.split('=');
key = /%\w+/g.test(key) ? decodeURIComponent(key) : key;
const isArray = key.includes('[]');
key = key.replace('[]', '');
value = decodeURIComponent(value.replace(/\+/g, ' '));
if (isArray) {
if (!data[key]) {
data[key] = [];
}
data[key].push(value);
} else {
data[key] = value;
}
return data;
}, {});
/**
* Convert search query into an object
*

View file

@ -1,8 +1,9 @@
<script>
import { GlFilteredSearchToken } from '@gitlab/ui';
import { mapState } from 'vuex';
import { getParameterByName, urlParamsToObject } from '~/lib/utils/common_utils';
import { setUrlParams } from '~/lib/utils/url_utility';
import { getParameterByName } from '~/lib/utils/common_utils';
// eslint-disable-next-line import/no-deprecated
import { setUrlParams, urlParamsToObject } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import { SEARCH_TOKEN_TYPE, SORT_PARAM } from '~/members/constants';
import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
@ -64,6 +65,7 @@ export default {
},
},
created() {
// eslint-disable-next-line import/no-deprecated
const query = urlParamsToObject(window.location.search);
const tokens = this.tokens

View file

@ -1,7 +1,8 @@
<script>
import { GlTabs, GlTab, GlBadge } from '@gitlab/ui';
import { mapState } from 'vuex';
import { urlParamsToObject } from '~/lib/utils/common_utils';
// eslint-disable-next-line import/no-deprecated
import { urlParamsToObject } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { MEMBER_TYPES } from '../constants';
import MembersApp from './app.vue';
@ -55,6 +56,7 @@ export default {
},
}),
urlParams() {
// eslint-disable-next-line import/no-deprecated
return Object.keys(urlParamsToObject(window.location.search));
},
activeTabIndexCalculatedFromUrlParams() {

View file

@ -9,8 +9,8 @@ import {
} from '@gitlab/ui';
import { debounce } from 'lodash';
import { mapState, mapActions } from 'vuex';
import { urlParamsToObject } from '~/lib/utils/common_utils';
import { redirectTo } from '~/lib/utils/url_utility';
// eslint-disable-next-line import/no-deprecated
import { redirectTo, urlParamsToObject } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
const tooltipMessage = __('Searching by both author and message is currently not supported.');
@ -52,6 +52,7 @@ export default {
},
mounted() {
this.fetchAuthors();
// eslint-disable-next-line import/no-deprecated
const params = urlParamsToObject(window.location.search);
const { search: searchParam, author: authorParam } = params;
const commitsSearchInput = this.projectCommitsEl.querySelector('#commits-search');

View file

@ -2,17 +2,23 @@
module Integrations
class Pivotaltracker < Integration
include ActionView::Helpers::UrlHelper
API_ENDPOINT = 'https://www.pivotaltracker.com/services/v5/source_commits'
prop_accessor :token, :restrict_to_branch
validates :token, presence: true, if: :activated?
def title
'PivotalTracker'
'Pivotal Tracker'
end
def description
s_('PivotalTrackerService|Add commit messages as comments to PivotalTracker stories.')
s_('PivotalTrackerService|Add commit messages as comments to Pivotal Tracker stories.')
end
def help
docs_link = link_to _('Learn more.'), Rails.application.routes.url_helpers.help_page_url('user/project/integrations/pivotal_tracker'), target: '_blank', rel: 'noopener noreferrer'
s_('Add commit messages as comments to Pivotal Tracker stories. %{docs_link}').html_safe % { docs_link: docs_link.html_safe }
end
def self.to_param
@ -24,14 +30,15 @@ module Integrations
{
type: 'text',
name: 'token',
placeholder: s_('PivotalTrackerService|Pivotal Tracker API token.'),
help: s_('PivotalTrackerService|Pivotal Tracker API token. User must have access to the story. All comments are attributed to this user.'),
required: true
},
{
type: 'text',
name: 'restrict_to_branch',
placeholder: s_('PivotalTrackerService|Comma-separated list of branches which will be ' \
'automatically inspected. Leave blank to include all branches.')
title: 'Restrict to branch (optional)',
help: s_('PivotalTrackerService|Comma-separated list of branches to ' \
'automatically inspect. Leave blank to include all branches.')
}
]
end

View file

@ -156,7 +156,7 @@ class Member < ApplicationRecord
distinct_members = select('DISTINCT ON (user_id, invite_email) *')
.order('user_id, invite_email, access_level DESC, expires_at DESC, created_at ASC')
from(distinct_members, :members)
unscoped.from(distinct_members, :members)
end
scope :order_name_asc, -> { left_join_users.reorder(Gitlab::Database.nulls_last_order('users.name', 'ASC')) }

View file

@ -3,19 +3,16 @@
%section.settings.no-animate#js-protected-branches-settings{ class: ('expanded' if expanded), data: { qa_selector: 'protected_branches_settings_content' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
Protected branches
= s_("ProtectedBranch|Protected branches")
%button.btn.gl-button.btn-default.js-settings-toggle.qa-expand-protected-branches{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
Keep stable branches secure, and force developers to use merge requests. #{link_to "What are protected branches?", help_page_path("user/project/protected_branches")}
= s_("ProtectedBranch|Keep stable branches secure and force developers to use merge requests.")
= link_to s_("ProtectedBranch|What are protected branches?"), help_page_path("user/project/protected_branches")
.settings-content
%p
By default, protected branches protect your code and:
%ul
%li Allow only users with Maintainer #{link_to "permissions", help_page_path("user/permissions")} to create new protected branches.
%li Allow only users with Maintainer permissions to push code.
%li Prevent <strong>anyone</strong> from #{link_to "force-pushing", help_page_path('topics/git/git_rebase', anchor: 'force-push')} to the branch.
%li Prevent <strong>anyone</strong> from deleting the branch.
= s_("ProtectedBranch|By default, protected branches restrict who can modify the branch.")
= link_to s_("ProtectedBranch|Learn more."), help_page_path("user/project/protected_branches", anchor: "who-can-modify-a-protected-branch")
- if can? current_user, :admin_project, @project
= content_for :create_protected_branch

View file

@ -3,18 +3,16 @@
%section.settings.no-animate#js-protected-tags-settings{ class: ('expanded' if expanded), data: { qa_selector: 'protected_tag_settings_content' } }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
Protected tags
= s_("ProtectedTag|Protected tags")
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
Limit access to creating and updating tags. #{link_to "What are protected tags?", help_page_path("user/project/protected_tags")}
= s_("ProtectedTag|Limit access to creating and updating tags.")
= link_to s_("ProtectedTag|What are protected tags?"), help_page_path("user/project/protected_tags")
.settings-content
%p
By default, protected tags protect your code and:
%ul
%li Allow only users with Maintainer #{link_to "permissions", help_page_path("user/permissions")} to create tags.
%li Prevent <strong>anyone</strong> from updating tags.
%li Prevent <strong>anyone</strong> from deleting tags.
= s_("ProtectedTag|By default, protected branches restrict who can modify the tag.")
= link_to s_("ProtectedTag|Learn more."), help_page_path("user/project/protected_tags", anchor: "who-can-modify-a-protected-tag")
- if can? current_user, :admin_project, @project
= yield :create_protected_tag

View file

@ -36,16 +36,29 @@ module Gitlab
importer_class.new(object, project, client).execute
counter.increment
increment_counters(project)
info(project.id, message: 'importer finished')
rescue StandardError => e
error(project.id, e, hash)
end
# Counters incremented:
# - global (prometheus): for metrics in Grafana
# - project (redis): used in FinishImportWorker to report number of objects imported
def increment_counters(project)
counter.increment
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :imported)
end
def counter
@counter ||= Gitlab::Metrics.counter(counter_name, counter_description)
end
def object_type
raise NotImplementedError
end
# Returns the representation class to use for the object. This class must
# define the class method `from_json_hash`.
def representation_class

View file

@ -12,6 +12,7 @@ module WorkerAttributes
VALID_URGENCIES = [:high, :low, :throttled].freeze
VALID_DATA_CONSISTENCIES = [:always, :sticky, :delayed].freeze
DEFAULT_DATA_CONSISTENCY = :always
NAMESPACE_WEIGHTS = {
auto_devops: 2,
@ -110,7 +111,7 @@ module WorkerAttributes
end
def get_data_consistency
class_attributes[:data_consistency] || :always
class_attributes[:data_consistency] || DEFAULT_DATA_CONSISTENCY
end
def get_data_consistency_feature_flag_enabled?

View file

@ -13,6 +13,10 @@ module Gitlab
Importer::DiffNoteImporter
end
def object_type
:diff_note
end
def counter_name
:github_importer_imported_diff_notes
end

View file

@ -13,6 +13,10 @@ module Gitlab
Importer::IssueAndLabelLinksImporter
end
def object_type
:issue
end
def counter_name
:github_importer_imported_issues
end

View file

@ -13,6 +13,10 @@ module Gitlab
Importer::LfsObjectImporter
end
def object_type
:lfs_object
end
def counter_name
:github_importer_imported_lfs_objects
end

View file

@ -13,6 +13,10 @@ module Gitlab
Importer::NoteImporter
end
def object_type
:note
end
def counter_name
:github_importer_imported_notes
end

View file

@ -15,6 +15,10 @@ module Gitlab
Importer::PullRequestMergedByImporter
end
def object_type
:pull_request_merged_by
end
def counter_name
:github_importer_imported_pull_requests_merged_by
end

View file

@ -15,6 +15,10 @@ module Gitlab
Importer::PullRequestReviewImporter
end
def object_type
:pull_request_review
end
def counter_name
:github_importer_imported_pull_request_reviews
end

View file

@ -13,6 +13,10 @@ module Gitlab
Importer::PullRequestImporter
end
def object_type
:pull_request
end
def counter_name
:github_importer_imported_pull_requests
end

View file

@ -29,7 +29,8 @@ module Gitlab
info(
project.id,
message: "GitHub project import finished",
duration_s: duration.round(2)
duration_s: duration.round(2),
object_counts: ::Gitlab::GithubImport::ObjectCounter.summary(project)
)
end

View file

@ -1,9 +0,0 @@
---
redirect_to: '../../integration/saml.md'
remove_date: '2021-06-15'
---
This document was moved to [another location](../../integration/saml.md).
<!-- This redirect file can be deleted after 2021-06-15>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../../geo/replication/remove_geo_site.md'
remove_date: '2021-06-01'
---
This document was moved to [another location](../../geo/replication/remove_geo_site.md).
<!-- This redirect file can be deleted after 2021-06-01 -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -28,9 +28,9 @@ and all **secondary** nodes:
1. **Optional:** [Pause replication on each **secondary** node.](../index.md#pausing-and-resuming-replication)
1. Log into the **primary** node.
1. [Update GitLab on the **primary** node using Omnibus's Geo-specific steps](https://docs.gitlab.com/omnibus/update/README.html#geo-deployment).
1. [Update GitLab on the **primary** node using Omnibus](https://docs.gitlab.com/omnibus/update/#update-using-the-official-repositories).
1. Log into each **secondary** node.
1. [Update GitLab on each **secondary** node using Omnibus's Geo-specific steps](https://docs.gitlab.com/omnibus/update/README.html#geo-deployment).
1. [Update GitLab on each **secondary** node using Omnibus](https://docs.gitlab.com/omnibus/update/#update-using-the-official-repositories).
1. If you paused replication in step 1, [resume replication on each **secondary**](../index.md#pausing-and-resuming-replication)
1. [Test](#check-status-after-updating) **primary** and **secondary** nodes, and check version in each.

View file

@ -265,7 +265,7 @@ The following metrics are available:
| Metric | Type | Since | Description | Labels |
|:--------------------------------- |:--------- |:------------------------------------------------------------- |:-------------------------------------- |:--------------------------------------------------------- |
| `db_load_balancing_hosts` | Gauge | [12.3](https://gitlab.com/gitlab-org/gitlab/-/issues/13630) | Current number of load balancing hosts | |
| `sidekiq_load_balancing_count` | Counter | 13.11 | Sidekiq jobs using load balancing with data consistency set to :sticky or :delayed | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency`, `data_consistency`, `database_chosen` |
| `sidekiq_load_balancing_count` | Counter | 13.11 | Sidekiq jobs using load balancing with data consistency set to :sticky or :delayed | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency`, `data_consistency`, `load_balancing_strategy` |
## Database partitioning metrics **(PREMIUM SELF)**

View file

@ -955,13 +955,15 @@ Get Pipeline-Emails service settings for a project.
GET /projects/:id/services/pipelines-email
```
## PivotalTracker
## Pivotal Tracker
Project Management Software (Source Commits Endpoint)
Add commit messages as comments to Pivotal Tracker stories.
### Create/Edit PivotalTracker service
See also the [Pivotal Tracker service documentation](../user/project/integrations/pivotal_tracker.md).
Set PivotalTracker service for a project.
### Create/Edit Pivotal Tracker service
Set Pivotal Tracker service for a project.
```plaintext
PUT /projects/:id/services/pivotaltracker
@ -971,21 +973,21 @@ Parameters:
| Parameter | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `token` | string | true | The PivotalTracker token |
| `token` | string | true | The Pivotal Tracker token |
| `restrict_to_branch` | boolean | false | Comma-separated list of branches to automatically inspect. Leave blank to include all branches. |
| `push_events` | boolean | false | Enable notifications for push events |
### Delete PivotalTracker service
### Delete Pivotal Tracker service
Delete PivotalTracker service for a project.
Delete Pivotal Tracker service for a project.
```plaintext
DELETE /projects/:id/services/pivotaltracker
```
### Get PivotalTracker service settings
### Get Pivotal Tracker service settings
Get PivotalTracker service settings for a project.
Get Pivotal Tracker service settings for a project.
```plaintext
GET /projects/:id/services/pivotaltracker

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-01'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-01'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -47,7 +47,7 @@ The JWT's payload looks like this:
"project_id": "22", #
"project_path": "mygroup/myproject", #
"user_id": "42", # Id of the user executing the job
"user_login": "myuser" # GitLab @username
"user_login": "myuser", # GitLab @username
"user_email": "myuser@example.com", # Email of the user executing the job
"pipeline_id": "1212", #
"pipeline_source": "web", # Pipeline source, see: https://docs.gitlab.com/ee/ci/yaml/#common-if-clauses-for-rules

View file

@ -1,9 +0,0 @@
---
redirect_to: 'README.md#contributed-examples'
remove_date: '2021-06-01'
---
This document was moved to [another location](README.md#contributed-examples).
<!-- This redirect file can be deleted after 2021-06-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'README.md#contributed-examples'
remove_date: '2021-06-01'
---
This document was moved to [another location](README.md#contributed-examples).
<!-- This redirect file can be deleted after 2021-06-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'README.md#contributed-examples'
remove_date: '2021-05-26'
---
This document was moved to [another location](README.md#contributed-examples).
<!-- This redirect file can be deleted after 2021-05-26. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-01'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-01'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -37,4 +37,4 @@ For more information about why `gitlab` is used for the `Host`, see
You can also use any other Docker image available on [Docker Hub](https://hub.docker.com/u/gitlab).
The `gitlab` image can accept environment variables. For more details,
see the [Omnibus documentation](../../install/README.md).
see the [Omnibus documentation](../../install/index.md).

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-01'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -556,6 +556,3 @@ padding. The padding is added around the element, enlarging the screenshot area.
#### Live example
Please use `spec/docs_screenshots/container_registry_docs.rb` as a guide and as an example to create your own scripts.
<!-- This redirect file can be deleted after February 1, 2021. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-06-01'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-06-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +1,9 @@
---
redirect_to: 'https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/'
remove_date: '2021-06-01'
remove_date: '2022-03-01'
---
This document was moved to [another location](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/).
<!-- This redirect file can be deleted after 2021-06-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->
<!-- This redirect file can be deleted after 2022-03-01. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../fe_guide/dependencies.md'
remove_date: '2021-05-14'
---
This document was moved to [another location](../fe_guide/dependencies.md).
<!-- This redirect file can be deleted after <2021-05-14>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'usage_ping/index.md'
remove_date: '2021-05-23'
---
This document was moved to [another location](usage_ping/index.md).
<!-- This redirect file can be deleted after <2021-05-23>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-11'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-11. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-11'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-11. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../user/project/working_with_projects.md'
remove_date: '2021-05-05'
---
This document was moved to [another location](../user/project/working_with_projects.md).
<!-- This redirect file can be deleted after <2021-05-05>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../user/project/working_with_projects.md'
remove_date: '2021-05-04'
---
This document was moved to [another location](../user/project/working_with_projects.md).
<!-- This redirect file can be deleted after <2021-05-04>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-11'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-11. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'saml.md'
remove_date: '2021-06-15'
---
This document was moved to [another location](saml.md).
<!-- This redirect file can be deleted after 2021-06-15>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-11'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-11. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-11'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-11. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'integrations.md'
remove_date: '2021-05-03'
---
This document was moved to [another location](integrations.md).
<!-- This redirect file can be deleted after <2021-05-03>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-11'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-11. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'cicd_variables.md'
remove_date: '2021-05-15'
---
This document was moved to [another location](cicd_variables.md).
<!-- This redirect file can be deleted after 2021-05-15. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../topics/index.md'
remove_date: '2021-05-11'
---
This document was moved to [another location](../topics/index.md).
<!-- This redirect file can be deleted after 2021-05-11. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../../topics/gitlab_flow.md'
remove_date: '2021-05-16'
---
This document was moved to [another location](../../topics/gitlab_flow.md).
<!-- This redirect file can be deleted after <2021-05-16>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../../../topics/git/cherry_picking.md'
remove_date: '2021-06-01'
---
This document was moved to [another location](../../../topics/git/cherry_picking.md).
<!-- This redirect file can be deleted after <2021-06-01>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../../../topics/git/tags.md'
remove_date: '2021-06-01'
---
This document was moved to [another location](../../../topics/git/tags.md).
<!-- This redirect file can be deleted after <2021-06-01>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: 'index.md'
remove_date: '2021-05-11'
---
This document was moved to [another location](index.md).
<!-- This redirect file can be deleted after 2021-05-11. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../user_cohorts.md'
remove_date: '2021-06-01'
---
This document was moved to [another location](../user_cohorts.md).
<!-- This redirect file can be deleted after <2021-06-01>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -38,7 +38,7 @@ The following is an example of the Credentials inventory page:
If you see a **Revoke** button, you can revoke that user's PAT. Whether you see a **Revoke** button depends on the token state, and if an expiration date has been set. For more information, see the following table:
| Token state | [Token expiration enforced?](settings/account_and_limit_settings.md#do-not-enforce-personal-access-token-expiration) | Show Revoke button? | Comments |
| Token state | [Token expiration enforced?](settings/account_and_limit_settings.md#allow-expired-personal-access-tokens-to-be-used) | Show Revoke button? | Comments |
|-------------|------------------------|--------------------|----------------------------------------------------------------------------|
| Active | Yes | Yes | Allows administrators to revoke the PAT, such as for a compromised account |
| Active | No | Yes | Allows administrators to revoke the PAT, such as for a compromised account |

View file

@ -199,7 +199,7 @@ Once a lifetime for personal access tokens is set, GitLab:
allowed lifetime. Three hours is given to allow administrators to change the allowed lifetime,
or remove it, before revocation takes place.
## Enforce SSH key expiration **(ULTIMATE SELF)**
## Allow expired SSH keys to be used **(ULTIMATE SELF)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/250480) in GitLab 13.9.
> - [Enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/320970) in GitLab 14.0.
@ -215,15 +215,14 @@ To allow the use of expired SSH keys:
Disabling SSH key expiration immediately enables all expired SSH keys.
## Do not enforce Personal Access Token expiration **(ULTIMATE SELF)**
## Allow expired Personal Access Tokens to be used **(ULTIMATE SELF)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214723) in GitLab Ultimate 13.1.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/296881) in GitLab 13.9.
By default, expired personal access tokens (PATs) cannot be used.
You can allow the use of expired PATs with the following steps:
By default, expired personal access tokens (PATs) **are not usable**.
To do this:
To allow the use of expired PATs:
1. On the top bar, select **Menu >** **{admin}** **Admin**.
1. In the left sidebar, select **Settings > General**.

View file

@ -209,7 +209,6 @@ request contains a denied license. For more details, see [Enabling license appro
Prerequisites:
- At least one [security scanner job](#security-scanning-tools) must be enabled.
- Maintainer or Owner [role](../permissions.md#project-members-permissions).
For this approval group, you must set the number of approvals required to greater than zero.

View file

@ -16,7 +16,7 @@ If you're unable to use [OAuth2](../../api/oauth2.md), you can use a personal ac
In both cases, you authenticate with a personal access token in place of your password.
Personal access tokens are required when [Two-Factor Authentication (2FA)](account/two_factor_authentication.md) is enabled.
Personal access tokens are required when [Two-Factor Authentication (2FA)](account/two_factor_authentication.md) is enabled.
For examples of how you can use a personal access token to authenticate with the API, see the [API documentation](../../api/README.md#personalproject-access-tokens).
@ -82,7 +82,7 @@ Personal access tokens expire on the date you define, at midnight UTC.
- In GitLab Ultimate, administrators can
[limit the lifetime of personal access tokens](../admin_area/settings/account_and_limit_settings.md#limit-the-lifetime-of-personal-access-tokens).
- In GitLab Ultimate, administrators can choose whether or not to
[enforce personal access token expiration](../admin_area/settings/account_and_limit_settings.md#do-not-enforce-personal-access-token-expiration).
[enforce personal access token expiration](../admin_area/settings/account_and_limit_settings.md#allow-expired-personal-access-tokens-to-be-used).
## Create a personal access token programmatically **(FREE SELF)**
@ -104,10 +104,10 @@ To create a personal access token programmatically:
```
1. Run the following commands to reference the username, the token, and the scopes.
The token must be 20 characters long. The scopes must be valid and are visible
[in the source code](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/auth.rb).
For example, to create a token that belongs to a user with username `automation-bot`:
```ruby
@ -141,7 +141,7 @@ To revoke a token programmatically:
```shell
sudo gitlab-rails console
```
1. To revoke a token of `token-string-here123`, run the following commands:
```ruby

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

View file

@ -1,9 +0,0 @@
---
redirect_to: '../../../integration/jira/jira_cloud_configuration.md'
remove_date: '2021-06-18'
---
This document was moved to [another location](../../../integration/jira/jira_cloud_configuration.md).
<!-- This redirect file can be deleted after <2021-06-18>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../../../integration/jira/jira_server_configuration.md'
remove_date: '2021-06-18'
---
This document was moved to [another location](../../../integration/jira/jira_server_configuration.md).
<!-- This redirect file can be deleted after <2021-06-18>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -15,11 +15,9 @@ functionality to GitLab.
You can find the available integrations under your project's
**Settings > Integrations** page.
There are more than 20 integrations to integrate with. Click on the one that you
There are more than 20 integrations to integrate with. Select the one that you
want to configure.
![Integrations list](img/project_integrations_v13_3.png)
## Integrations listing
Click on the service links to see further configuration instructions and details.
@ -51,7 +49,7 @@ Click on the service links to see further configuration instructions and details
| [Microsoft Teams notifications](microsoft_teams.md) | Receive event notifications. | **{dotted-circle}** No |
| Packagist | Update your projects. | **{check-circle}** Yes |
| Pipelines emails | Send the pipeline status to a list of recipients by email. | **{dotted-circle}** No |
| PivotalTracker | Use PivotalTracker as the issue tracker. | **{dotted-circle}** No |
| [Pivotal Tracker](pivotal_tracker.md) | Add commit messages as comments to Pivotal Tracker stories. | **{dotted-circle}** No |
| [Prometheus](prometheus.md) | Monitor application metrics. | **{dotted-circle}** No |
| Pushover | Get real-time notifications on your device. | **{dotted-circle}** No |
| [Redmine](redmine.md) | Use Redmine as the issue tracker. | **{dotted-circle}** No |

View file

@ -0,0 +1,47 @@
---
stage: Create
group: Ecosystem
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Pivotal Tracker service **(FREE)**
This service adds commit messages as comments to Pivotal Tracker stories.
Once enabled, commit messages are checked for square brackets containing a hash mark followed by
the story ID (for example, `[#555]`). Every story ID found gets the commit comment added to it.
You can also close a story with a message containing: `fix [#555]`.
You can use any of these words:
- `fix`
- `fixed`
- `fixes`
- `complete`
- `completes`
- `completed`
- `finish`
- `finished`
- `finishes`
- `delivers`
Read more about the
[Source Commits endpoint](https://www.pivotaltracker.com/help/api/rest/v5#Source_Commits) in
the Pivotal Tracker API documentation.
See also the [Pivotal Tracker service API documentation](../../../api/services.md#pivotal-tracker).
## Set up Pivotal Tracker
In Pivotal Tracker, [create an API token](https://www.pivotaltracker.com/help/articles/api_token/).
Complete these steps in GitLab:
1. Go to the project you want to configure.
1. Go to the [Integrations page](overview.md#accessing-integrations).
1. Select **Pivotal Tracker**.
1. Ensure that the **Active** toggle is enabled.
1. Paste the token you generated in Pivotal Tracker.
1. (Optional) To restrict this setting to specific branches, list them in the **Restrict to branch**
field, separated with commas.
1. Select **Save changes** or optionally select **Test settings**.

View file

@ -1,9 +0,0 @@
---
redirect_to: 'drafts.md'
remove_date: '2021-05-19'
---
This document was moved to [another location](drafts.md).
<!-- This redirect file can be deleted after <2021-05-19>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -1,9 +0,0 @@
---
redirect_to: '../../ci/README.md'
remove_date: '2021-06-01'
---
This document is deprecated. See the latest [GitLab CI/CD documentation](../../ci/README.md).
<!-- This redirect file can be deleted after <2021-06-01>. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/#move-or-rename-a-page -->

View file

@ -13,24 +13,24 @@ further restrictions on certain branches, they can be protected.
The default branch for your repository is protected by default.
## Who can access a protected branch
## Who can modify a protected branch
When a branch is protected, the default behavior enforces
these restrictions on the branch.
When a branch is protected, the default behavior enforces these restrictions on the branch.
| Action | Who can do it |
|--------------------------|---------------|
| Protect a branch | Maintainers only. |
| Push to the branch | GitLab administrators and anyone with **Allowed** permission. (*) |
| Force push to the branch | No one. |
| Delete the branch | No one. |
| Action | Who can do it |
|:-------------------------|:------------------------------------------------------------------|
| Protect a branch | Maintainers only. |
| Push to the branch | GitLab administrators and anyone with **Allowed** permission. (1) |
| Force push to the branch | No one. |
| Delete the branch | No one. |
(*) Users with the Developer role can create a project in a group,
but might not be allowed to initially push to the [default branch](repository/branches/default.md).
1. Users with the Developer role can create a project in a group, but might not be allowed to
initially push to the [default branch](repository/branches/default.md).
### Set the default branch protection level
Administrators can set a default branch protection level in the [Admin Area](../admin_area/settings/visibility_and_access_controls.md#default-branch-protection).
Administrators can set a default branch protection level in the
[Admin Area](../admin_area/settings/visibility_and_access_controls.md#default-branch-protection).
## Configure a protected branch
@ -176,10 +176,10 @@ When enabled, members who are can push to this branch can also force push.
## Require Code Owner approval on a protected branch **(PREMIUM)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13251) in GitLab Premium 12.4.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/13251) in GitLab Premium 12.4.
> - [In](https://gitlab.com/gitlab-org/gitlab/-/issues/35097) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.5 and later, users and groups who can push to protected branches do not have to use a merge request to merge their feature branches. This means they can skip merge request approval rules.
You can require at least one approval by a
[Code Owner](code_owners.md) to a file changed by the
You can require at least one approval by a [Code Owner](code_owners.md) to a file changed by the
merge request.
To protect a new branch and enable Code Owner's approval:
@ -201,9 +201,6 @@ When enabled, all merge requests for these branches require approval
by a Code Owner per matched rule before they can be merged.
Additionally, direct pushes to the protected branch are denied if a rule is matched.
[In](https://gitlab.com/gitlab-org/gitlab/-/issues/35097) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.5 and later,
users and groups who can push to protected branches do not have to use a merge request to merge their feature branches. Thus, they can skip merge request approval rules.
## Run pipelines on protected branches
The permission to merge or push to protected branches defines

View file

@ -9,15 +9,24 @@ type: reference, howto
> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10356) in GitLab 9.1.
Protected tags allow control over who has permission to create tags as well as preventing accidental update or deletion once created. Each rule allows you to match either an individual tag name, or use wildcards to control multiple tags at once.
Protected tags:
- Allow control over who has permission to create tags.
- Prevent accidental update or deletion once created.
Each rule allows you to match either:
- An individual tag name.
- Wildcards to control multiple tags at once.
This feature evolved out of [protected branches](protected_branches.md)
## Overview
## Who can modify a protected tag
Protected tags prevent anyone from updating or deleting the tag, and prevent
creation of matching tags based on the permissions you have selected. By default,
anyone without Maintainer [permissions](../permissions.md) is prevented from creating tags.
By default:
- To create tags, you must have the [Maintainer role](../permissions.md).
- No one can update or delete tags.
## Configuring protected tags

View file

@ -10,7 +10,7 @@ type: reference, howto
NOTE:
Project access tokens are supported for self-managed instances on Free and above. They are also supported on GitLab SaaS Premium and above (excluding [trial licenses](https://about.gitlab.com/free-trial/)).
> - [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2587) in GitLab 13.0.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210181) in GitLab 13.0.
> - [Became available on GitLab.com](https://gitlab.com/gitlab-org/gitlab/-/issues/235765) in GitLab 13.5 for paid groups only.
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/235765) in GitLab 13.5.
@ -36,6 +36,9 @@ For examples of how you can use a project access token to authenticate with the
## Project bot users
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/210181) in GitLab 13.0.
> - [Excluded from license seat use](https://gitlab.com/gitlab-org/gitlab/-/issues/223695) in GitLab 13.5.
Project bot users are [GitLab-created service accounts](../../../subscriptions/self_managed/index.md#billable-users) and do not count as licensed seats.
For each project access token created, a bot user is created and added to the project with

View file

@ -7,8 +7,21 @@ module Gitlab
JobReplicaNotUpToDate = Class.new(StandardError)
def call(worker, job, _queue)
if requires_primary?(worker.class, job)
worker_class = worker.class
strategy = select_load_balancing_strategy(worker_class, job)
# This is consumed by ServerMetrics and StructuredLogger to emit metrics so we only
# make this available when load-balancing is actually utilized.
job['load_balancing_strategy'] = strategy.to_s if load_balancing_available?(worker_class)
case strategy
when :primary, :retry_primary
Session.current.use_primary!
when :retry_replica
raise JobReplicaNotUpToDate, "Sidekiq job #{worker_class} JID-#{job['jid']} couldn't use the replica."\
" Replica was not up to date."
when :replica
# this means we selected an up-to-date replica, but there is nothing to do in this case.
end
yield
@ -23,31 +36,27 @@ module Gitlab
Session.clear_session
end
def requires_primary?(worker_class, job)
return true unless worker_class.include?(::ApplicationWorker)
return true unless worker_class.utilizes_load_balancing_capabilities?
return true unless worker_class.get_data_consistency_feature_flag_enabled?
def select_load_balancing_strategy(worker_class, job)
return :primary unless load_balancing_available?(worker_class)
location = job['database_write_location'] || job['database_replica_location']
return true unless location
job_data_consistency = worker_class.get_data_consistency
job[:data_consistency] = job_data_consistency.to_s
return :primary unless location
if replica_caught_up?(location)
job[:database_chosen] = 'replica'
false
elsif job_data_consistency == :delayed && not_yet_retried?(job)
job[:database_chosen] = 'retry'
raise JobReplicaNotUpToDate, "Sidekiq job #{worker_class} JID-#{job['jid']} couldn't use the replica."\
" Replica was not up to date."
:replica
elsif worker_class.get_data_consistency == :delayed
not_yet_retried?(job) ? :retry_replica : :retry_primary
else
job[:database_chosen] = 'primary'
true
:primary
end
end
def load_balancing_available?(worker_class)
worker_class.include?(::ApplicationWorker) &&
worker_class.utilizes_load_balancing_capabilities? &&
worker_class.get_data_consistency_feature_flag_enabled?
end
def not_yet_retried?(job)
# if `retry_count` is `nil` it indicates that this job was never retried
# the `0` indicates that this is a first retry

View file

@ -22,6 +22,10 @@ module Gitlab
:pull_requests_comments
end
def object_type
:diff_note
end
def id_for_already_imported_cache(note)
note.id
end

View file

@ -18,6 +18,10 @@ module Gitlab
ImportIssueWorker
end
def object_type
:issue
end
def collection_method
:issues
end

View file

@ -18,6 +18,10 @@ module Gitlab
ImportLfsObjectWorker
end
def object_type
:lfs_object
end
def collection_method
:lfs_objects
end
@ -26,6 +30,8 @@ module Gitlab
lfs_objects = Projects::LfsPointers::LfsObjectDownloadListService.new(project).execute
lfs_objects.each do |object|
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
yield object
end
rescue StandardError => e

View file

@ -18,6 +18,10 @@ module Gitlab
ImportNoteWorker
end
def object_type
:note
end
def collection_method
:issues_comments
end

View file

@ -22,6 +22,10 @@ module Gitlab
pr.number
end
def object_type
:pull_request
end
def each_object_to_import
super do |pr|
update_repository if update_repository?(pr)

View file

@ -22,6 +22,10 @@ module Gitlab
:pull_requests_merged_by
end
def object_type
:pull_request_merged_by
end
def id_for_already_imported_cache(merge_request)
merge_request.id
end
@ -30,6 +34,8 @@ module Gitlab
project.merge_requests.with_state(:merged).find_each do |merge_request|
next if already_imported?(merge_request)
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
pull_request = client.pull_request(project.import_source, merge_request.iid)
yield(pull_request)

View file

@ -29,6 +29,10 @@ module Gitlab
:pull_request_reviews
end
def object_type
:pull_request_review
end
def id_for_already_imported_cache(review)
review.id
end
@ -57,6 +61,8 @@ module Gitlab
project.merge_requests.find_each do |merge_request|
next if already_imported?(merge_request)
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
client
.pull_request_reviews(project.import_source, merge_request.iid)
.each do |review|
@ -81,6 +87,8 @@ module Gitlab
page.objects.each do |review|
next if already_imported?(review)
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
review.merge_request_id = merge_request.id
yield(review)

View file

@ -0,0 +1,56 @@
# frozen_string_literal: true
# Count objects fetched or imported from Github in the context of the
# project being imported.
module Gitlab
module GithubImport
class ObjectCounter
OPERATIONS = %w[fetched imported].freeze
COUNTER_LIST_KEY = 'github-importer/object-counters-list/%{project}/%{operation}'
COUNTER_KEY = 'github-importer/object-counter/%{project}/%{operation}/%{object_type}'
CACHING = Gitlab::Cache::Import::Caching
class << self
def increment(project, object_type, operation)
validate_operation!(operation)
counter_key = COUNTER_KEY % { project: project.id, operation: operation, object_type: object_type }
add_counter_to_list(project, operation, counter_key)
CACHING.increment(counter_key)
end
def summary(project)
OPERATIONS.each_with_object({}) do |operation, result|
result[operation] = {}
CACHING
.values_from_set(counter_list_key(project, operation))
.sort
.each do |counter|
object_type = counter.split('/').last
result[operation][object_type] = CACHING.read_integer(counter)
end
end
end
private
def add_counter_to_list(project, operation, key)
CACHING.set_add(counter_list_key(project, operation), key)
end
def counter_list_key(project, operation)
COUNTER_LIST_KEY % { project: project.id, operation: operation }
end
def validate_operation!(operation)
unless operation.to_s.presence_in(OPERATIONS)
raise ArgumentError, "Operation must be #{OPERATIONS.join(' or ')}"
end
end
end
end
end
end

View file

@ -103,6 +103,8 @@ module Gitlab
page.objects.each do |object|
next if already_imported?(object)
Gitlab::GithubImport::ObjectCounter.increment(project, object_type, :fetched)
yield object
# We mark the object as imported immediately so we don't end up
@ -129,6 +131,10 @@ module Gitlab
Gitlab::Cache::Import::Caching.set_add(already_imported_cache_key, id)
end
def object_type
raise NotImplementedError
end
# Returns the ID to use for the cache used for checking if an object has
# already been imported or not.
#

View file

@ -68,7 +68,7 @@ module Gitlab
message = base_message(payload)
payload['database_chosen'] = job[:database_chosen] if job[:database_chosen]
payload['load_balancing_strategy'] = job['load_balancing_strategy'] if job['load_balancing_strategy']
if job_exception
payload['message'] = "#{message}: fail: #{payload['duration_s']} sec"

View file

@ -74,10 +74,10 @@ module Gitlab
@metrics[:sidekiq_elasticsearch_requests_total].increment(labels, get_elasticsearch_calls(instrumentation))
@metrics[:sidekiq_elasticsearch_requests_duration_seconds].observe(labels, get_elasticsearch_time(instrumentation))
if ::Gitlab::Database::LoadBalancing.enable? && job[:database_chosen]
with_load_balancing_settings(job) do |settings|
load_balancing_labels = {
database_chosen: job[:database_chosen],
data_consistency: job[:data_consistency]
load_balancing_strategy: settings['load_balancing_strategy'],
data_consistency: settings['worker_data_consistency']
}
@metrics[:sidekiq_load_balancing_count].increment(labels.merge(load_balancing_labels), 1)
@ -105,6 +105,15 @@ module Gitlab
private
def with_load_balancing_settings(job)
return unless ::Gitlab::Database::LoadBalancing.enable?
keys = %w[load_balancing_strategy worker_data_consistency]
return unless keys.all? { |k| job.key?(k) }
yield job.slice(*keys)
end
def get_thread_cputime
defined?(Process::CLOCK_THREAD_CPUTIME_ID) ? Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID) : 0
end

View file

@ -1984,6 +1984,9 @@ msgstr ""
msgid "Add commit messages as comments to Asana tasks. %{docs_link}"
msgstr ""
msgid "Add commit messages as comments to Pivotal Tracker stories. %{docs_link}"
msgstr ""
msgid "Add deploy freeze"
msgstr ""
@ -24239,13 +24242,13 @@ msgstr ""
msgid "Pipeline|with stages"
msgstr ""
msgid "PivotalTrackerService|Add commit messages as comments to PivotalTracker stories."
msgid "PivotalTrackerService|Add commit messages as comments to Pivotal Tracker stories."
msgstr ""
msgid "PivotalTrackerService|Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches."
msgid "PivotalTrackerService|Comma-separated list of branches to automatically inspect. Leave blank to include all branches."
msgstr ""
msgid "PivotalTrackerService|Pivotal Tracker API token."
msgid "PivotalTrackerService|Pivotal Tracker API token. User must have access to the story. All comments are attributed to this user."
msgstr ""
msgid "Plain diff"
@ -26339,12 +26342,21 @@ msgstr ""
msgid "ProtectedBranch|Branch:"
msgstr ""
msgid "ProtectedBranch|By default, protected branches restrict who can modify the branch."
msgstr ""
msgid "ProtectedBranch|Code owner approval"
msgstr ""
msgid "ProtectedBranch|Does not apply to users allowed to push. Optional sections are not enforced."
msgstr ""
msgid "ProtectedBranch|Keep stable branches secure and force developers to use merge requests."
msgstr ""
msgid "ProtectedBranch|Learn more."
msgstr ""
msgid "ProtectedBranch|Protect"
msgstr ""
@ -26354,6 +26366,9 @@ msgstr ""
msgid "ProtectedBranch|Protected branch (%{protected_branches_count})"
msgstr ""
msgid "ProtectedBranch|Protected branches"
msgstr ""
msgid "ProtectedBranch|Reject code pushes that change files listed in the CODEOWNERS file."
msgstr ""
@ -26369,6 +26384,9 @@ msgstr ""
msgid "ProtectedBranch|Toggle code owner approval"
msgstr ""
msgid "ProtectedBranch|What are protected branches?"
msgstr ""
msgid "ProtectedEnvironment|%{environment_name} will be writable for developers. Are you sure?"
msgstr ""
@ -26414,6 +26432,21 @@ msgstr ""
msgid "ProtectedEnvironment|Your environment has been unprotected"
msgstr ""
msgid "ProtectedTag|By default, protected branches restrict who can modify the tag."
msgstr ""
msgid "ProtectedTag|Learn more."
msgstr ""
msgid "ProtectedTag|Limit access to creating and updating tags."
msgstr ""
msgid "ProtectedTag|Protected tags"
msgstr ""
msgid "ProtectedTag|What are protected tags?"
msgstr ""
msgid "Protip: %{linkStart}Auto DevOps%{linkEnd} uses Kubernetes clusters to deploy your code!"
msgstr ""

View file

@ -68,8 +68,8 @@ then
fi
# Do not use 'README.md', instead use 'index.md'
# Number of 'README.md's as of 2020-10-13
NUMBER_READMES=28
# Number of 'README.md's as of 2021-06-21
NUMBER_READMES=15
FIND_READMES=$(find doc/ -name "README.md" | wc -l)
echo '=> Checking for new README.md files...'
echo

View file

@ -10,11 +10,11 @@ RSpec.describe 'User activates PivotalTracker' do
end
it 'activates service', :js do
visit_project_integration('PivotalTracker')
visit_project_integration('Pivotal Tracker')
fill_in('Token', with: 'verySecret')
click_test_then_save_integration(expect_test_to_fail: false)
expect(page).to have_content('PivotalTracker settings saved and active.')
expect(page).to have_content('Pivotal Tracker settings saved and active.')
end
end

View file

@ -1,5 +1,4 @@
import createFlash, {
deprecatedCreateFlash,
createFlashEl,
createAction,
hideFlash,
@ -125,120 +124,6 @@ describe('Flash', () => {
});
});
describe('deprecatedCreateFlash', () => {
const message = 'test';
const type = 'alert';
const parent = document;
const actionConfig = null;
const fadeTransition = false;
const addBodyClass = true;
const defaultParams = [message, type, parent, actionConfig, fadeTransition, addBodyClass];
describe('no flash-container', () => {
it('does not add to the DOM', () => {
const flashEl = deprecatedCreateFlash(message);
expect(flashEl).toBeNull();
expect(document.querySelector('.flash-alert')).toBeNull();
});
});
describe('with flash-container', () => {
beforeEach(() => {
setFixtures(
'<div class="content-wrapper js-content-wrapper"><div class="flash-container"></div></div>',
);
});
afterEach(() => {
document.querySelector('.js-content-wrapper').remove();
});
it('adds flash element into container', () => {
deprecatedCreateFlash(...defaultParams);
expect(document.querySelector('.flash-alert')).not.toBeNull();
expect(document.body.className).toContain('flash-shown');
});
it('adds flash into specified parent', () => {
deprecatedCreateFlash(
message,
type,
document.querySelector('.content-wrapper'),
actionConfig,
fadeTransition,
addBodyClass,
);
expect(document.querySelector('.content-wrapper .flash-alert')).not.toBeNull();
expect(document.querySelector('.content-wrapper').innerText.trim()).toEqual(message);
});
it('adds container classes when inside content-wrapper', () => {
deprecatedCreateFlash(...defaultParams);
expect(document.querySelector('.flash-text').className).toBe('flash-text');
expect(document.querySelector('.content-wrapper').innerText.trim()).toEqual(message);
});
it('does not add container when outside of content-wrapper', () => {
document.querySelector('.content-wrapper').className = 'js-content-wrapper';
deprecatedCreateFlash(...defaultParams);
expect(document.querySelector('.flash-text').className.trim()).toContain('flash-text');
});
it('removes element after clicking', () => {
deprecatedCreateFlash(...defaultParams);
document.querySelector('.flash-alert .js-close-icon').click();
expect(document.querySelector('.flash-alert')).toBeNull();
expect(document.body.className).not.toContain('flash-shown');
});
describe('with actionConfig', () => {
it('adds action link', () => {
const newActionConfig = { title: 'test' };
deprecatedCreateFlash(
message,
type,
parent,
newActionConfig,
fadeTransition,
addBodyClass,
);
expect(document.querySelector('.flash-action')).not.toBeNull();
});
it('calls actionConfig clickHandler on click', () => {
const newActionConfig = {
title: 'test',
clickHandler: jest.fn(),
};
deprecatedCreateFlash(
message,
type,
parent,
newActionConfig,
fadeTransition,
addBodyClass,
);
document.querySelector('.flash-action').click();
expect(newActionConfig.clickHandler).toHaveBeenCalled();
});
});
});
});
describe('createFlash', () => {
const message = 'test';
const type = 'alert';

View file

@ -26,42 +26,6 @@ describe('common_utils', () => {
});
});
describe('urlParamsToArray', () => {
it('returns empty array for empty querystring', () => {
expect(commonUtils.urlParamsToArray('')).toEqual([]);
});
it('should decode params', () => {
expect(commonUtils.urlParamsToArray('?label_name%5B%5D=test')[0]).toBe('label_name[]=test');
});
it('should remove the question mark from the search params', () => {
const paramsArray = commonUtils.urlParamsToArray('?test=thing');
expect(paramsArray[0][0]).not.toBe('?');
});
});
describe('urlParamsToObject', () => {
it('parses path for label with trailing +', () => {
expect(commonUtils.urlParamsToObject('label_name[]=label%2B', {})).toEqual({
label_name: ['label+'],
});
});
it('parses path for milestone with trailing +', () => {
expect(commonUtils.urlParamsToObject('milestone_title=A%2B', {})).toEqual({
milestone_title: 'A+',
});
});
it('parses path for search terms with spaces', () => {
expect(commonUtils.urlParamsToObject('search=two+words', {})).toEqual({
search: 'two words',
});
});
});
describe('handleLocationHash', () => {
beforeEach(() => {
jest.spyOn(window.document, 'getElementById');

View file

@ -650,6 +650,45 @@ describe('URL utility', () => {
});
});
describe('urlParamsToArray', () => {
it('returns empty array for empty querystring', () => {
expect(urlUtils.urlParamsToArray('')).toEqual([]);
});
it('should decode params', () => {
expect(urlUtils.urlParamsToArray('?label_name%5B%5D=test')[0]).toBe('label_name[]=test');
});
it('should remove the question mark from the search params', () => {
const paramsArray = urlUtils.urlParamsToArray('?test=thing');
expect(paramsArray[0][0]).not.toBe('?');
});
});
describe('urlParamsToObject', () => {
it('parses path for label with trailing +', () => {
// eslint-disable-next-line import/no-deprecated
expect(urlUtils.urlParamsToObject('label_name[]=label%2B', {})).toEqual({
label_name: ['label+'],
});
});
it('parses path for milestone with trailing +', () => {
// eslint-disable-next-line import/no-deprecated
expect(urlUtils.urlParamsToObject('milestone_title=A%2B', {})).toEqual({
milestone_title: 'A+',
});
});
it('parses path for search terms with spaces', () => {
// eslint-disable-next-line import/no-deprecated
expect(urlUtils.urlParamsToObject('search=two+words', {})).toEqual({
search: 'two words',
});
});
});
describe('queryToObject', () => {
it.each`
case | query | options | result

View file

@ -5,6 +5,14 @@ require 'spec_helper'
RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
let(:middleware) { described_class.new }
let(:load_balancer) { double.as_null_object }
let(:has_replication_lag) { false }
before do
allow(::Gitlab::Database::LoadBalancing).to receive_message_chain(:proxy, :load_balancer).and_return(load_balancer)
allow(load_balancer).to receive(:select_up_to_date_host).and_return(!has_replication_lag)
end
after do
Gitlab::Database::LoadBalancing::Session.clear_session
end
@ -39,7 +47,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
end
end
shared_examples_for 'replica is up to date' do |location, data_consistency|
shared_examples_for 'replica is up to date' do |location|
it 'does not stick to the primary', :aggregate_failures do
expect(middleware).to receive(:replica_caught_up?).with(location).and_return(true)
@ -47,13 +55,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
expect(Gitlab::Database::LoadBalancing::Session.current.use_primary?).not_to be_truthy
end
expect(job[:database_chosen]).to eq('replica')
end
it "updates job hash with data_consistency :#{data_consistency}" do
middleware.call(worker, job, double(:queue)) do
expect(job).to include(data_consistency: data_consistency.to_s)
end
expect(job['load_balancing_strategy']).to eq('replica')
end
end
@ -75,7 +77,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
allow(middleware).to receive(:replica_caught_up?).and_return(true)
end
it_behaves_like 'replica is up to date', '0/D525E3A8', data_consistency
it_behaves_like 'replica is up to date', '0/D525E3A8'
end
context 'when database primary location is set' do
@ -85,13 +87,13 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
allow(middleware).to receive(:replica_caught_up?).and_return(true)
end
it_behaves_like 'replica is up to date', '0/D525E3A8', data_consistency
it_behaves_like 'replica is up to date', '0/D525E3A8'
end
context 'when database location is not set' do
let(:job) { { 'job_id' => 'a180b47c-3fd6-41b8-81e9-34da61c3400e' } }
it_behaves_like 'stick to the primary', nil
it_behaves_like 'stick to the primary'
end
end
@ -124,10 +126,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
include_examples 'sticks based on data consistency', :delayed
context 'when replica is not up to date' do
before do
allow(::Gitlab::Database::LoadBalancing).to receive_message_chain(:proxy, :load_balancer, :release_host)
allow(::Gitlab::Database::LoadBalancing).to receive_message_chain(:proxy, :load_balancer, :select_up_to_date_host).and_return(false)
end
let(:has_replication_lag) { true }
around do |example|
with_sidekiq_server_middleware do |chain|
@ -143,7 +142,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
end.to raise_error(Sidekiq::JobRetry::Skip)
expect(job['error_class']).to eq('Gitlab::Database::LoadBalancing::SidekiqServerMiddleware::JobReplicaNotUpToDate')
expect(job[:database_chosen]).to eq('retry')
expect(job['load_balancing_strategy']).to eq('retry_replica')
end
end
@ -154,7 +153,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
end.to raise_error(Sidekiq::JobRetry::Skip)
process_job(job)
expect(job[:database_chosen]).to eq('primary')
expect(job['load_balancing_strategy']).to eq('retry_primary')
end
end
@ -163,7 +162,7 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
stub_feature_flags(sidekiq_load_balancing_rotate_up_to_date_replica: false)
end
it 'uses different implmentation' do
it 'uses different implementation' do
expect(::Gitlab::Database::LoadBalancing).to receive_message_chain(:proxy, :load_balancer, :host, :caught_up?).and_return(false)
expect do
@ -185,9 +184,9 @@ RSpec.describe Gitlab::Database::LoadBalancing::SidekiqServerMiddleware do
include_examples 'stick to the primary'
it 'updates job hash with primary database chosen', :aggregate_failures do
expect { |b| middleware.call(worker, job, double(:queue), &b) }.to yield_control
expect(job[:database_chosen]).to eq('primary')
middleware.call(worker, job, double(:queue)) do
expect(job['load_balancing_strategy']).to eq('primary')
end
end
end
end

View file

@ -0,0 +1,24 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::GithubImport::ObjectCounter, :clean_gitlab_redis_cache do
let_it_be(:project) { create(:project) }
it 'validates the operation being incremented' do
expect { described_class.increment(project, :issue, :unknown) }
.to raise_error(ArgumentError, 'Operation must be fetched or imported')
end
it 'increments the counter and saves the key to be listed in the summary later' do
described_class.increment(project, :issue, :fetched)
described_class.increment(project, :issue, :fetched)
described_class.increment(project, :issue, :imported)
described_class.increment(project, :issue, :imported)
expect(described_class.summary(project)).to eq({
'fetched' => { 'issue' => 2 },
'imported' => { 'issue' => 2 }
})
end
end

View file

@ -11,6 +11,10 @@ RSpec.describe Gitlab::GithubImport::ParallelScheduling do
Class
end
def object_type
:dummy
end
def collection_method
:issues
end

View file

@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::GithubImport do
context 'github.com' do
let(:project) { double(:project, import_url: 'http://t0ken@github.com/user/repo.git') }
let(:project) { double(:project, import_url: 'http://t0ken@github.com/user/repo.git', id: 1) }
it 'returns a new Client with a custom token' do
expect(described_class::Client)

View file

@ -342,7 +342,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
end
context 'when the job uses load balancing capabilities' do
let(:expected_payload) { { 'database_chosen' => 'retry' } }
let(:expected_payload) { { 'load_balancing_strategy' => 'retry' } }
before do
allow(Time).to receive(:now).and_return(timestamp)
@ -354,7 +354,7 @@ RSpec.describe Gitlab::SidekiqLogging::StructuredLogger do
expect(logger).to receive(:info).with(include(expected_payload)).ordered
call_subject(job, 'test_queue') do
job[:database_chosen] = 'retry'
job['load_balancing_strategy'] = 'retry'
end
end
end

View file

@ -109,22 +109,20 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
end
context 'DB load balancing' do
using RSpec::Parameterized::TableSyntax
subject { described_class.new }
let(:queue) { :test }
let(:worker_class) { worker.class }
let(:job) { {} }
let(:job_status) { :done }
let(:labels_with_job_status) { default_labels.merge(job_status: job_status.to_s) }
let(:default_labels) do
{ queue: queue.to_s,
worker: worker_class.to_s,
boundary: "",
external_dependencies: "no",
feature_category: "",
urgency: "low" }
let(:worker) { TestWorker.new }
let(:client_middleware) { Gitlab::Database::LoadBalancing::SidekiqClientMiddleware.new }
let(:load_balancer) { double.as_null_object }
let(:load_balancing_metric) { double('load balancing metric') }
let(:job) { { "retry" => 3, "job_id" => "a180b47c-3fd6-41b8-81e9-34da61c3400e" } }
def process_job
client_middleware.call(worker_class, job, queue, double) do
worker_class.process_job(job)
end
end
before do
@ -132,84 +130,93 @@ RSpec.describe Gitlab::SidekiqMiddleware::ServerMetrics do
TestWorker.class_eval do
include Sidekiq::Worker
include WorkerAttributes
def perform(*args)
end
end
allow(::Gitlab::Database::LoadBalancing).to receive_message_chain(:proxy, :load_balancer).and_return(load_balancer)
allow(load_balancing_metric).to receive(:increment)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_load_balancing_count, anything).and_return(load_balancing_metric)
end
around do |example|
with_sidekiq_server_middleware do |chain|
chain.add Gitlab::Database::LoadBalancing::SidekiqServerMiddleware
chain.add described_class
Sidekiq::Testing.inline! { example.run }
end
end
let(:worker) { TestWorker.new }
include_context 'server metrics with mocked prometheus'
include_context 'server metrics call'
include_context 'clear DB Load Balancing configuration'
context 'when load_balancing is enabled' do
let(:load_balancing_metric) { double('load balancing metric') }
include_context 'clear DB Load Balancing configuration'
shared_context 'worker declaring data consistency' do
let(:worker_class) { LBTestWorker }
before do
allow(::Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true)
allow(Gitlab::Metrics).to receive(:counter).with(:sidekiq_load_balancing_count, anything).and_return(load_balancing_metric)
end
stub_const('LBTestWorker', Class.new(TestWorker))
LBTestWorker.class_eval do
include ApplicationWorker
describe '#initialize' do
it 'sets load_balancing metrics' do
expect(Gitlab::Metrics).to receive(:counter).with(:sidekiq_load_balancing_count, anything).and_return(load_balancing_metric)
subject
data_consistency :delayed
end
end
end
context 'when load_balancing is enabled' do
before do
allow(::Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(true)
end
describe '#call' do
include_context 'server metrics call'
context 'when worker declares data consistency' do
include_context 'worker declaring data consistency'
context 'when :database_chosen is provided' do
where(:database_chosen) do
%w[primary retry replica]
end
it 'increments load balancing counter' do
process_job
with_them do
context "when #{params[:database_chosen]} is used" do
let(:labels_with_load_balancing) do
labels_with_job_status.merge(database_chosen: database_chosen, data_consistency: 'delayed')
end
before do
job[:database_chosen] = database_chosen
job[:data_consistency] = 'delayed'
allow(load_balancing_metric).to receive(:increment)
end
it 'increment sidekiq_load_balancing_count' do
expect(load_balancing_metric).to receive(:increment).with(labels_with_load_balancing, 1)
described_class.new.call(worker, job, :test) { nil }
end
end
expect(load_balancing_metric).to have_received(:increment).with(
a_hash_including(
data_consistency: :delayed,
load_balancing_strategy: 'replica'
), 1)
end
end
context 'when :database_chosen is not provided' do
it 'does not increment sidekiq_load_balancing_count' do
expect(load_balancing_metric).not_to receive(:increment)
context 'when worker does not declare data consistency' do
it 'does not increment load balancing counter' do
process_job
described_class.new.call(worker, job, :test) { nil }
expect(load_balancing_metric).not_to have_received(:increment)
end
end
end
end
context 'when load_balancing is disabled' do
include_context 'clear DB Load Balancing configuration'
include_context 'worker declaring data consistency'
before do
allow(::Gitlab::Database::LoadBalancing).to receive(:enable?).and_return(false)
end
describe '#initialize' do
it 'doesnt set load_balancing metrics' do
it 'does not set load_balancing metrics' do
expect(Gitlab::Metrics).not_to receive(:counter).with(:sidekiq_load_balancing_count, anything)
subject
end
end
describe '#call' do
it 'does not increment load balancing counter' do
process_job
expect(load_balancing_metric).not_to have_received(:increment)
end
end
end
end
end

View file

@ -29,6 +29,10 @@ RSpec.describe ReplaceUniqueIndexOnCycleAnalyticsStages, :migration, schema: 202
stage_record
end
after do
described_class.new.up
end
it 'removes duplicated stage records' do
subject

Some files were not shown because too many files have changed in this diff Show more