Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-06-27 15:06:55 +00:00
parent 274a42ccfa
commit 0847321aee
67 changed files with 645 additions and 311 deletions

View File

@ -27,4 +27,8 @@ Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engi
Once the flaky failure has been fixed on the default branch, open merge requests to cherry-pick the fix to the active stable branches.
### Logs
<!-- Add here failing job logs -->
/label ~"type::maintenance" ~"failure::flaky-test" ~"priority::3" ~"severity::3"

View File

@ -21,4 +21,8 @@ Please read the below documentations for a workflow of triaging and resolving br
Please refer to the [Resolution guidance](https://about.gitlab.com/handbook/engineering/workflow/#resolution-of-broken-master) to learn more about resolution of broken master.
### Logs
<!-- Add here failing job logs -->
/label ~"master:broken" ~"Engineering Productivity" ~"priority::1" ~"severity::1" ~"type::maintenance" ~"maintenance::pipelines"

View File

@ -1025,3 +1025,10 @@ RSpec/FactoryBot/LocalStaticAssignment:
Rails/TransactionExitStatement:
Enabled: true
Search/AvoidCheckingFinishedOnDeprecatedMigrations:
Include:
- 'ee/app/models/**/*.rb'
- 'ee/lib/elastic/**/*.rb'
- 'ee/lib/gitlab/elastic/**/*.rb'
- 'ee/spec/support/helpers/elasticsearch_helpers.rb'

View File

@ -0,0 +1,3 @@
---
Search/AvoidCheckingFinishedOnDeprecatedMigrations:
Details: grace period

View File

@ -16,7 +16,7 @@ import {
import * as Sentry from '@sentry/browser';
import { isEqual, isEmpty, omit } from 'lodash';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
import { PROMO_URL, DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility';
import {
integrationTypes,
integrationSteps,
@ -38,6 +38,7 @@ export default {
placeholders: {
prometheus: targetPrometheusUrlPlaceholder,
},
incidentManagementDocsLink: `${DOCS_URL_IN_EE_DIR}/operations/incident_management/integrations.html#configuration`,
JSON_VALIDATE_DELAY,
typeSet,
integrationSteps,
@ -617,7 +618,7 @@ export default {
>
<alert-settings-form-help-block
:message="viewCredentialsHelpMsg"
link="https://docs.gitlab.com/ee/operations/incident_management/integrations.html#configuration"
:link="$options.incidentManagementDocsLink"
/>
<gl-form-group id="integration-webhook">

View File

@ -2,6 +2,7 @@
import { GlLink, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
import { DOCS_URL } from 'jh_else_ce/lib/utils/url_utility';
import { pipelineEditorTrackingOptions } from '../../../constants';
export default {
@ -34,7 +35,7 @@ export default {
this.track(actions.helpDrawerLinks.runners, { label });
},
},
RUNNER_HELP_URL: 'https://docs.gitlab.com/runner/register/index.html',
RUNNER_HELP_URL: `${DOCS_URL}/runner/register/index.html`,
};
</script>
<template>

View File

@ -58,6 +58,9 @@ export default {
pipelinesPath: {
default: '',
},
newSchedulePath: {
default: '',
},
},
apollo: {
schedules: {
@ -289,7 +292,12 @@ export default {
</gl-tab>
<template #tabs-end>
<gl-button variant="confirm" class="gl-ml-auto" data-testid="new-schedule-button">
<gl-button
:href="newSchedulePath"
variant="confirm"
class="gl-ml-auto"
data-testid="new-schedule-button"
>
{{ $options.i18n.newSchedule }}
</gl-button>
</template>

View File

@ -26,6 +26,11 @@ export default {
GlLink,
GlSprintf,
},
inject: {
newSchedulePath: {
default: '',
},
},
computed: {
schedulesHelpPath() {
return helpPagePath('ci/pipelines/schedules');
@ -37,7 +42,7 @@ export default {
<gl-empty-state
:svg-path="$options.SCHEDULE_MD_SVG_URL"
:primary-button-text="$options.i18n.createNew"
primary-button-link="#"
:primary-button-link="newSchedulePath"
>
<template #title>
<h3>

View File

@ -8,16 +8,15 @@ import {
GlFormGroup,
GlFormInput,
GlFormTextarea,
GlLink,
GlSprintf,
} from '@gitlab/ui';
import { uniqueId } from 'lodash';
import Vue from 'vue';
import { __, s__ } from '~/locale';
import { createAlert } from '~/alert';
import { visitUrl } from '~/lib/utils/url_utility';
import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
import RefSelector from '~/ref/components/ref_selector.vue';
import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue';
import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue';
import createPipelineScheduleMutation from '../graphql/mutations/create_pipeline_schedule.mutation.graphql';
import { VARIABLE_TYPE, FILE_TYPE } from '../constants';
export default {
@ -30,8 +29,6 @@ export default {
GlFormGroup,
GlFormInput,
GlFormTextarea,
GlLink,
GlSprintf,
RefSelector,
TimezoneDropdown,
IntervalPatternInput,
@ -44,6 +41,7 @@ export default {
'cronTimezone',
'dailyLimit',
'settingsLink',
'schedulesPath',
],
props: {
timezoneData: {
@ -55,24 +53,19 @@ export default {
required: false,
default: '',
},
editing: {
type: Boolean,
required: true,
},
},
data() {
return {
refValue: {
shortName: this.refParam,
// this is needed until we add support for ref type in url query strings
// ensure default branch is called with full ref on load
// https://gitlab.com/gitlab-org/gitlab/-/issues/287815
fullName: this.refParam === this.defaultBranch ? `refs/heads/${this.refParam}` : undefined,
},
cronValue: this.cron,
description: '',
scheduleRef: this.defaultBranch,
activated: true,
timezone: this.cronTimezone,
formCiVariables: {},
// TODO: Add the GraphQL query to help populate the predefined variables
// app/assets/javascripts/ci/pipeline_new/components/pipeline_new_form.vue#131
predefinedValueOptions: {},
variables: [],
};
},
i18n: {
@ -91,6 +84,9 @@ export default {
),
removeVariableLabel: s__('CiVariables|Remove variable'),
variables: s__('Pipeline|Variables'),
scheduleCreateError: s__(
'PipelineSchedules|An error occurred while creating the pipeline schedule.',
),
},
typeOptions: {
[VARIABLE_TYPE]: __('Variable'),
@ -103,15 +99,6 @@ export default {
dropdownHeader: this.$options.i18n.targetBranchTag,
};
},
refFullName() {
return this.refValue.fullName;
},
variables() {
return this.formCiVariables[this.refFullName]?.variables ?? [];
},
descriptions() {
return this.formCiVariables[this.refFullName]?.descriptions ?? {};
},
typeOptionsListbox() {
return [
{
@ -127,51 +114,84 @@ export default {
getEnabledRefTypes() {
return [REF_TYPE_BRANCHES, REF_TYPE_TAGS];
},
preparedVariables() {
return this.variables.filter((variable) => variable.key !== '');
},
},
created() {
Vue.set(this.formCiVariables, this.refFullName, {
variables: [],
descriptions: {},
});
this.addEmptyVariable(this.refFullName);
this.addEmptyVariable();
},
methods: {
addEmptyVariable(refValue) {
const { variables } = this.formCiVariables[refValue];
addEmptyVariable() {
const lastVar = this.variables[this.variables.length - 1];
const lastVar = variables[variables.length - 1];
if (lastVar?.key === '' && lastVar?.value === '') {
return;
}
variables.push({
uniqueId: uniqueId(`var-${refValue}`),
variable_type: VARIABLE_TYPE,
this.variables.push({
variableType: VARIABLE_TYPE,
key: '',
value: '',
});
},
setVariableAttribute(key, attribute, value) {
const { variables } = this.formCiVariables[this.refFullName];
const variable = variables.find((v) => v.key === key);
const variable = this.variables.find((v) => v.key === key);
variable[attribute] = value;
},
shouldShowValuesDropdown(key) {
return this.predefinedValueOptions[key]?.length > 1;
},
removeVariable(index) {
this.variables.splice(index, 1);
},
canRemove(index) {
return index < this.variables.length - 1;
},
scheduleHandler() {
if (!this.editing) {
this.createPipelineSchedule();
}
},
async createPipelineSchedule() {
try {
const {
data: {
pipelineScheduleCreate: { errors },
},
} = await this.$apollo.mutate({
mutation: createPipelineScheduleMutation,
variables: {
input: {
description: this.description,
cron: this.cronValue,
cronTimezone: this.timezone,
ref: this.scheduleRef,
variables: this.preparedVariables,
active: this.activated,
projectPath: this.fullPath,
},
},
});
if (errors.length > 0) {
createAlert({ message: errors[0] });
} else {
visitUrl(this.schedulesPath);
}
} catch {
createAlert({ message: this.$options.i18n.scheduleCreateError });
}
},
setCronValue(cron) {
this.cronValue = cron;
},
setTimezone(timezone) {
this.timezone = timezone.identifier || '';
},
},
};
</script>
<template>
<div class="col-lg-8">
<div class="col-lg-8 gl-pl-0">
<gl-form>
<!--Description-->
<gl-form-group :label="$options.i18n.description" label-for="schedule-description">
@ -181,6 +201,7 @@ export default {
type="text"
:placeholder="$options.i18n.shortDescriptionPipeline"
data-testid="schedule-description"
required
/>
</gl-form-group>
<!--Interval Pattern-->
@ -190,6 +211,7 @@ export default {
:initial-cron-interval="cron"
:daily-limit="dailyLimit"
:send-native-errors="false"
@cronValue="setCronValue"
/>
</gl-form-group>
<!--Timezone-->
@ -199,12 +221,14 @@ export default {
:value="timezone"
:timezone-data="timezoneData"
name="schedule-timezone"
@input="setTimezone"
/>
</gl-form-group>
<!--Branch/Tag Selector-->
<gl-form-group :label="$options.i18n.targetBranchTag" label-for="schedule-target-branch-tag">
<ref-selector
id="schedule-target-branch-tag"
v-model="scheduleRef"
:enabled-ref-types="getEnabledRefTypes"
:project-id="projectId"
:value="scheduleRef"
@ -217,7 +241,7 @@ export default {
<gl-form-group :label="$options.i18n.variables">
<div
v-for="(variable, index) in variables"
:key="variable.uniqueId"
:key="`var-${index}`"
class="gl-mb-3 gl-pb-2"
data-testid="ci-variable-row"
data-qa-selector="ci_variable_row_container"
@ -226,14 +250,14 @@ export default {
class="gl-display-flex gl-align-items-stretch gl-flex-direction-column gl-md-flex-direction-row"
>
<gl-dropdown
:text="$options.typeOptions[variable.variable_type]"
:text="$options.typeOptions[variable.variableType]"
:class="$options.formElementClasses"
data-testid="pipeline-form-ci-variable-type"
>
<gl-dropdown-item
v-for="type in Object.keys($options.typeOptions)"
:key="type"
@click="setVariableAttribute(variable.key, 'variable_type', type)"
@click="setVariableAttribute(variable.key, 'variableType', type)"
>
{{ $options.typeOptions[type] }}
</gl-dropdown-item>
@ -244,26 +268,10 @@ export default {
:class="$options.formElementClasses"
data-testid="pipeline-form-ci-variable-key"
data-qa-selector="ci_variable_key_field"
@change="addEmptyVariable(refFullName)"
@change="addEmptyVariable()"
/>
<gl-dropdown
v-if="shouldShowValuesDropdown(variable.key)"
:text="variable.value"
:class="$options.formElementClasses"
class="gl-flex-grow-1 gl-mr-0!"
data-testid="pipeline-form-ci-variable-value-dropdown"
>
<gl-dropdown-item
v-for="value in predefinedValueOptions[variable.key]"
:key="value"
data-testid="pipeline-form-ci-variable-value-dropdown-items"
@click="setVariableAttribute(variable.key, 'value', value)"
>
{{ value }}
</gl-dropdown-item>
</gl-dropdown>
<gl-form-textarea
v-else
v-model="variable.value"
:placeholder="s__('CiVariables|Input variable value')"
class="gl-mb-3 gl-h-7!"
@ -292,30 +300,19 @@ export default {
/>
</template>
</div>
<div v-if="descriptions[variable.key]" class="gl-text-gray-500 gl-mb-3">
{{ descriptions[variable.key] }}
</div>
</div>
<template #description
><gl-sprintf :message="$options.i18n.variablesDescription">
<template #link="{ content }">
<gl-link :href="settingsLink">{{ content }}</gl-link>
</template>
</gl-sprintf></template
>
</gl-form-group>
<!--Activated-->
<gl-form-checkbox id="schedule-active" v-model="activated" class="gl-mb-3">{{
$options.i18n.activated
}}</gl-form-checkbox>
<gl-form-checkbox id="schedule-active" v-model="activated" class="gl-mb-3">
{{ $options.i18n.activated }}
</gl-form-checkbox>
<gl-button type="submit" variant="confirm" data-testid="schedule-submit-button">{{
$options.i18n.savePipelineSchedule
}}</gl-button>
<gl-button type="reset" data-testid="schedule-cancel-button">{{
$options.i18n.cancel
}}</gl-button>
<gl-button variant="confirm" data-testid="schedule-submit-button" @click="scheduleHandler">
{{ $options.i18n.savePipelineSchedule }}
</gl-button>
<gl-button :href="schedulesPath" data-testid="schedule-cancel-button">
{{ $options.i18n.cancel }}
</gl-button>
</gl-form>
</div>
</template>

View File

@ -1,6 +1,7 @@
<script>
import { GlButton, GlButtonGroup, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
import { s__ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
export const i18n = {
playTooltip: s__('PipelineSchedules|Run pipeline schedule'),
@ -44,6 +45,11 @@ export default {
canRemove() {
return this.schedule.userPermissions.adminPipelineSchedule;
},
editPathWithIdParam() {
const id = getIdFromGraphQLId(this.schedule.id);
return `${this.schedule.editPath}?id=${id}`;
},
},
};
</script>
@ -67,7 +73,14 @@ export default {
data-testid="take-ownership-pipeline-schedule-btn"
@click="$emit('showTakeOwnershipModal', schedule.id)"
/>
<gl-button v-if="canUpdate" v-gl-tooltip :title="$options.i18n.editTooltip" icon="pencil" />
<gl-button
v-if="canUpdate"
v-gl-tooltip
:href="editPathWithIdParam"
:title="$options.i18n.editTooltip"
icon="pencil"
data-testid="edit-pipeline-schedule-btn"
/>
<gl-button
v-if="canRemove"
v-gl-tooltip

View File

@ -1,2 +1,2 @@
export const VARIABLE_TYPE = 'env_var';
export const FILE_TYPE = 'file';
export const VARIABLE_TYPE = 'ENV_VAR';
export const FILE_TYPE = 'FILE';

View File

@ -0,0 +1,6 @@
mutation createPipelineSchedule($input: PipelineScheduleCreateInput!) {
pipelineScheduleCreate(input: $input) {
clientMutationId
errors
}
}

View File

@ -11,6 +11,7 @@ query getPipelineSchedulesQuery($projectPath: ID!, $status: PipelineScheduleStat
id
description
forTag
editPath
refPath
refForDisplay
lastPipeline {

View File

@ -18,7 +18,7 @@ export default () => {
return false;
}
const { fullPath, pipelinesPath } = containerEl.dataset;
const { fullPath, pipelinesPath, newSchedulePath, schedulesPath } = containerEl.dataset;
return new Vue({
el: containerEl,
@ -27,6 +27,8 @@ export default () => {
provide: {
fullPath,
pipelinesPath,
newSchedulePath,
schedulesPath,
},
render(createElement) {
return createElement(PipelineSchedules);

View File

@ -9,7 +9,7 @@ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient(),
});
export default (selector) => {
export default (selector, editing = false) => {
const containerEl = document.querySelector(selector);
if (!containerEl) {
@ -25,6 +25,7 @@ export default (selector) => {
projectId,
defaultBranch,
settingsLink,
schedulesPath,
} = containerEl.dataset;
return new Vue({
@ -39,12 +40,14 @@ export default (selector) => {
cronTimezone: cronTimezone ?? '',
cron: cron ?? '',
settingsLink,
schedulesPath,
},
render(createElement) {
return createElement(PipelineSchedulesForm, {
props: {
timezoneData: JSON.parse(timezoneData),
refParam: defaultBranch,
editing,
},
});
},

View File

@ -1,4 +1,5 @@
import { __, s__ } from '~/locale';
import { DOCS_URL } from 'jh_else_ce/lib/utils/url_utility';
export const RUNNER_TYPENAME = 'CiRunner'; // __typename
@ -268,12 +269,10 @@ export const DEFAULT_PLATFORM = LINUX_PLATFORM;
// Runner docs are in a separate repository and are not shipped with GitLab
// they are rendered as external URLs.
export const INSTALL_HELP_URL = 'https://docs.gitlab.com/runner/install';
export const EXECUTORS_HELP_URL = 'https://docs.gitlab.com/runner/executors/';
export const SERVICE_COMMANDS_HELP_URL =
'https://docs.gitlab.com/runner/commands/#service-related-commands';
export const CHANGELOG_URL = 'https://gitlab.com/gitlab-org/gitlab-runner/blob/main/CHANGELOG.md';
export const DOCKER_HELP_URL = 'https://docs.gitlab.com/runner/install/docker.html';
export const KUBERNETES_HELP_URL = 'https://docs.gitlab.com/runner/install/kubernetes.html';
export const RUNNER_MANAGERS_HELP_URL =
'https://docs.gitlab.com/runner/fleet_scaling/#workers-executors-and-autoscaling-capabilities';
export const INSTALL_HELP_URL = `${DOCS_URL}/runner/install`;
export const EXECUTORS_HELP_URL = `${DOCS_URL}/runner/executors/`;
export const SERVICE_COMMANDS_HELP_URL = `${DOCS_URL}/runner/commands/#service-related-commands`;
export const CHANGELOG_URL = `https://gitlab.com/gitlab-org/gitlab-runner/blob/main/CHANGELOG.md`;
export const DOCKER_HELP_URL = `${DOCS_URL}/runner/install/docker.html`;
export const KUBERNETES_HELP_URL = `${DOCS_URL}/runner/install/kubernetes.html`;
export const RUNNER_MANAGERS_HELP_URL = `${DOCS_URL}/runner/fleet_scaling/#workers-executors-and-autoscaling-capabilities`;

View File

@ -1,10 +1,14 @@
<script>
import { GlSprintf, GlTooltipDirective, GlModal } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility';
import eventHub from '../event_hub';
import stopEnvironmentMutation from '../graphql/mutations/stop_environment.mutation.graphql';
export default {
yamlDocsLink: `${DOCS_URL_IN_EE_DIR}/ee/ci/yaml/`,
stoppingEnvironmentDocsLink: `${DOCS_URL_IN_EE_DIR}/environments/#stopping-an-environment`,
id: 'stop-environment-modal',
name: 'StopEnvironmentModal',
@ -98,18 +102,15 @@ export default {
<strong>{{ content }}</strong>
</template>
<template #ciConfigLink="{ content }">
<a href="https://docs.gitlab.com/ee/ci/yaml/" target="_blank" rel="noopener noreferrer">
<a :href="$options.yamlDocsLink" target="_blank" rel="noopener noreferrer">
{{ content }}</a
>
</template>
</gl-sprintf>
</p>
<a
href="https://docs.gitlab.com/ee/ci/environments/#stopping-an-environment"
target="_blank"
rel="noopener noreferrer"
>{{ s__('Environments|Learn more about stopping environments') }}</a
>
<a :href="$options.stoppingEnvironmentDocsLink" target="_blank" rel="noopener noreferrer">{{
s__('Environments|Learn more about stopping environments')
}}</a>
</div>
</gl-modal>
</template>

View File

@ -1,4 +1,5 @@
import { helpPagePath } from '~/helpers/help_page_helper';
import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
export const STATUS_TYPES = {
SUCCESS: 'success',
@ -8,7 +9,7 @@ export const STATUS_TYPES = {
export const UPGRADE_DOCS_URL = helpPagePath('update/index');
export const ABOUT_RELEASES_PAGE = 'https://about.gitlab.com/releases/categories/releases/';
export const ABOUT_RELEASES_PAGE = `${PROMO_URL}/releases/categories/releases/`;
export const ALERT_MODAL_ID = 'security-patch-upgrade-alert-modal';

View File

@ -1,6 +1,6 @@
<script>
import { GlAlert, GlButton, GlEmptyState, GlLink, GlSprintf, GlTable } from '@gitlab/ui';
import { setUrlParams } from '~/lib/utils/url_utility';
import { setUrlParams, DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility';
import { __ } from '~/locale';
const GOOGLE_CONSOLE_URL = 'https://console.cloud.google.com/iam-admin/serviceaccounts';
@ -49,6 +49,7 @@ export default {
},
},
GOOGLE_CONSOLE_URL,
secretsDocsLink: `${DOCS_URL_IN_EE_DIR}/ci/secrets/`,
};
</script>
@ -86,7 +87,7 @@ export default {
<gl-alert class="gl-mt-5" :dismissible="false" variant="tip">
<gl-sprintf :message="$options.i18n.secretManagersDescription">
<template #docLink="{ content }">
<gl-link href="https://docs.gitlab.com/ee/ci/secrets/">
<gl-link :href="$options.secretsDocsLink">
{{ content }}
</gl-link>
</template>

View File

@ -1,6 +1,7 @@
<script>
import { GlAlert, GlBadge, GlLink, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import { DOCS_URL } from 'jh_else_ce/lib/utils/url_utility';
/**
* Renders Stuck Runners block for job's view.
*/
@ -31,7 +32,7 @@ export default {
return this.tags.length > 0;
},
protectedBranchSettingsDocsLink() {
return 'https://docs.gitlab.com/runner/security/index.html#reduce-the-security-risk-of-using-privileged-containers';
return `${DOCS_URL}/runner/security/index.html#reduce-the-security-risk-of-using-privileged-containers`;
},
stuckData() {
if (this.hasNoRunnersWithCorrespondingTags) {

View File

@ -2,7 +2,7 @@ import initPipelineSchedulesFormApp from '~/ci/pipeline_schedules/mount_pipeline
import initForm from '../shared/init_form';
if (gon.features?.pipelineSchedulesVue) {
initPipelineSchedulesFormApp('#pipeline-schedules-form-edit');
initPipelineSchedulesFormApp('#pipeline-schedules-form-edit', true);
} else {
initForm();
}

View File

@ -10,6 +10,7 @@ import {
import { getWeekdayNames } from '~/lib/utils/datetime_utility';
import { __, s__, sprintf } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility';
const KEY_EVERY_DAY = 'everyDay';
const KEY_EVERY_WEEK = 'everyWeek';
@ -54,7 +55,7 @@ export default {
inputNameAttribute: 'schedule[cron]',
radioValue: this.initialCronInterval ? KEY_CUSTOM : KEY_EVERY_DAY,
cronInterval: this.initialCronInterval,
cronSyntaxUrl: 'https://docs.gitlab.com/ee/topics/cron/',
cronSyntaxUrl: `${DOCS_URL_IN_EE_DIR}/topics/cron/`,
};
},
computed: {
@ -116,7 +117,7 @@ export default {
},
},
watch: {
cronInterval() {
cronInterval(val) {
// updates field validation state when model changes, as
// glFieldError only updates on input.
if (this.sendNativeErrors) {
@ -124,6 +125,8 @@ export default {
gl.pipelineScheduleFieldErrors.updateFormValidityState();
});
}
this.$emit('cronValue', val);
},
radioValue: {
immediate: true,

View File

@ -2,7 +2,7 @@
import { GlButton, GlCard, GlSprintf, GlLink, GlPopover, GlModalDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { mergeUrlParams, DOCS_URL } from '~/lib/utils/url_utility';
import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
import apolloProvider from '~/pipelines/graphql/provider';
import CiTemplates from './ci_templates.vue';
@ -31,7 +31,7 @@ export default {
apolloProvider,
iOSTemplateName: 'iOS-Fastlane',
modalId: 'runner-instructions-modal',
runnerDocsLink: 'https://docs.gitlab.com/runner/install/osx',
runnerDocsLink: `${DOCS_URL}/runner/install/osx`,
whatElseLink: helpPagePath('ci/index.md'),
i18n: {
title: s__('Pipelines|Get started with GitLab CI/CD'),

View File

@ -4,10 +4,12 @@ import { __, n__, sprintf } from '~/locale';
import { createAlert } from '~/alert';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { TYPENAME_ISSUE } from '~/graphql_shared/constants';
import { DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility';
import getIssueCrmContactsQuery from '../../queries/get_issue_crm_contacts.query.graphql';
import issueCrmContactsSubscription from '../../queries/issue_crm_contacts.subscription.graphql';
export default {
crmDocsLink: `${DOCS_URL_IN_EE_DIR}/user/crm/`,
components: {
GlIcon,
GlLink,
@ -104,9 +106,7 @@ export default {
<span> {{ contactCount }} </span>
</div>
<div class="hide-collapsed help-button gl-float-right">
<gl-link href="https://docs.gitlab.com/ee/user/crm/" target="_blank"
><gl-icon name="question-o"
/></gl-link>
<gl-link :href="$options.crmDocsLink" target="_blank"><gl-icon name="question-o" /></gl-link>
</div>
<div class="title hide-collapsed gl-mb-2 gl-line-height-20 gl-font-weight-bold">
{{ contactsLabel }}

View File

@ -6,6 +6,7 @@ import { s__, __ } from '~/locale';
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import SatisfactionRate from '~/surveys/components/satisfaction_rate.vue';
import Tracking from '~/tracking';
import { PROMO_URL } from 'jh_else_ce/lib/utils/url_utility';
const steps = [
{
@ -50,6 +51,7 @@ export default {
thanks: s__('MrSurvey|Thank you for your feedback!'),
},
gitlabLogo,
privacyLink: `${PROMO_URL}/privacy/`,
data() {
return {
visible: false,
@ -152,7 +154,7 @@ export default {
<template #link="{ content }">
<a
class="gl-text-decoration-underline gl-text-gray-500"
href="https://about.gitlab.com/privacy/"
:href="$options.privacyLink"
target="_blank"
rel="noreferrer nofollow"
v-text="content"

View File

@ -1,5 +1,6 @@
import { s__ } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import { DOCS_URL_IN_EE_DIR } from 'jh_else_ce/lib/utils/url_utility';
import { stateToComponentMap as classStateMap, stateKey } from './stores/state_maps';
export const FOUR_MINUTES_IN_MS = 1000 * 60 * 4;
@ -26,7 +27,7 @@ export const SP_SHOW_TRACK_VALUE = 10;
export const SP_HELP_CONTENT = s__(
`mrWidget|GitLab %{linkStart}CI/CD can automatically build, test, and deploy your application.%{linkEnd} It only takes a few minutes to get started, and we can help you create a pipeline configuration file.`,
);
export const SP_HELP_URL = 'https://docs.gitlab.com/ee/ci/quick_start/';
export const SP_HELP_URL = `${DOCS_URL_IN_EE_DIR}/ci/quick_start/`;
export const SP_ICON_NAME = 'status_notfound';
export const MERGE_ACTIVE_STATUS_PHRASES = [

View File

@ -1,6 +1,7 @@
<script>
import { GlButton, GlIcon } from '@gitlab/ui';
import { s__ } from '~/locale';
import { DOCS_URL } from 'jh_else_ce/lib/utils/url_utility';
export default {
components: {
@ -16,7 +17,7 @@ export default {
'Runners|To install Runner in a container follow the instructions described in the GitLab documentation',
),
I18N_VIEW_INSTRUCTIONS: s__('Runners|View installation instructions'),
HELP_URL: 'https://docs.gitlab.com/runner/install/docker.html',
HELP_URL: `${DOCS_URL}/runner/install/docker.html`,
};
</script>
<template>

View File

@ -1,6 +1,7 @@
<script>
import { GlButton, GlIcon } from '@gitlab/ui';
import { s__ } from '~/locale';
import { DOCS_URL } from 'jh_else_ce/lib/utils/url_utility';
export default {
components: {
@ -16,7 +17,7 @@ export default {
'Runners|To install Runner in Kubernetes follow the instructions described in the GitLab documentation.',
),
I18N_VIEW_INSTRUCTIONS: s__('Runners|View installation instructions'),
HELP_URL: 'https://docs.gitlab.com/runner/install/kubernetes.html',
HELP_URL: `${DOCS_URL}/runner/install/kubernetes.html`,
};
</script>
<template>

View File

@ -6,7 +6,7 @@
#pipeline-schedules-callout{ data: { docs_url: help_page_path('ci/pipelines/schedules'), illustration_url: image_path('illustrations/pipeline_schedule_callout.svg') } }
- if Feature.enabled?(:pipeline_schedules_vue, @project)
#pipeline-schedules-app{ data: { full_path: @project.full_path, pipelines_path: project_pipelines_path(@project) } }
#pipeline-schedules-app{ data: { full_path: @project.full_path, pipelines_path: project_pipelines_path(@project), new_schedule_path: new_project_pipeline_schedule_path(@project) } }
- else
.top-area
- schedule_path_proc = ->(scope) { pipeline_schedules_path(@project, scope: scope) }

View File

@ -9,6 +9,6 @@
= _("Schedule a new pipeline")
- if Feature.enabled?(:pipeline_schedules_vue, @project)
#pipeline-schedules-form-new{ data: { full_path: @project.full_path, cron: @schedule.cron, daily_limit: @schedule.daily_limit, timezone_data: timezone_data.to_json, cron_timezone: @schedule.cron_timezone, project_id: @project.id, default_branch: @project.default_branch, settings_link: project_settings_ci_cd_path(@project), } }
#pipeline-schedules-form-new{ data: { full_path: @project.full_path, cron: @schedule.cron, daily_limit: @schedule.daily_limit, timezone_data: timezone_data.to_json, cron_timezone: @schedule.cron_timezone, project_id: @project.id, default_branch: @project.default_branch, settings_link: project_settings_ci_cd_path(@project), schedules_path: pipeline_schedules_path(@project) } }
- else
= render "form"

View File

@ -91,19 +91,8 @@ changes.delete(:docs)
changes.delete(:changelog)
# No special review for feature flags needed.
changes.delete(:feature_flag)
categories = Set.new(changes.keys - [:unknown])
# Ensure to spin for database reviewer/maintainer when ~database is applied (e.g. to review SQL queries)
categories << :database if helper.mr_labels.include?('database')
# Ensure to spin for UX reviewer when ~UX is applied (e.g. to review changes to the UI) except when it's from wider community contribution where we want to assign from the corresponding group
categories << :ux if helper.mr_labels.include?('UX') && !helper.mr_labels.include?('Community contribution') # rubocop:disable Rails/NegateInclude
# Ensure to spin for Analytics Instrumentation reviewer when ~"analytics instrumentation::review pending" is applied
categories << :analytics_instrumentation if helper.mr_labels.include?("analytics instrumentation::review pending")
# Skip Analytics Instrumentation reviews for growth experiment MRs
categories.delete(:analytics_instrumentation) if helper.mr_labels.include?("growth experiment")
categories = roulette.prepare_categories(changes.keys - [:unknown])
# Skip specialty reviews for stable branch MRs since they have already been merged to the default branch
categories.subtract([:database, :ux, :analytics_instrumentation]) if stable_branch.valid_stable_branch?
@ -111,6 +100,10 @@ categories.subtract([:database, :ux, :analytics_instrumentation]) if stable_bran
if changes.any?
random_roulette_spins = roulette.spin(nil, categories, timezone_experiment: false)
if categories.include?(:ux) # rubocop:disable Style/IfUnlessModifier
roulette.assign_pedroms_for_ux_wider_community_contribution(random_roulette_spins)
end
rows = random_roulette_spins.map do |spin|
markdown_row_for_spin(spin.category, spin)
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class AddElasticsearchNumberOfShardsToApplicationSettings < Gitlab::Database::Migration[2.1]
enable_lock_retries!
def change
add_column :application_settings, :elasticsearch_worker_number_of_shards, :integer, null: false, default: 2
end
end

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
class UpdateElasticsearchNumberOfShardsInApplicationSettingsForGitlabCom < Gitlab::Database::Migration[2.1]
restrict_gitlab_migration gitlab_schema: :gitlab_main
enable_lock_retries!
def up
return unless Gitlab.com?
execute 'UPDATE application_settings SET elasticsearch_worker_number_of_shards = 16'
end
def down
return unless Gitlab.com?
execute 'UPDATE application_settings SET elasticsearch_worker_number_of_shards = 2'
end
end

View File

@ -0,0 +1 @@
f40ca52d92fd6c2c3a64b5b3c46c46f1f690b67b5320e84f55f62d4723c7d7e6

View File

@ -0,0 +1 @@
b44a08db74d15671a63afff9c41da67c0486c57bb893dc147ae073d78541457c

View File

@ -11758,6 +11758,7 @@ CREATE TABLE application_settings (
default_branch_protection_defaults jsonb DEFAULT '{}'::jsonb NOT NULL,
gitlab_shell_operation_limit integer DEFAULT 600,
elasticsearch_requeue_workers boolean DEFAULT false NOT NULL,
elasticsearch_worker_number_of_shards integer DEFAULT 2 NOT NULL,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_container_registry_pre_import_tags_rate_positive CHECK ((container_registry_pre_import_tags_rate >= (0)::numeric)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),

View File

@ -206,7 +206,7 @@ configuration option in `gitlab.yml`. These metrics are served from the
| `sidekiq_jobs_dead_total` | Counter | 13.7 | Sidekiq dead jobs (jobs that have run out of retries) | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
| `sidekiq_redis_requests_total` | Counter | 13.1 | Redis requests during a Sidekiq job execution | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_elasticsearch_requests_total` | Counter | 13.1 | Elasticsearch requests during a Sidekiq job execution | `queue`, `boundary`, `external_dependencies`, `feature_category`, `job_status`, `urgency` |
| `sidekiq_jobs_deferred_total` | Counter | 16.1 | Number of jobs being deferred when `defer_sidekiq_jobs` feature flag is enabled | `worker` |
| `sidekiq_jobs_deferred_total` | Counter | 16.1 | Number of jobs being deferred (either via `run_sidekiq_jobs` feature flag or DB health status indicator) | `worker` |
| `sidekiq_running_jobs` | Gauge | 12.2 | Number of Sidekiq jobs running | `queue`, `boundary`, `external_dependencies`, `feature_category`, `urgency` |
| `sidekiq_concurrency` | Gauge | 12.5 | Maximum number of Sidekiq jobs | |
| `sidekiq_mem_total_bytes` | Gauge | 15.3 | Number of bytes allocated for both objects consuming an object slot and objects that required a malloc'| |

View File

@ -435,22 +435,6 @@ The GitLab exporter allows you to measure various GitLab metrics, pulled from Re
[Read more about the GitLab exporter](gitlab_exporter.md).
## Configuring Prometheus to monitor Kubernetes
If your GitLab server is running within Kubernetes, Prometheus collects metrics from the Nodes and [annotated Pods](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#kubernetes_sd_config) in the cluster, including performance data on each container. This is particularly helpful if your CI/CD environments run in the same cluster, as you can use the [Prometheus project integration](../../../user/project/integrations/prometheus.md) to monitor them.
To disable the monitoring of Kubernetes:
1. Edit `/etc/gitlab/gitlab.rb`.
1. Add (or find and uncomment) the following line and set it to `false`:
```ruby
prometheus['monitor_kubernetes'] = false
```
1. Save the file and [reconfigure GitLab](../../restart_gitlab.md#reconfigure-a-linux-package-installation) for the changes to
take effect.
## Troubleshooting
### `/var/opt/gitlab/prometheus` consumes too much disk space

View File

@ -375,6 +375,7 @@ listed in the descriptions of the relevant settings.
| `elasticsearch_requeue_workers` **(PREMIUM)** | boolean | no | Enable automatic requeuing of indexing workers. This improves non-code indexing throughput by enqueuing Sidekiq jobs until all documents are processed. |
| `elasticsearch_limit_indexing` **(PREMIUM)** | boolean | no | Limit Elasticsearch to index certain namespaces and projects. |
| `elasticsearch_max_bulk_concurrency` **(PREMIUM)** | integer | no | Maximum concurrency of Elasticsearch bulk requests per indexing operation. This only applies to repository indexing operations. |
| `elasticsearch_worker_number_of_shards` **(PREMIUM)** | integer | no | Number of indexing worker shards. This improves non-code indexing throughput by enqueuing more parallel Sidekiq jobs. Default is `2`. |
| `elasticsearch_max_bulk_size_mb` **(PREMIUM)** | integer | no | Maximum size of Elasticsearch bulk indexing requests in MB. This only applies to repository indexing operations. |
| `elasticsearch_namespace_ids` **(PREMIUM)** | array of integers | no | The namespaces to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. |
| `elasticsearch_project_ids` **(PREMIUM)** | array of integers | no | The projects to index via Elasticsearch if `elasticsearch_limit_indexing` is enabled. |
@ -555,6 +556,7 @@ listed in the descriptions of the relevant settings.
| `unique_ips_limit_enabled` | boolean | no | (**If enabled, requires:** `unique_ips_limit_per_user` and `unique_ips_limit_time_window`) Limit sign in from multiple IPs. |
| `unique_ips_limit_per_user` | integer | required by: `unique_ips_limit_enabled` | Maximum number of IPs per user. |
| `unique_ips_limit_time_window` | integer | required by: `unique_ips_limit_enabled` | How many seconds an IP is counted towards the limit. |
| `update_runner_versions_enabled` | boolean | no | Fetch GitLab Runner release version data from GitLab.com. For more information, see how to [determine which runners need to be upgraded](../ci/runners/configure_runners.md#determine-which-runners-need-to-be-upgraded). |
| `usage_ping_enabled` | boolean | no | Every week GitLab reports license usage back to GitLab, Inc. |
| `user_deactivation_emails_enabled` | boolean | no | Send an email to users upon account deactivation. |
| `user_default_external` | boolean | no | Newly registered users are external by default. |

View File

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 125 KiB

View File

@ -149,7 +149,7 @@ created using the [Experiment Tracking template](https://gitlab.com/gitlab-org/g
`worker` feature flags are used for controlling Sidekiq workers behavior, such as deferring Sidekiq jobs.
`worker` feature flags likely do not have any YAML definition as the name could be dynamically generated using
the worker name itself, e.g. `defer_sidekiq_jobs_AuthorizedProjectsWorker`. Some examples for using `worker` type feature
the worker name itself, e.g. `run_sidekiq_jobs_AuthorizedProjectsWorker`. Some examples for using `worker` type feature
flags can be found in [deferring Sidekiq jobs](#deferring-sidekiq-jobs).
## Feature flag definition and validation
@ -713,33 +713,29 @@ Feature flags with [`worker` type](#worker-type) can be used to control the beha
### Deferring Sidekiq jobs
Feature flags with the format of `defer_sidekiq_jobs_{WorkerName}` delay the execution of the worker
by scheduling the job at a later time.
When disabled, feature flags with the format of `run_sidekiq_jobs_{WorkerName}` delay the execution of the worker
by scheduling the job at a later time. This feature flag is enabled by default for all workers.
Deferring jobs can be useful during an incident where contentious behavior from
worker instances are saturating infrastructure resources (such as database and database connection pool).
The implementation can be found at [DeferJobs Sidekiq server middleware](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/sidekiq_middleware/defer_jobs.rb).
NOTE:
Jobs are deferred indefinitely as long as the feature flag is enabled. It is important to disable the
Jobs are deferred indefinitely as long as the feature flag is disabled. It is important to enable the
feature flag after the worker is deemed safe to continue processing.
When set to true, 100% of the jobs are deferred. When you want processing to resume, you can
When set to false, 100% of the jobs are deferred. When you want processing to resume, you can
use a **percentage of time** rollout. For example:
```shell
# defer 100% of the jobs
/chatops run feature set defer_sidekiq_jobs_SlowRunningWorker true
# not running any jobs, deferring all 100% of the jobs
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker false
# defer 99% of the jobs, only letting 1% processed
/chatops run feature set defer_sidekiq_jobs_SlowRunningWorker 99
# only running 10% of the jobs, deferring 90% of the jobs
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker 10
# defer 50% of the jobs
/chatops run feature set defer_sidekiq_jobs_SlowRunningWorker 50
# running 50% of the jobs, deferring 50% of the jobs
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker 50
# stop deferring the jobs, jobs are being processed normally
/chatops run feature set defer_sidekiq_jobs_SlowRunningWorker false
# back to running all jobs normally
/chatops run feature set run_sidekiq_jobs_SlowRunningWorker true
```
NOTE:
The percentage of time value denotes the percentage of time the jobs are being deferred (instead of being processed).
For example, setting to `99` means only 1% of the jobs are being processed at random.

View File

@ -47,7 +47,7 @@ If the highest number stable branch is unclear, check the [GitLab blog](https://
|:------------------------|:----------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [Ruby](#2-ruby) | `3.0.x` | From GitLab 15.10, Ruby 3.0 is required. You must use the standard MRI implementation of Ruby. We love [JRuby](https://www.jruby.org/) and [Rubinius](https://github.com/rubinius/rubinius#the-rubinius-language-platform), but GitLab needs several Gems that have native extensions. |
| [RubyGems](#3-rubygems) | `3.4.x` | A specific RubyGems version is not fully needed, but it's recommended to update so you can enjoy some known performance improvements. |
| [Go](#4-go) | `1.18.x` | From GitLab 15.6, Go 1.18 or later is required. |
| [Go](#4-go) | `1.19.x` | From GitLab 16.1, Go 1.19 or later is required. |
| [Git](#git) | `2.38.x` | From GitLab 15.8, Git 2.38.x and later is required. It's highly recommended that you use the [Git version provided by Gitaly](#git). |
| [Node.js](#5-node) | `18.16.x` | From GitLab 16.1, Node.js 18.16 or later is required. |
@ -247,11 +247,11 @@ Linux. You can find downloads for other platforms at the
# Remove former Go installation folder
sudo rm -rf /usr/local/go
curl --remote-name --location --progress-bar "https://go.dev/dl/go1.18.8.linux-amd64.tar.gz"
echo '4d854c7bad52d53470cf32f1b287a5c0c441dc6b98306dea27358e099698142a go1.18.8.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.18.8.linux-amd64.tar.gz
curl --remote-name --location --progress-bar "https://go.dev/dl/go1.19.10.linux-amd64.tar.gz"
echo '8b045a483d3895c6edba2e90a9189262876190dbbd21756870cdd63821810677 go1.19.10.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.19.10.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,gofmt} /usr/local/bin/
rm go1.18.8.linux-amd64.tar.gz
rm go1.19.10.linux-amd64.tar.gz
```
## 5. Node

View File

@ -35,7 +35,6 @@ The following Rake tasks are available for use with GitLab:
| [Migrate snippets to Git](migrate_snippets.md) | Migrate GitLab Snippets to Git repositories, and show the migration status. |
| [Praefect Rake tasks](../administration/raketasks/praefect.md) | [Praefect](../administration/gitaly/praefect.md)-related tasks. |
| [Project import/export](../administration/raketasks/project_import_export.md) | Prepare for [project exports and imports](../user/project/settings/import_export.md). |
| [Sample Prometheus data](generate_sample_prometheus_data.md) | Generate sample Prometheus data. |
| [Sidekiq job migration](../administration/sidekiq/sidekiq_job_migration.md) | Migrate Sidekiq jobs scheduled for future dates to a new queue. |
| [Service Desk email](../administration/raketasks/service_desk_email.md) | Service Desk email-related tasks. |
| [SMTP maintenance](../administration/raketasks/smtp.md) | SMTP-related tasks. |

View File

@ -297,4 +297,3 @@ and customized to fit your workflow. Here are some helpful resources for further
1. [Incremental rollout to production](../cicd_variables.md#incremental-rollout-to-production)
1. [Disable jobs you don't need with CI/CD variables](../cicd_variables.md)
1. [Use your own buildpacks to build your application](../customize.md#custom-buildpacks)
1. [Prometheus monitoring](../../../user/project/integrations/prometheus.md)

View File

@ -302,4 +302,3 @@ and customized to fit your workflow. Here are some helpful resources for further
1. [Incremental rollout to production](../cicd_variables.md#incremental-rollout-to-production)
1. [Disable jobs you don't need with CI/CD variables](../cicd_variables.md)
1. [Use your own buildpacks to build your application](../customize.md#custom-buildpacks)
1. [Prometheus monitoring](../../../user/project/integrations/prometheus.md)

View File

@ -96,11 +96,11 @@ Download and install Go (for Linux, 64-bit):
# Remove former Go installation folder
sudo rm -rf /usr/local/go
curl --remote-name --location --progress-bar "https://go.dev/dl/go1.18.8.linux-amd64.tar.gz"
echo '4d854c7bad52d53470cf32f1b287a5c0c441dc6b98306dea27358e099698142a go1.18.8.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.18.8.linux-amd64.tar.gz
curl --remote-name --location --progress-bar "https://go.dev/dl/go1.19.10.linux-amd64.tar.gz"
echo '8b045a483d3895c6edba2e90a9189262876190dbbd21756870cdd63821810677 go1.19.10.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.19.10.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,gofmt} /usr/local/bin/
rm go1.18.8.linux-amd64.tar.gz
rm go1.19.10.linux-amd64.tar.gz
```
### 6. Update Git

View File

@ -160,6 +160,30 @@ Any group owner can approve or decline the request.
If you change your mind before your request is approved, select
**Withdraw Access Request**.
## View group members
To view a group's members:
1. On the left sidebar, at the top, select **Search GitLab** (**{search}**) to find your group.
1. Select **Manage > Members**.
A table displays the member's:
- **Account** name and username
- **Source** of their [membership](../project/members/index.md#membership-types).
For transparency, GitLab displays all membership sources of group members.
Members who have multiple membership sources are displayed and counted as separate members.
For example, if a member has been added to the group both directly and through inheritance,
the member is displayed twice in the **Members** table, with different sources,
and is counted as two individual members of the group.
- [**Max role**](../project/members/index.md#which-roles-you-can-assign) in the group
- **Expiration** date of their group membership
- **Activity** related to their account
NOTE:
The display of group members' **Source** might be inconsistent.
For more information, see [issue 414557](https://gitlab.com/gitlab-org/gitlab/-/issues/414557).
## Filter and sort members in a group
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/21727) in GitLab 12.6.

View File

@ -72,7 +72,6 @@ You can configure the following integrations.
| Packagist | Keep your PHP dependencies updated on Packagist. | **{check-circle}** Yes |
| [Pipelines emails](pipeline_status_emails.md) | Send the pipeline status to a list of recipients by email. | **{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 |
| [Pumble](pumble.md) | Send event notifications to a Pumble channel. | **{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

@ -81,7 +81,6 @@ Use the toggles to enable or disable features in the project.
| **Wiki** | **{check-circle}** Yes | Enables a separate system for [documentation](../wiki/index.md).
| **Snippets** | **{check-circle}** Yes | Enables [sharing of code and text](../../snippets.md).
| **Pages** | **{check-circle}** Yes | Allows you to [publish static websites](../pages/index.md).
| **Metrics Dashboard** | **{check-circle}** Yes | Control access to [metrics dashboard](../integrations/prometheus.md).
| **Releases** | **{check-circle}** Yes | Control access to [Releases](../releases/index.md).
| **Environments** | **{check-circle}** Yes | Control access to [Environments and Deployments](../../../ci/environments/index.md).
| **Feature flags** | **{check-circle}** Yes | Control access to [Feature flags](../../../operations/feature_flags.md).

View File

@ -20,18 +20,18 @@ cache:
paths:
- ${TF_ROOT}/.terraform/
.terraform:fmt: &terraform_fmt
.terraform:fmt:
stage: validate
script:
- gitlab-terraform fmt
allow_failure: true
.terraform:validate: &terraform_validate
.terraform:validate:
stage: validate
script:
- gitlab-terraform validate
.terraform:build: &terraform_build
.terraform:build:
stage: build
script:
- gitlab-terraform plan
@ -46,7 +46,7 @@ cache:
reports:
terraform: ${TF_ROOT}/plan.json
.terraform:deploy: &terraform_deploy
.terraform:deploy:
stage: deploy
script:
- gitlab-terraform apply
@ -56,7 +56,7 @@ cache:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
.terraform:destroy: &terraform_destroy
.terraform:destroy:
stage: cleanup
script:
- gitlab-terraform destroy

View File

@ -22,7 +22,7 @@ variables:
TF_ROOT: ${CI_PROJECT_DIR} # The relative path to the root directory of the Terraform project
TF_STATE_NAME: default # The name of the state file used by the GitLab Managed Terraform state backend
.terraform:fmt: &terraform_fmt
.terraform:fmt:
stage: validate
script:
- gitlab-terraform fmt
@ -33,7 +33,7 @@ variables:
when: never
- if: $CI_COMMIT_BRANCH # If there's no open merge request, add it to a *branch* pipeline instead.
.terraform:validate: &terraform_validate
.terraform:validate:
stage: validate
script:
- gitlab-terraform validate
@ -43,7 +43,7 @@ variables:
when: never
- if: $CI_COMMIT_BRANCH # If there's no open merge request, add it to a *branch* pipeline instead.
.terraform:build: &terraform_build
.terraform:build:
stage: build
script:
- gitlab-terraform plan
@ -63,7 +63,7 @@ variables:
when: never
- if: $CI_COMMIT_BRANCH # If there's no open merge request, add it to a *branch* pipeline instead.
.terraform:deploy: &terraform_deploy
.terraform:deploy:
stage: deploy
script:
- gitlab-terraform apply
@ -73,7 +73,7 @@ variables:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
.terraform:destroy: &terraform_destroy
.terraform:destroy:
stage: cleanup
script:
- gitlab-terraform destroy

View File

@ -4,13 +4,19 @@ module Gitlab
module SidekiqMiddleware
class DeferJobs
DELAY = ENV.fetch("SIDEKIQ_DEFER_JOBS_DELAY", 5.minutes)
FEATURE_FLAG_PREFIX = "defer_sidekiq_jobs"
RUN_FEATURE_FLAG_PREFIX = "run_sidekiq_jobs"
DatabaseHealthStatusChecker = Struct.new(:id, :job_class_name)
DEFERRED_COUNTER = :sidekiq_jobs_deferred_total
def initialize
@metrics = init_metrics
end
# There are 2 scenarios under which this middleware defers a job
# 1. defer_sidekiq_jobs_#{worker_name} FF, jobs are deferred indefinitely until this feature flag
# is turned off or when Feature.enabled? returns false by chance while using `percentage of time` value.
# 1. When run_sidekiq_jobs_#{worker_name} FF is disabled. This FF is enabled by default
# for all workers.
# 2. Gitlab::Database::HealthStatus, on evaluating the db health status if it returns any indicator
# with stop signal, the jobs will be delayed by 'x' seconds (set in worker).
def call(worker, job, _queue)
@ -26,7 +32,7 @@ module Gitlab
job['deferred_count'] += 1
worker.class.perform_in(delay, *job['args'])
counter.increment({ worker: worker.class.name })
@metrics.fetch(DEFERRED_COUNTER).increment({ worker: worker.class.name })
# This breaks the middleware chain and return
return
@ -38,18 +44,19 @@ module Gitlab
private
def defer_job_info(worker_class, job)
if defer_job_by_ff?(worker_class)
if !run_job_by_ff?(worker_class)
[true, DELAY, :feature_flag]
elsif defer_job_by_database_health_signal?(job, worker_class)
[true, worker_class.database_health_check_attrs[:delay_by], :database_health_check]
end
end
def defer_job_by_ff?(worker_class)
def run_job_by_ff?(worker_class)
# always returns true by default for all workers unless the FF is specifically disabled, e.g. during an incident
Feature.enabled?(
:"#{FEATURE_FLAG_PREFIX}_#{worker_class.name}",
:"#{RUN_FEATURE_FLAG_PREFIX}_#{worker_class.name}",
type: :worker,
default_enabled_if_undefined: false
default_enabled_if_undefined: true
)
end
@ -72,8 +79,10 @@ module Gitlab
Gitlab::Database::HealthStatus.evaluate(health_context).any?(&:stop?)
end
def counter
@counter ||= Gitlab::Metrics.counter(:sidekiq_jobs_deferred_total, 'The number of jobs deferred')
def init_metrics
{
DEFERRED_COUNTER => Gitlab::Metrics.counter(DEFERRED_COUNTER, 'The number of jobs deferred')
}
end
end
end

View File

@ -23421,6 +23421,9 @@ msgstr ""
msgid "Improve customer support with Service Desk"
msgstr ""
msgid "Improve indexing performance by increasing Sidekiq load. Values greater than the current shard limit (%{limit}) are not allowed."
msgstr ""
msgid "Improve quality with test cases"
msgstr ""
@ -31531,6 +31534,9 @@ msgstr ""
msgid "Number of shards"
msgstr ""
msgid "Number of shards for non-code indexing"
msgstr ""
msgid "OK"
msgstr ""
@ -33499,6 +33505,9 @@ msgstr ""
msgid "PipelineSchedules|All"
msgstr ""
msgid "PipelineSchedules|An error occurred while creating the pipeline schedule."
msgstr ""
msgid "PipelineSchedules|Are you sure you want to delete this pipeline schedule?"
msgstr ""

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
module RuboCop
module Cop
module Search
# Cop that prevents checking migration_has_finished? on deprecated migrations
#
# @example
#
# # bad
# def disable_project_joins_for_blob?
# Elastic::DataMigrationService
# .migration_has_finished?(:backfill_project_permissions_in_blobs_using_permutations)
# end
#
# # good
# def disable_project_joins_for_blob?
# Elastic::DataMigrationService.migration_has_finished?(:backfill_project_permissions_in_blobs)
# end
class AvoidCheckingFinishedOnDeprecatedMigrations < RuboCop::Cop::Base
MSG = 'Migration is deprecated and can not be used with `migration_has_finished?`.'
def_node_matcher :deprecated_migration?, <<~PATTERN
(send
(const (const {nil? cbase} :Elastic) :DataMigrationService) :migration_has_finished?
(sym :backfill_project_permissions_in_blobs_using_permutations))
PATTERN
RESTRICT_ON_SEND = %i[migration_has_finished?].freeze
def on_send(node)
add_offense(node) if deprecated_migration?(node)
end
end
end
end
end

View File

@ -151,7 +151,9 @@ function debug_rspec_variables() {
function handle_retry_rspec_in_new_process() {
local rspec_run_status="${1}"
if [[ $rspec_run_status -eq 2 ]]; then
if [[ $rspec_run_status -eq 3 ]]; then
echoerr "Not retrying failing examples since we failed early on purpose!"
elif [[ $rspec_run_status -eq 2 ]]; then
echoerr "Not retrying failing examples since there were errors happening outside of the RSpec examples!"
elif [[ $rspec_run_status -eq 1 ]]; then
# Experiment to retry failed examples in a new RSpec process: https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/1148

View File

@ -1,14 +1,29 @@
import MockAdapter from 'axios-mock-adapter';
import { GlForm } from '@gitlab/ui';
import { nextTick } from 'vue';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import { createAlert } from '~/alert';
import PipelineSchedulesForm from '~/ci/pipeline_schedules/components/pipeline_schedules_form.vue';
import RefSelector from '~/ref/components/ref_selector.vue';
import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
import TimezoneDropdown from '~/vue_shared/components/timezone_dropdown/timezone_dropdown.vue';
import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue';
import createPipelineScheduleMutation from '~/ci/pipeline_schedules/graphql/mutations/create_pipeline_schedule.mutation.graphql';
import { timezoneDataFixture } from '../../../vue_shared/components/timezone_dropdown/helpers';
import { createScheduleMutationResponse } from '../mock_data';
Vue.use(VueApollo);
jest.mock('~/alert');
jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn(),
joinPaths: jest.fn().mockReturnValue(''),
}));
describe('Pipeline schedules form', () => {
let wrapper;
@ -17,11 +32,21 @@ describe('Pipeline schedules form', () => {
const cron = '';
const dailyLimit = '';
const createComponent = (mountFn = shallowMountExtended, stubs = {}) => {
const createMutationHandlerSuccess = jest.fn().mockResolvedValue(createScheduleMutationResponse);
const createMutationHandlerFailed = jest.fn().mockRejectedValue(new Error('GraphQL error'));
const createMockApolloProvider = (
requestHandlers = [[createPipelineScheduleMutation, createMutationHandlerSuccess]],
) => {
return createMockApollo(requestHandlers);
};
const createComponent = (mountFn = shallowMountExtended, editing = false, requestHandlers) => {
wrapper = mountFn(PipelineSchedulesForm, {
propsData: {
timezoneData: timezoneDataFixture,
refParam: 'master',
editing,
},
provide: {
fullPath: 'gitlab-org/gitlab',
@ -31,8 +56,9 @@ describe('Pipeline schedules form', () => {
cronTimezone: '',
dailyLimit,
settingsLink: '',
schedulesPath: '/root/ci-project/-/pipeline_schedules',
},
stubs,
apolloProvider: createMockApolloProvider(requestHandlers),
});
};
@ -49,11 +75,17 @@ describe('Pipeline schedules form', () => {
const findValueInputs = () => wrapper.findAllByTestId('pipeline-form-ci-variable-value');
const findRemoveIcons = () => wrapper.findAllByTestId('remove-ci-variable-row');
beforeEach(() => {
createComponent();
});
const addVariableToForm = () => {
const input = findKeyInputs().at(0);
input.element.value = 'test_var_2';
input.trigger('change');
};
describe('Form elements', () => {
beforeEach(() => {
createComponent();
});
it('displays form', () => {
expect(findForm().exists()).toBe(true);
});
@ -102,19 +134,16 @@ describe('Pipeline schedules form', () => {
it('displays the submit and cancel buttons', () => {
expect(findSubmitButton().exists()).toBe(true);
expect(findCancelButton().exists()).toBe(true);
expect(findCancelButton().attributes('href')).toBe('/root/ci-project/-/pipeline_schedules');
});
});
describe('CI variables', () => {
let mock;
const addVariableToForm = () => {
const input = findKeyInputs().at(0);
input.element.value = 'test_var_2';
input.trigger('change');
};
beforeEach(() => {
// mock is needed when we fully mount
// downstream components request needs to be mocked
mock = new MockAdapter(axios);
createComponent(mountExtended);
});
@ -157,4 +186,77 @@ describe('Pipeline schedules form', () => {
expect(findVariableRows()).toHaveLength(1);
});
});
describe('schedule creation', () => {
describe('schedule creation success', () => {
let mock;
beforeEach(() => {
// mock is needed when we fully mount
// downstream components request needs to be mocked
mock = new MockAdapter(axios);
createComponent(mountExtended);
});
afterEach(() => {
mock.restore();
});
it('creates pipeline schedule', async () => {
findDescription().element.value = 'My schedule';
findDescription().trigger('change');
findTimezoneDropdown().vm.$emit('input', {
formattedTimezone: '[UTC-4] Eastern Time (US & Canada)',
identifier: 'America/New_York',
});
findIntervalComponent().vm.$emit('cronValue', '0 16 * * *');
addVariableToForm();
findSubmitButton().vm.$emit('click');
await waitForPromises();
expect(createMutationHandlerSuccess).toHaveBeenCalledWith({
input: {
active: true,
cron: '0 16 * * *',
cronTimezone: 'America/New_York',
description: 'My schedule',
projectPath: 'gitlab-org/gitlab',
ref: 'main',
variables: [
{
key: 'test_var_2',
value: '',
variableType: 'ENV_VAR',
},
],
},
});
expect(visitUrl).toHaveBeenCalledWith('/root/ci-project/-/pipeline_schedules');
expect(createAlert).not.toHaveBeenCalled();
});
});
describe('schedule creation failure', () => {
beforeEach(() => {
createComponent(shallowMountExtended, false, [
[createPipelineScheduleMutation, createMutationHandlerFailed],
]);
});
it('shows error for failed pipeline schedule creation', async () => {
findSubmitButton().vm.$emit('click');
await waitForPromises();
expect(createAlert).toHaveBeenCalledWith({
message: 'An error occurred while creating the pipeline schedule.',
});
});
});
});
});

View File

@ -57,6 +57,7 @@ describe('Pipeline schedules app', () => {
wrapper = mountExtended(PipelineSchedules, {
provide: {
fullPath: 'gitlab-org/gitlab',
newSchedulePath: '/root/ci-project/-/pipeline_schedules/new',
},
mocks: {
$toast,
@ -101,6 +102,10 @@ describe('Pipeline schedules app', () => {
expect(findLoadingIcon().exists()).toBe(false);
});
it('new schedule button links to new schedule path', () => {
expect(findNewButton().attributes('href')).toBe('/root/ci-project/-/pipeline_schedules/new');
});
});
describe('fetching pipeline schedules', () => {

View File

@ -1,6 +1,7 @@
import { GlButton } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import PipelineScheduleActions from '~/ci/pipeline_schedules/components/table/cells/pipeline_schedule_actions.vue';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import {
mockPipelineScheduleNodes,
mockPipelineScheduleCurrentUser,
@ -28,6 +29,7 @@ describe('Pipeline schedule actions', () => {
const findDeleteBtn = () => wrapper.findByTestId('delete-pipeline-schedule-btn');
const findTakeOwnershipBtn = () => wrapper.findByTestId('take-ownership-pipeline-schedule-btn');
const findPlayScheduleBtn = () => wrapper.findByTestId('play-pipeline-schedule-btn');
const findEditScheduleBtn = () => wrapper.findByTestId('edit-pipeline-schedule-btn');
it('displays buttons when user is the owner of schedule and has adminPipelineSchedule permissions', () => {
createComponent();
@ -76,4 +78,15 @@ describe('Pipeline schedule actions', () => {
playPipelineSchedule: [[mockPipelineScheduleNodes[0].id]],
});
});
it('edit button links to edit schedule path', () => {
createComponent();
const { schedule } = defaultProps;
const id = getIdFromGraphQLId(schedule.id);
const expectedPath = `${schedule.editPath}?id=${id}`;
expect(findEditScheduleBtn().attributes('href')).toBe(expectedPath);
});
});

View File

@ -79,4 +79,14 @@ export const takeOwnershipMutationResponse = {
},
};
export const createScheduleMutationResponse = {
data: {
pipelineScheduleCreate: {
clientMutationId: null,
errors: [],
__typename: 'PipelineScheduleCreatePayload',
},
},
};
export { mockGetPipelineSchedulesGraphQLResponse };

View File

@ -219,4 +219,17 @@ describe('Interval Pattern Input Component', () => {
expect(findIcon().exists()).toBe(false);
});
});
describe('cronValue event', () => {
it('emits cronValue event with cron value', async () => {
createWrapper();
findCustomInput().element.value = '0 16 * * *';
findCustomInput().trigger('input');
await nextTick();
expect(wrapper.emitted()).toEqual({ cronValue: [['0 16 * * *']] });
});
});
});

View File

@ -1,5 +1,7 @@
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import todoMarkDoneMutation from '~/graphql_shared/mutations/todo_mark_done.mutation.graphql';
import SidebarTodo from '~/vue_shared/alert_details/components/sidebar/sidebar_todo.vue';
import createAlertTodoMutation from '~/vue_shared/alert_details/graphql/mutations/alert_todo_create.mutation.graphql';
@ -9,41 +11,39 @@ const mockAlert = mockAlerts[0];
describe('Alert Details Sidebar To Do', () => {
let wrapper;
let requestHandler;
function mountComponent({ data, sidebarCollapsed = true, loading = false, stubs = {} } = {}) {
const defaultHandler = {
createAlertTodo: jest.fn().mockResolvedValue({}),
markAsDone: jest.fn().mockResolvedValue({}),
};
const createMockApolloProvider = (handler) => {
Vue.use(VueApollo);
requestHandler = handler;
return createMockApollo([
[todoMarkDoneMutation, handler.markAsDone],
[createAlertTodoMutation, handler.createAlertTodo],
]);
};
function mountComponent({ data, sidebarCollapsed = true, handler = defaultHandler } = {}) {
wrapper = mount(SidebarTodo, {
apolloProvider: createMockApolloProvider(handler),
propsData: {
alert: { ...mockAlert },
...data,
sidebarCollapsed,
projectPath: 'projectPath',
},
mocks: {
$apollo: {
mutate: jest.fn(),
queries: {
alert: {
loading,
},
},
},
},
stubs,
});
}
const findToDoButton = () => wrapper.find('[data-testid="alert-todo-button"]');
describe('updating the alert to do', () => {
const mockUpdatedMutationResult = {
data: {
updateAlertTodo: {
errors: [],
alert: {},
},
},
};
describe('adding a todo', () => {
beforeEach(() => {
mountComponent({
@ -60,18 +60,15 @@ describe('Alert Details Sidebar To Do', () => {
});
it('calls `$apollo.mutate` with `createAlertTodoMutation` mutation and variables containing `iid`, `todoEvent`, & `projectPath`', async () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
findToDoButton().trigger('click');
await nextTick();
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: createAlertTodoMutation,
variables: {
expect(requestHandler.createAlertTodo).toHaveBeenCalledWith(
expect.objectContaining({
iid: '1527542',
projectPath: 'projectPath',
},
});
}),
);
});
});
@ -91,17 +88,11 @@ describe('Alert Details Sidebar To Do', () => {
});
it('calls `$apollo.mutate` with `todoMarkDoneMutation` mutation and variables containing `id`', async () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockResolvedValue(mockUpdatedMutationResult);
findToDoButton().trigger('click');
await nextTick();
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: todoMarkDoneMutation,
update: expect.anything(),
variables: {
id: '1234',
},
expect(requestHandler.markAsDone).toHaveBeenCalledWith({
id: '1234',
});
});
});

View File

@ -5,19 +5,10 @@ require 'spec_helper'
RSpec.describe Gitlab::SidekiqMiddleware::DeferJobs, feature_category: :scalability do
let(:job) { { 'jid' => 123, 'args' => [456] } }
let(:queue) { 'test_queue' }
let(:deferred_worker) do
let(:test_worker) do
Class.new do
def self.name
'TestDeferredWorker'
end
include ApplicationWorker
end
end
let(:undeferred_worker) do
Class.new do
def self.name
'UndeferredWorker'
'TestWorker'
end
include ApplicationWorker
end
@ -26,55 +17,43 @@ RSpec.describe Gitlab::SidekiqMiddleware::DeferJobs, feature_category: :scalabil
subject { described_class.new }
before do
stub_const('TestDeferredWorker', deferred_worker)
stub_const('UndeferredWorker', undeferred_worker)
stub_const('TestWorker', test_worker)
end
describe '#call' do
context 'with worker not opted for database health check' do
context 'when sidekiq_defer_jobs feature flag is enabled for a worker' do
context 'when run_sidekiq_jobs feature flag is disabled' do
let(:deferred_jobs_metric) { instance_double(Prometheus::Client::Counter, increment: true) }
before do
stub_feature_flags("defer_sidekiq_jobs_#{TestDeferredWorker.name}": true)
stub_feature_flags("defer_sidekiq_jobs_#{UndeferredWorker.name}": false)
stub_feature_flags(run_sidekiq_jobs_TestWorker: false)
allow(Gitlab::Metrics).to receive(:counter).and_call_original
allow(Gitlab::Metrics).to receive(:counter).with(described_class::DEFERRED_COUNTER, anything)
.and_return(deferred_jobs_metric)
end
context 'for the affected worker' do
it 'defers the job' do
expect(TestDeferredWorker).to receive(:perform_in).with(described_class::DELAY, *job['args'])
expect { |b| subject.call(TestDeferredWorker.new, job, queue, &b) }.not_to yield_control
end
it 'defers the job' do
expect(TestWorker).to receive(:perform_in).with(described_class::DELAY, *job['args'])
expect { |b| subject.call(TestWorker.new, job, queue, &b) }.not_to yield_control
end
it 'increments the defer_count' do
(1..5).each do |count|
subject.call(TestDeferredWorker.new, job, queue)
expect(job).to include('deferred_count' => count)
end
it 'increments the defer_count' do
(1..5).each do |count|
subject.call(TestWorker.new, job, queue)
expect(job).to include('deferred_count' => count)
end
end
context 'for other workers' do
it 'runs the job normally' do
expect { |b| subject.call(UndeferredWorker.new, job, queue, &b) }.to yield_control
end
end
it 'increments the counter' do
expect(deferred_jobs_metric).to receive(:increment).with({ worker: "TestWorker" })
it 'increments the metric counter' do
subject.call(TestDeferredWorker.new, job, queue)
counter = ::Gitlab::Metrics.registry.get(:sidekiq_jobs_deferred_total)
expect(counter.get({ worker: "TestDeferredWorker" })).to eq(1)
subject.call(TestWorker.new, job, queue)
end
end
context 'when sidekiq_defer_jobs feature flag is disabled' do
before do
stub_feature_flags("defer_sidekiq_jobs_#{TestDeferredWorker.name}": false)
stub_feature_flags("defer_sidekiq_jobs_#{UndeferredWorker.name}": false)
end
context 'when run_sidekiq_jobs feature flag is enabled' do
it 'runs the job normally' do
expect { |b| subject.call(TestDeferredWorker.new, job, queue, &b) }.to yield_control
expect { |b| subject.call(UndeferredWorker.new, job, queue, &b) }.to yield_control
expect { |b| subject.call(TestWorker.new, job, queue, &b) }.to yield_control
end
end
end
@ -90,14 +69,12 @@ RSpec.describe Gitlab::SidekiqMiddleware::DeferJobs, feature_category: :scalabil
end
before do
stub_feature_flags("defer_sidekiq_jobs_#{TestDeferredWorker.name}": false)
TestDeferredWorker.defer_on_database_health_signal(*health_signal_attrs.values)
TestWorker.defer_on_database_health_signal(*health_signal_attrs.values)
end
context 'without any stop signal from database health check' do
it 'runs the job normally' do
expect { |b| subject.call(TestDeferredWorker.new, job, queue, &b) }.to yield_control
expect { |b| subject.call(TestWorker.new, job, queue, &b) }.to yield_control
end
end
@ -108,9 +85,9 @@ RSpec.describe Gitlab::SidekiqMiddleware::DeferJobs, feature_category: :scalabil
end
it 'defers the job by set time' do
expect(TestDeferredWorker).to receive(:perform_in).with(health_signal_attrs[:delay], *job['args'])
expect(TestWorker).to receive(:perform_in).with(health_signal_attrs[:delay], *job['args'])
TestDeferredWorker.perform_async(*job['args'])
TestWorker.perform_async(*job['args'])
end
end
end

View File

@ -31,7 +31,6 @@ RSpec.describe Gitlab::SidekiqMiddleware do
shared_examples "a middleware chain" do
before do
configurator.call(chain)
stub_feature_flags("defer_sidekiq_jobs_#{worker_class.name}": false) # not letting this worker deferring its jobs
end
it "passes through the right middlewares", :aggregate_failures do
enabled_sidekiq_middlewares.each do |middleware|

View File

@ -0,0 +1,44 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe UpdateElasticsearchNumberOfShardsInApplicationSettingsForGitlabCom, feature_category: :global_search do
let(:settings) { table(:application_settings) }
describe "#up" do
it 'does nothing when not in gitlab.com' do
record = settings.create!
expect { migrate! }.not_to change { record.reload.elasticsearch_worker_number_of_shards }
end
it 'updates elasticsearch_worker_number_of_shards when gitlab.com' do
allow(Gitlab).to receive(:com?).and_return(true)
record = settings.create!
expect { migrate! }.to change { record.reload.elasticsearch_worker_number_of_shards }.from(2).to(16)
end
end
describe "#down" do
it 'does nothing when not in gitlab.com' do
record = settings.create!(elasticsearch_worker_number_of_shards: 16)
migrate!
expect { schema_migrate_down! }.not_to change { record.reload.elasticsearch_worker_number_of_shards }
end
it 'updates elasticsearch_worker_number_of_shards when gitlab.com' do
allow(Gitlab).to receive(:com?).and_return(true)
record = settings.create!(elasticsearch_worker_number_of_shards: 16)
migrate!
expect { schema_migrate_down! }.to change { record.reload.elasticsearch_worker_number_of_shards }.from(16).to(2)
end
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
require 'rubocop_spec_helper'
require_relative '../../../../rubocop/cop/search/avoid_checking_finished_on_deprecated_migrations'
RSpec.describe RuboCop::Cop::Search::AvoidCheckingFinishedOnDeprecatedMigrations, feature_category: :global_search do
context 'when a deprecated class is used with migration_has_finished?' do
it 'flags it as an offense' do
expect_offense <<~SOURCE
return if Elastic::DataMigrationService.migration_has_finished?(:backfill_project_permissions_in_blobs_using_permutations)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Migration is deprecated and can not be used with `migration_has_finished?`.
SOURCE
end
end
context 'when a non deprecated class is used with migration_has_finished?' do
it 'does not flag it as an offense' do
expect_no_offenses <<~SOURCE
return if Elastic::DataMigrationService.migration_has_finished?(:backfill_project_permissions_in_blobs)
SOURCE
end
end
context 'when migration_has_finished? method is called on another class' do
it 'does not flag it as an offense' do
expect_no_offenses <<~SOURCE
return if Klass.migration_has_finished?(:backfill_project_permissions_in_blobs_using_permutations)
SOURCE
end
end
end

View File

@ -122,7 +122,7 @@ RSpec.configure do |config|
warn "********************************************************************************************"
warn
exit 1
exit 3
end
end