Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
7f5e08060f
commit
49d26b2348
113 changed files with 1830 additions and 558 deletions
|
@ -160,7 +160,6 @@ linters:
|
|||
- 'app/views/projects/_gitlab_import_modal.html.haml'
|
||||
- 'app/views/projects/_home_panel.html.haml'
|
||||
- 'app/views/projects/_import_project_pane.html.haml'
|
||||
- 'app/views/projects/_issuable_by_email.html.haml'
|
||||
- 'app/views/projects/_readme.html.haml'
|
||||
- 'app/views/projects/artifacts/_artifact.html.haml'
|
||||
- 'app/views/projects/artifacts/_tree_file.html.haml'
|
||||
|
|
|
@ -2473,50 +2473,3 @@ Gitlab/NamespacedClass:
|
|||
- 'spec/support/sidekiq_middleware.rb'
|
||||
- 'spec/tasks/gitlab/task_helpers_spec.rb'
|
||||
- 'spec/uploaders/object_storage_spec.rb'
|
||||
|
||||
# WIP: https://gitlab.com/gitlab-org/gitlab/-/issues/299105
|
||||
Style/FrozenStringLiteralComment:
|
||||
Exclude:
|
||||
- 'Gemfile'
|
||||
- 'Rakefile'
|
||||
- 'app/views/dashboard/issues.atom.builder'
|
||||
- 'app/views/dashboard/projects/index.atom.builder'
|
||||
- 'app/views/events/_event.atom.builder'
|
||||
- 'app/views/groups/issues.atom.builder'
|
||||
- 'app/views/groups/show.atom.builder'
|
||||
- 'app/views/issues/_issue.atom.builder'
|
||||
- 'app/views/issues/_issues_calendar.ics.ruby'
|
||||
- 'app/views/layouts/xml.atom.builder'
|
||||
- 'app/views/projects/commits/_commit.atom.builder'
|
||||
- 'app/views/projects/commits/show.atom.builder'
|
||||
- 'app/views/projects/issues/index.atom.builder'
|
||||
- 'app/views/projects/show.atom.builder'
|
||||
- 'app/views/projects/tags/_tag.atom.builder'
|
||||
- 'app/views/projects/tags/index.atom.builder'
|
||||
- 'app/views/users/show.atom.builder'
|
||||
- 'bin/secpick'
|
||||
- 'danger/changes_size/Dangerfile'
|
||||
- 'danger/metadata/Dangerfile'
|
||||
- 'scripts/flaky_examples/detect-new-flaky-examples'
|
||||
- 'scripts/flaky_examples/prune-old-flaky-examples'
|
||||
- 'scripts/gather-test-memory-data'
|
||||
- 'scripts/generate-gems-memory-metrics-static'
|
||||
- 'scripts/generate-gems-size-metrics-static'
|
||||
- 'scripts/generate-memory-metrics-on-boot'
|
||||
- 'scripts/generate-test-mapping'
|
||||
- 'scripts/gitaly-test-build'
|
||||
- 'scripts/gitaly-test-spawn'
|
||||
- 'scripts/gitaly_test.rb'
|
||||
- 'scripts/insert-rspec-profiling-data'
|
||||
- 'scripts/lint-rugged'
|
||||
- 'scripts/merge-html-reports'
|
||||
- 'scripts/merge-reports'
|
||||
- 'scripts/merge-simplecov'
|
||||
- 'scripts/no-ee-check'
|
||||
- 'scripts/pack-test-mapping'
|
||||
- 'scripts/static-analysis'
|
||||
- 'scripts/sync-reports'
|
||||
- 'scripts/unpack-test-mapping'
|
||||
- 'scripts/update-feature-categories'
|
||||
- 'scripts/used-feature-flags'
|
||||
- 'scripts/verify-tff-mapping'
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rails', '~> 6.0.3.1'
|
||||
|
|
2
Rakefile
2
Rakefile
|
@ -1,4 +1,6 @@
|
|||
#!/usr/bin/env rake
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||
|
||||
|
|
1
app/assets/javascripts/feature_highlight/constants.js
Normal file
1
app/assets/javascripts/feature_highlight/constants.js
Normal file
|
@ -0,0 +1 @@
|
|||
export const POPOVER_TARGET_ID = 'feature-highlight-trigger';
|
|
@ -0,0 +1,101 @@
|
|||
<script>
|
||||
import {
|
||||
GlPopover,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
GlButton,
|
||||
GlSafeHtmlDirective as SafeHtml,
|
||||
} from '@gitlab/ui';
|
||||
import clusterPopover from '@gitlab/svgs/dist/illustrations/cluster_popover.svg';
|
||||
import { __ } from '~/locale';
|
||||
import { dismiss } from './feature_highlight_helper';
|
||||
import { POPOVER_TARGET_ID } from './constants';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlPopover,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
GlButton,
|
||||
},
|
||||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
props: {
|
||||
autoDevopsHelpPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
highlightId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
dismissEndpoint: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dismissed: false,
|
||||
triggerHidden: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
dismiss() {
|
||||
dismiss(this.dismissEndpoint, this.highlightId);
|
||||
this.$refs.popover.$emit('close');
|
||||
this.dismissed = true;
|
||||
},
|
||||
hideTrigger() {
|
||||
if (this.dismissed) {
|
||||
this.triggerHidden = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
clusterPopover,
|
||||
targetId: POPOVER_TARGET_ID,
|
||||
i18n: {
|
||||
highlightMessage: __('Allows you to add and manage Kubernetes clusters.'),
|
||||
autoDevopsProTipMessage: __(
|
||||
'Protip: %{linkStart}Auto DevOps%{linkEnd} uses Kubernetes clusters to deploy your code!',
|
||||
),
|
||||
dismissButtonLabel: __('Got it!'),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="gl-ml-3">
|
||||
<span v-if="!triggerHidden" :id="$options.targetId" class="feature-highlight"></span>
|
||||
<gl-popover
|
||||
ref="popover"
|
||||
:target="$options.targetId"
|
||||
:css-classes="['feature-highlight-popover']"
|
||||
triggers="hover"
|
||||
container="body"
|
||||
placement="right"
|
||||
boundary="viewport"
|
||||
@hidden="hideTrigger"
|
||||
>
|
||||
<span
|
||||
v-safe-html="$options.clusterPopover"
|
||||
class="feature-highlight-illustration gl-display-flex gl-justify-content-center gl-py-4 gl-w-full"
|
||||
></span>
|
||||
<div class="gl-px-4 gl-py-5">
|
||||
<p>
|
||||
{{ $options.i18n.highlightMessage }}
|
||||
</p>
|
||||
<p>
|
||||
<gl-sprintf :message="$options.i18n.autoDevopsProTipMessage">
|
||||
<template #link="{ content }">
|
||||
<gl-link class="gl-font-sm" :href="autoDevopsHelpPath">{{ content }}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<gl-button size="small" icon="thumb-up" variant="confirm" @click="dismiss">
|
||||
{{ $options.i18n.dismissButtonLabel }}
|
||||
</gl-button>
|
||||
</div>
|
||||
</gl-popover>
|
||||
</div>
|
||||
</template>
|
28
app/assets/javascripts/feature_highlight/index.js
Normal file
28
app/assets/javascripts/feature_highlight/index.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
import Vue from 'vue';
|
||||
|
||||
const init = async () => {
|
||||
const el = document.querySelector('.js-feature-highlight');
|
||||
|
||||
if (!el) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { autoDevopsHelpPath, highlight: highlightId, dismissEndpoint } = el.dataset;
|
||||
const { default: FeatureHighlight } = await import(
|
||||
/* webpackChunkName: 'feature_highlight' */ './feature_highlight_popover.vue'
|
||||
);
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
render: (h) =>
|
||||
h(FeatureHighlight, {
|
||||
props: {
|
||||
autoDevopsHelpPath,
|
||||
highlightId,
|
||||
dismissEndpoint,
|
||||
},
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
export default init;
|
169
app/assets/javascripts/issuable/components/issuable_by_email.vue
Normal file
169
app/assets/javascripts/issuable/components/issuable_by_email.vue
Normal file
|
@ -0,0 +1,169 @@
|
|||
<script>
|
||||
import {
|
||||
GlButton,
|
||||
GlModal,
|
||||
GlModalDirective,
|
||||
GlTooltipDirective,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
GlFormInputGroup,
|
||||
GlIcon,
|
||||
} from '@gitlab/ui';
|
||||
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
|
||||
import { sprintf, __ } from '~/locale';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
|
||||
export default {
|
||||
name: 'IssuableByEmail',
|
||||
components: {
|
||||
GlButton,
|
||||
GlModal,
|
||||
GlSprintf,
|
||||
GlLink,
|
||||
GlFormInputGroup,
|
||||
GlIcon,
|
||||
ModalCopyButton,
|
||||
},
|
||||
directives: {
|
||||
GlModal: GlModalDirective,
|
||||
GlTooltip: GlTooltipDirective,
|
||||
},
|
||||
inject: {
|
||||
initialEmail: {
|
||||
default: null,
|
||||
},
|
||||
issuableType: {
|
||||
default: '',
|
||||
},
|
||||
emailsHelpPagePath: {
|
||||
default: '',
|
||||
},
|
||||
quickActionsHelpPath: {
|
||||
default: '',
|
||||
},
|
||||
markdownHelpPath: {
|
||||
default: '',
|
||||
},
|
||||
resetPath: {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
email: this.initialEmail,
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
issuableName: this.issuableType === 'issue' ? 'issue' : 'merge request',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
mailToLink() {
|
||||
const subject = sprintf(__('Enter the %{name} title'), {
|
||||
name: this.issuableName,
|
||||
});
|
||||
const body = sprintf(__('Enter the %{name} description'), {
|
||||
name: this.issuableName,
|
||||
});
|
||||
// eslint-disable-next-line @gitlab/require-i18n-strings
|
||||
return `mailto:${this.email}?subject=${subject}&body=${body}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async resetIncomingEmailToken() {
|
||||
try {
|
||||
const {
|
||||
data: { new_address: newAddress },
|
||||
} = await axios.put(this.resetPath);
|
||||
this.email = newAddress;
|
||||
} catch {
|
||||
this.$toast.show(__('There was an error when reseting email token.'), { type: 'error' });
|
||||
}
|
||||
},
|
||||
cancelHandler() {
|
||||
this.$refs.modal.hide();
|
||||
},
|
||||
},
|
||||
modalId: 'issuable-email-modal',
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<gl-button v-gl-modal="$options.modalId" variant="link" data-testid="issuable-email-modal-btn"
|
||||
><gl-sprintf :message="__('Email a new %{name} to this project')"
|
||||
><template #name>{{ issuableName }}</template></gl-sprintf
|
||||
></gl-button
|
||||
>
|
||||
<gl-modal ref="modal" :modal-id="$options.modalId">
|
||||
<template #modal-title>
|
||||
<gl-sprintf :message="__('Create new %{name} by email')">
|
||||
<template #name>{{ issuableName }}</template>
|
||||
</gl-sprintf>
|
||||
</template>
|
||||
<p>
|
||||
<gl-sprintf
|
||||
:message="
|
||||
__(
|
||||
'You can create a new %{name} inside this project by sending an email to the following email address:',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #name>{{ issuableName }}</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<gl-form-input-group :value="email" readonly select-on-click class="gl-mb-4">
|
||||
<template #append>
|
||||
<modal-copy-button :text="email" :title="__('Copy')" :modal-id="$options.modalId" />
|
||||
<gl-button
|
||||
v-gl-tooltip.hover
|
||||
:href="mailToLink"
|
||||
:title="__('Send email')"
|
||||
icon="mail"
|
||||
data-testid="mail-to-btn"
|
||||
/>
|
||||
</template>
|
||||
</gl-form-input-group>
|
||||
<p>
|
||||
<gl-sprintf
|
||||
:message="
|
||||
__(
|
||||
'The subject will be used as the title of the new issue, and the message will be the description. %{quickActionsLinkStart}Quick actions%{quickActionsLinkEnd} and styling with %{markdownLinkStart}Markdown%{markdownLinkEnd} are supported.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #quickActionsLink="{ content }">
|
||||
<gl-link :href="quickActionsHelpPath" target="_blank">{{ content }}</gl-link>
|
||||
</template>
|
||||
<template #markdownLink="{ content }">
|
||||
<gl-link :href="markdownHelpPath" target="_blank">{{ content }}</gl-link>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<p>
|
||||
<gl-sprintf
|
||||
:message="
|
||||
__(
|
||||
'This is a private email address %{helpIcon} generated just for you. Anyone who gets ahold of it can create issues or merge requests as if they were you. You should %{resetLinkStart}reset it%{resetLinkEnd} if that ever happens.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #helpIcon>
|
||||
<gl-link :href="emailsHelpPagePath" target="_blank"
|
||||
><gl-icon class="gl-text-blue-600" name="question-o"
|
||||
/></gl-link>
|
||||
</template>
|
||||
<template #resetLink="{ content }">
|
||||
<gl-button
|
||||
variant="link"
|
||||
data-testid="incoming-email-token-reset"
|
||||
@click="resetIncomingEmailToken"
|
||||
>{{ content }}</gl-button
|
||||
>
|
||||
</template>
|
||||
</gl-sprintf>
|
||||
</p>
|
||||
<template #modal-footer>
|
||||
<gl-button category="secondary" @click="cancelHandler">{{ s__('Cancel') }}</gl-button>
|
||||
</template>
|
||||
</gl-modal>
|
||||
</div>
|
||||
</template>
|
35
app/assets/javascripts/issuable/init_issuable_by_email.js
Normal file
35
app/assets/javascripts/issuable/init_issuable_by_email.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import Vue from 'vue';
|
||||
import { GlToast } from '@gitlab/ui';
|
||||
import IssuableByEmail from './components/issuable_by_email.vue';
|
||||
|
||||
Vue.use(GlToast);
|
||||
|
||||
export default () => {
|
||||
const el = document.querySelector('.js-issueable-by-email');
|
||||
|
||||
if (!el) return null;
|
||||
|
||||
const {
|
||||
initialEmail,
|
||||
issuableType,
|
||||
emailsHelpPagePath,
|
||||
quickActionsHelpPath,
|
||||
markdownHelpPath,
|
||||
resetPath,
|
||||
} = el.dataset;
|
||||
|
||||
return new Vue({
|
||||
el,
|
||||
provide: {
|
||||
initialEmail,
|
||||
issuableType,
|
||||
emailsHelpPagePath,
|
||||
quickActionsHelpPath,
|
||||
markdownHelpPath,
|
||||
resetPath,
|
||||
},
|
||||
render(h) {
|
||||
return h(IssuableByEmail);
|
||||
},
|
||||
});
|
||||
};
|
|
@ -1,35 +1,7 @@
|
|||
import $ from 'jquery';
|
||||
import axios from './lib/utils/axios_utils';
|
||||
import { deprecatedCreateFlash as flash } from './flash';
|
||||
import { s__, __ } from './locale';
|
||||
import issuableInitBulkUpdateSidebar from './issuable_init_bulk_update_sidebar';
|
||||
|
||||
export default class IssuableIndex {
|
||||
constructor(pagePrefix) {
|
||||
issuableInitBulkUpdateSidebar.init(pagePrefix);
|
||||
IssuableIndex.resetIncomingEmailToken();
|
||||
}
|
||||
|
||||
static resetIncomingEmailToken() {
|
||||
const $resetToken = $('.incoming-email-token-reset');
|
||||
|
||||
$resetToken.on('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
$resetToken.text(s__('EmailToken|resetting...'));
|
||||
|
||||
axios
|
||||
.put($resetToken.attr('href'))
|
||||
.then(({ data }) => {
|
||||
$('#issuable_email').val(data.new_address).focus();
|
||||
|
||||
$resetToken.text(s__('EmailToken|reset it'));
|
||||
})
|
||||
.catch(() => {
|
||||
flash(__('There was an error when reseting email token.'));
|
||||
|
||||
$resetToken.text(s__('EmailToken|reset it'));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { FILTERED_SEARCH } from '~/pages/constants';
|
|||
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
|
||||
import initIssuablesList from '~/issues_list';
|
||||
import initManualOrdering from '~/manual_ordering';
|
||||
import initIssuableByEmail from '~/issuable/init_issuable_by_email';
|
||||
|
||||
IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
|
||||
|
||||
|
@ -24,3 +25,4 @@ new UsersSelect();
|
|||
|
||||
initManualOrdering();
|
||||
initIssuablesList();
|
||||
initIssuableByEmail();
|
||||
|
|
|
@ -6,6 +6,7 @@ import initFilteredSearch from '~/pages/search/init_filtered_search';
|
|||
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
|
||||
import { FILTERED_SEARCH } from '~/pages/constants';
|
||||
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
|
||||
import initIssuableByEmail from '~/issuable/init_issuable_by_email';
|
||||
|
||||
new IssuableIndex(ISSUABLE_INDEX.MERGE_REQUEST); // eslint-disable-line no-new
|
||||
|
||||
|
@ -19,3 +20,5 @@ initFilteredSearch({
|
|||
|
||||
new UsersSelect(); // eslint-disable-line no-new
|
||||
new ShortcutsNavigation(); // eslint-disable-line no-new
|
||||
|
||||
initIssuableByEmail();
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import initActivityCharts from '~/analytics/product_analytics/activity_charts_bundle';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => initActivityCharts());
|
||||
initActivityCharts();
|
||||
|
|
|
@ -83,6 +83,9 @@ export default {
|
|||
alertId: {
|
||||
default: '',
|
||||
},
|
||||
isThreatMonitoringPage: {
|
||||
default: false,
|
||||
},
|
||||
projectId: {
|
||||
default: '',
|
||||
},
|
||||
|
@ -364,7 +367,11 @@ export default {
|
|||
</alert-summary-row>
|
||||
<alert-details-table :alert="alert" :loading="loading" />
|
||||
</gl-tab>
|
||||
<gl-tab :data-testid="$options.tabsConfig[1].id" :title="$options.tabsConfig[1].title">
|
||||
<gl-tab
|
||||
v-if="isThreatMonitoringPage"
|
||||
:data-testid="$options.tabsConfig[1].id"
|
||||
:title="$options.tabsConfig[1].title"
|
||||
>
|
||||
<alert-metrics :dashboard-url="alert.metricsDashboardUrl" />
|
||||
</gl-tab>
|
||||
<gl-tab :data-testid="$options.tabsConfig[2].id" :title="$options.tabsConfig[2].title">
|
||||
|
|
|
@ -19,6 +19,10 @@ export default {
|
|||
projectId: {
|
||||
default: '',
|
||||
},
|
||||
// TODO remove this limitation in https://gitlab.com/gitlab-org/gitlab/-/issues/296717
|
||||
isThreatMonitoringPage: {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
props: {
|
||||
alert: {
|
||||
|
@ -62,6 +66,7 @@ export default {
|
|||
@alert-error="$emit('alert-error', $event)"
|
||||
/>
|
||||
<sidebar-status
|
||||
v-if="!isThreatMonitoringPage"
|
||||
:project-path="projectPath"
|
||||
:alert="alert"
|
||||
@toggle-sidebar="$emit('toggle-sidebar')"
|
||||
|
|
|
@ -9,11 +9,10 @@ export const SEVERITY_LEVELS = {
|
|||
UNKNOWN: s__('severity|Unknown'),
|
||||
};
|
||||
|
||||
export const DEFAULT_PAGE = 'OPERATIONS';
|
||||
|
||||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
export const PAGE_CONFIG = {
|
||||
OPERATIONS: {
|
||||
TITLE: 'OPERATIONS',
|
||||
// Tracks snowplow event when user views alert details
|
||||
TRACK_ALERTS_DETAILS_VIEWS_OPTIONS: {
|
||||
category: 'Alert Management',
|
||||
|
@ -26,4 +25,7 @@ export const PAGE_CONFIG = {
|
|||
label: 'Status',
|
||||
},
|
||||
},
|
||||
THREAT_MONITORING: {
|
||||
TITLE: 'THREAT_MONITORING',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -6,13 +6,13 @@ import createDefaultClient from '~/lib/graphql';
|
|||
import AlertDetails from './components/alert_details.vue';
|
||||
import sidebarStatusQuery from './graphql/queries/alert_sidebar_status.query.graphql';
|
||||
import createRouter from './router';
|
||||
import { DEFAULT_PAGE, PAGE_CONFIG } from './constants';
|
||||
import { PAGE_CONFIG } from './constants';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
||||
export default (selector) => {
|
||||
const domEl = document.querySelector(selector);
|
||||
const { alertId, projectPath, projectIssuesPath, projectId, page = DEFAULT_PAGE } = domEl.dataset;
|
||||
const { alertId, projectPath, projectIssuesPath, projectId, page } = domEl.dataset;
|
||||
const router = createRouter();
|
||||
|
||||
const resolvers = {
|
||||
|
@ -52,16 +52,19 @@ export default (selector) => {
|
|||
const provide = {
|
||||
projectPath,
|
||||
alertId,
|
||||
page,
|
||||
projectIssuesPath,
|
||||
projectId,
|
||||
};
|
||||
|
||||
if (page === DEFAULT_PAGE) {
|
||||
if (page === PAGE_CONFIG.OPERATIONS.TITLE) {
|
||||
const { TRACK_ALERTS_DETAILS_VIEWS_OPTIONS, TRACK_ALERT_STATUS_UPDATE_OPTIONS } = PAGE_CONFIG[
|
||||
page
|
||||
];
|
||||
provide.trackAlertsDetailsViewsOptions = TRACK_ALERTS_DETAILS_VIEWS_OPTIONS;
|
||||
provide.trackAlertStatusUpdateOptions = TRACK_ALERT_STATUS_UPDATE_OPTIONS;
|
||||
} else if (page === PAGE_CONFIG.THREAT_MONITORING.TITLE) {
|
||||
provide.isThreatMonitoringPage = true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
$bs-input-focus-border: #80bdff;
|
||||
$bs-input-focus-box-shadow: rgba(0, 123, 255, 0.25);
|
||||
|
||||
a:not(.btn):not(.gl-tab-nav-item),
|
||||
a:not(.btn),
|
||||
.gl-button.btn-link,
|
||||
.gl-button.btn-link:hover,
|
||||
.gl-button.btn-link:focus,
|
||||
|
@ -34,6 +34,7 @@
|
|||
.ide-pipeline .top-bar .controllers .controllers-buttons,
|
||||
.controllers-buttons svg,
|
||||
.nav-links li a.active,
|
||||
.gl-tabs-nav li a.gl-tab-nav-item-active,
|
||||
.md-area.is-focused {
|
||||
color: var(--ide-text-color, $gl-text-color);
|
||||
}
|
||||
|
@ -44,13 +45,15 @@
|
|||
}
|
||||
|
||||
.nav-links:not(.quick-links) li:not(.md-header-toolbar) a,
|
||||
.gl-tabs-nav li a,
|
||||
.dropdown-menu-inner-content,
|
||||
.file-row .file-row-icon svg,
|
||||
.file-row:hover .file-row-icon svg {
|
||||
color: var(--ide-text-color-secondary, $gl-text-color-secondary);
|
||||
}
|
||||
|
||||
.nav-links:not(.quick-links) li:not(.md-header-toolbar) {
|
||||
.nav-links:not(.quick-links) li:not(.md-header-toolbar),
|
||||
.gl-tabs-nav li {
|
||||
&:hover a,
|
||||
&.active a,
|
||||
a:hover,
|
||||
|
@ -61,6 +64,10 @@
|
|||
border-color: var(--ide-input-border, $gray-darkest);
|
||||
}
|
||||
}
|
||||
|
||||
a.gl-tab-nav-item-active {
|
||||
box-shadow: inset 0 -2px 0 0 var(--ide-input-border, $gray-darkest);
|
||||
}
|
||||
}
|
||||
|
||||
.drag-handle:hover {
|
||||
|
@ -142,6 +149,7 @@
|
|||
.md table:not(.code) tbody td,
|
||||
.md table:not(.code) tr th,
|
||||
.nav-links:not(.quick-links),
|
||||
.gl-tabs-nav,
|
||||
.common-note-form .md-area.is-focused .nav-links {
|
||||
border-color: var(--ide-border-color-alt, $white-dark);
|
||||
}
|
||||
|
@ -175,6 +183,10 @@
|
|||
border-color: var(--ide-highlight-accent, $gl-text-color);
|
||||
}
|
||||
|
||||
.gl-tabs-nav li a.gl-tab-nav-item-active {
|
||||
box-shadow: inset 0 -2px 0 0 var(--ide-highlight-accent, $gl-text-color);
|
||||
}
|
||||
|
||||
// for other themes, suppress different avatar default colors for simplicity
|
||||
.avatar-container {
|
||||
&,
|
||||
|
@ -304,6 +316,11 @@
|
|||
border-color: var(--ide-dropdown-hover-background, $border-color);
|
||||
}
|
||||
|
||||
.gl-tabs-nav {
|
||||
background-color: var(--ide-dropdown-hover-background, $white);
|
||||
box-shadow: inset 0 -2px 0 0 var(--ide-dropdown-hover-background, $border-color);
|
||||
}
|
||||
|
||||
.divider {
|
||||
background-color: var(--ide-dropdown-hover-background, $gray-100);
|
||||
border-color: var(--ide-dropdown-hover-background, $gray-100);
|
||||
|
|
|
@ -97,7 +97,8 @@ $ide-commit-header-height: 48px;
|
|||
border-right: 1px solid var(--ide-border-color, $white-dark);
|
||||
border-bottom: 1px solid var(--ide-border-color, $white-dark);
|
||||
|
||||
&.active {
|
||||
&.active,
|
||||
.gl-tab-nav-item-active {
|
||||
background-color: var(--ide-highlight-background, $white);
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
@ -114,6 +115,42 @@ $ide-commit-header-height: 48px;
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gl-tab-content {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.gl-tabs-nav {
|
||||
border-width: 0;
|
||||
|
||||
li {
|
||||
padding: 0 !important;
|
||||
background: transparent !important;
|
||||
border: 0 !important;
|
||||
|
||||
a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: $grid-size $gl-padding !important;
|
||||
box-shadow: none !important;
|
||||
font-weight: normal !important;
|
||||
|
||||
background-color: var(--ide-background-hover, $gray-normal);
|
||||
border-right: 1px solid var(--ide-border-color, $white-dark);
|
||||
border-bottom: 1px solid var(--ide-border-color, $white-dark);
|
||||
|
||||
&.gl-tab-nav-item-active {
|
||||
background-color: var(--ide-highlight-background, $white);
|
||||
border-color: var(--ide-border-color, $white-dark);
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
.multi-file-tab-close svg {
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.multi-file-tab {
|
||||
|
@ -634,7 +671,8 @@ $ide-commit-header-height: 48px;
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
.nav-links,
|
||||
.gl-tabs-nav {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
|
@ -976,17 +1014,25 @@ $ide-commit-header-height: 48px;
|
|||
}
|
||||
|
||||
.ide-nav-form {
|
||||
.nav-links li {
|
||||
.nav-links li,
|
||||
.gl-tabs-nav li {
|
||||
width: 50%;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
|
||||
a {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
|
||||
&:not(.active) {
|
||||
&:not(.active),
|
||||
&:not(.gl-tab-nav-item-active) {
|
||||
background-color: var(--ide-dropdown-background, $gray-light);
|
||||
}
|
||||
|
||||
&.gl-tab-nav-item-active {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -108,18 +108,6 @@ ul.related-merge-requests > li {
|
|||
}
|
||||
}
|
||||
|
||||
.issuable-email-modal-btn {
|
||||
padding: 0;
|
||||
color: $blue-600;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.email-modal-input-group {
|
||||
margin-bottom: 10px;
|
||||
|
||||
|
|
|
@ -178,8 +178,8 @@ module EventsHelper
|
|||
def event_note_target_url(event)
|
||||
if event.commit_note?
|
||||
project_commit_url(event.project, event.note_target, anchor: dom_id(event.target))
|
||||
elsif event.project_snippet_note?
|
||||
project_snippet_url(event.project, event.note_target, anchor: dom_id(event.target))
|
||||
elsif event.snippet_note?
|
||||
gitlab_snippet_url(event.note_target, anchor: dom_id(event.target))
|
||||
elsif event.issue_note?
|
||||
project_issue_url(event.project, id: event.note_target, anchor: dom_id(event.target))
|
||||
elsif event.merge_request_note?
|
||||
|
|
|
@ -20,7 +20,8 @@ module Projects::AlertManagementHelper
|
|||
'alert-id' => alert_id,
|
||||
'project-path' => project.full_path,
|
||||
'project-id' => project.id,
|
||||
'project-issues-path' => project_issues_path(project)
|
||||
'project-issues-path' => project_issues_path(project),
|
||||
'page' => 'OPERATIONS'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -294,10 +294,14 @@ class Event < ApplicationRecord
|
|||
note? && target && target.for_merge_request?
|
||||
end
|
||||
|
||||
def project_snippet_note?
|
||||
def snippet_note?
|
||||
note? && target && target.for_snippet?
|
||||
end
|
||||
|
||||
def project_snippet_note?
|
||||
note? && target && target.for_project_snippet?
|
||||
end
|
||||
|
||||
def personal_snippet_note?
|
||||
note? && target && target.for_personal_snippet?
|
||||
end
|
||||
|
|
|
@ -259,6 +259,10 @@ class Note < ApplicationRecord
|
|||
noteable_type == 'AlertManagement::Alert'
|
||||
end
|
||||
|
||||
def for_project_snippet?
|
||||
noteable.is_a?(ProjectSnippet)
|
||||
end
|
||||
|
||||
def for_personal_snippet?
|
||||
noteable.is_a?(PersonalSnippet)
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
xml.title "#{current_user.name} issues"
|
||||
xml.link href: url_for(safe_params), rel: "self", type: "application/atom+xml"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
xml.title "Activity"
|
||||
xml.link href: dashboard_projects_url(rss_url_options), rel: "self", type: "application/atom+xml"
|
||||
xml.link href: dashboard_projects_url, rel: "alternate", type: "text/html"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
return unless event.visible_to_user?(current_user)
|
||||
|
||||
event = event.present
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
xml.title "#{@group.name} issues"
|
||||
xml.link href: url_for(safe_params), rel: "self", type: "application/atom+xml"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
xml.title "#{@group.name} activity"
|
||||
xml.link href: group_url(@group, rss_url_options), rel: "self", type: "application/atom+xml"
|
||||
xml.link href: group_url(@group), rel: "alternate", type: "text/html"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
xml.entry do
|
||||
xml.id project_issue_url(issue.project, issue)
|
||||
xml.link href: project_issue_url(issue.project, issue)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
cal = Icalendar::Calendar.new
|
||||
cal.prodid = '-//GitLab//NONSGML GitLab//EN'
|
||||
cal.x_wr_calname = 'GitLab Issues'
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
xml.instruct!
|
||||
xml.feed 'xmlns' => 'http://www.w3.org/2005/Atom', 'xmlns:media' => 'http://search.yahoo.com/mrss/' do
|
||||
xml << yield
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
- name = issuable_type == 'issue' ? 'issue' : 'merge request'
|
||||
|
||||
.issuable-footer.text-center
|
||||
%button.issuable-email-modal-btn{ type: "button", data: { toggle: "modal", target: "#issuable-email-modal" } }
|
||||
Email a new #{name} to this project
|
||||
|
||||
#issuable-email-modal.modal.fade{ tabindex: "-1", role: "dialog" }
|
||||
.modal-dialog{ role: "document" }
|
||||
.modal-content
|
||||
.modal-header
|
||||
%h4.modal-title
|
||||
Create new #{name} by email
|
||||
%button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') }
|
||||
%span{ "aria-hidden": true } ×
|
||||
.modal-body
|
||||
%p
|
||||
You can create a new #{name} inside this project by sending an email to the following email address:
|
||||
.email-modal-input-group.input-group
|
||||
= text_field_tag :issuable_email, email, class: "monospace js-select-on-focus form-control", readonly: true
|
||||
.input-group-append
|
||||
= clipboard_button(target: '#issuable_email', class: 'btn btn-clipboard input-group-text btn-transparent d-none d-sm-block')
|
||||
|
||||
- if issuable_type == 'issue'
|
||||
- enter_title_text = _('Enter the issue title')
|
||||
- enter_description_text = _('Enter the issue description')
|
||||
- else
|
||||
- enter_title_text = _('Enter the merge request title')
|
||||
- enter_description_text = _('Enter the merge request description')
|
||||
= mail_to email, class: 'btn btn-clipboard btn-transparent',
|
||||
subject: enter_title_text,
|
||||
body: enter_description_text,
|
||||
title: _('Send email'),
|
||||
data: { toggle: 'tooltip', placement: 'bottom' } do
|
||||
= sprite_icon('mail')
|
||||
|
||||
%p
|
||||
= render 'by_email_description'
|
||||
%p
|
||||
This is a private email address
|
||||
%span<
|
||||
= link_to help_page_path('development/emails', anchor: 'email-namespace'), target: '_blank', rel: 'noopener', aria: { label: 'Learn more about incoming email addresses' } do
|
||||
= sprite_icon('question-o')
|
||||
|
||||
generated just for you.
|
||||
|
||||
Anyone who gets ahold of it can create issues or merge requests as if they were you.
|
||||
You should
|
||||
= link_to 'reset it', new_issuable_address_project_path(@project, issuable_type: issuable_type), class: 'incoming-email-token-reset'
|
||||
if that ever happens.
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
xml.entry do
|
||||
xml.id project_commit_url(@project, id: commit.id)
|
||||
xml.link href: project_commit_url(@project, id: commit.id)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
xml.title "#{@project.name}:#{@ref} commits"
|
||||
xml.link href: project_commits_url(@project, @ref, rss_url_options), rel: "self", type: "application/atom+xml"
|
||||
xml.link href: project_commits_url(@project, @ref), rel: "alternate", type: "text/html"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
xml.title "#{@project.name} issues"
|
||||
xml.link href: url_for(safe_params), rel: "self", type: "application/atom+xml"
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
- page_title _("Issues")
|
||||
- new_issue_email = @project.new_issuable_address(current_user, 'issue')
|
||||
- add_page_specific_style 'page_bundles/issues_list'
|
||||
- issuable_type = 'issue'
|
||||
|
||||
= content_for :meta_tags do
|
||||
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{@project.name} issues")
|
||||
|
@ -24,7 +25,8 @@
|
|||
.issues-holder
|
||||
= render 'issues'
|
||||
- if new_issue_email
|
||||
= render 'projects/issuable_by_email', email: new_issue_email, issuable_type: 'issue'
|
||||
.issuable-footer.text-center
|
||||
.js-issueable-by-email{ data: { initial_email: new_issue_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } }
|
||||
- else
|
||||
- new_project_issue_button_path = @project.archived? ? false : new_project_issue_path(@project)
|
||||
= render 'shared/empty_states/issues', new_project_issue_button_path: new_project_issue_button_path, show_import_button: true
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
- @can_bulk_update = can?(current_user, :admin_merge_request, @project)
|
||||
- merge_project = merge_request_source_project_for_project(@project)
|
||||
- new_merge_request_path = project_new_merge_request_path(merge_project) if merge_project
|
||||
- issuable_type = 'merge_request'
|
||||
|
||||
- page_title _("Merge Requests")
|
||||
- new_merge_request_email = @project.new_issuable_address(current_user, 'merge_request')
|
||||
|
@ -21,6 +22,7 @@
|
|||
.merge-requests-holder
|
||||
= render 'merge_requests'
|
||||
- if new_merge_request_email
|
||||
= render 'projects/issuable_by_email', email: new_merge_request_email, issuable_type: 'merge_request'
|
||||
.issuable-footer.text-center
|
||||
.js-issueable-by-email{ data: { initial_email: new_merge_request_email, issuable_type: issuable_type, emails_help_page_path: help_page_path('development/emails', anchor: 'email-namespace'), quick_actions_help_path: help_page_path('user/project/quick_actions'), markdown_help_path: help_page_path('user/markdown'), reset_path: new_issuable_address_project_path(@project, issuable_type: issuable_type) } }
|
||||
- else
|
||||
= render 'shared/empty_states/merge_requests', button_path: new_merge_request_path
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
xml.title "#{@project.name} activity"
|
||||
xml.link href: project_url(@project, rss_url_options), rel: "self", type: "application/atom+xml"
|
||||
xml.link href: project_url(@project), rel: "alternate", type: "text/html"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
commit = @repository.commit(tag.dereferenced_target)
|
||||
release = @releases.find { |r| r.tag == tag.name }
|
||||
tag_url = project_tag_url(@project, tag.name)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
xml.title "#{@project.name} tags"
|
||||
xml.link href: project_tags_url(@project, @ref, rss_url_options), rel: 'self', type: 'application/atom+xml'
|
||||
xml.link href: project_tags_url(@project, @ref), rel: 'alternate', type: 'text/html'
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
= link_to issuable_path(issuable), data: { track_event: 'click_text', track_label: "#{issuable.class.name.downcase}_title", track_property: 'search_result' }, class: 'gl-w-full' do
|
||||
%span.term.str-truncated.gl-font-weight-bold.gl-ml-2= issuable.title
|
||||
.gl-text-gray-500.gl-my-3
|
||||
= sprintf(s_(' %{project_name}#%{issuable_iid} · opened %{issuable_created} by %{author}'), { project_name: issuable.project.full_name, issuable_iid: issuable.iid, issuable_created: time_ago_with_tooltip(issuable.created_at, placement: 'bottom'), author: link_to_member(@project, issuable.author, avatar: false) }).html_safe
|
||||
= sprintf(s_(' %{project_name}#%{issuable_iid} · opened %{issuable_created} by %{author} · updated %{issuable_updated}'), { project_name: issuable.project.full_name, issuable_iid: issuable.iid, issuable_created: time_ago_with_tooltip(issuable.created_at, placement: 'bottom'), issuable_updated: time_ago_with_tooltip(issuable.updated_at, placement: 'bottom'), author: link_to_member(@project, issuable.author, avatar: false) }).html_safe
|
||||
.description.term.col-sm-10.gl-px-0
|
||||
= highlight_and_truncate_issuable(issuable, @search_term, @search_highlight)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
xml.title "#{@user.name} activity"
|
||||
xml.link href: user_url(@user, :atom), rel: "self", type: "application/atom+xml"
|
||||
xml.link href: user_url(@user), rel: "alternate", type: "text/html"
|
||||
|
|
16
bin/secpick
16
bin/secpick
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
# frozen_string_literal: false
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'active_support/core_ext/object/to_query'
|
||||
require 'optparse'
|
||||
|
@ -9,12 +9,12 @@ require 'rainbow/refinement'
|
|||
using Rainbow
|
||||
|
||||
module Secpick
|
||||
BRANCH_PREFIX = 'security'.freeze
|
||||
STABLE_SUFFIX = 'stable'.freeze
|
||||
BRANCH_PREFIX = 'security'
|
||||
STABLE_SUFFIX = 'stable'
|
||||
|
||||
DEFAULT_REMOTE = 'security'.freeze
|
||||
DEFAULT_REMOTE = 'security'
|
||||
|
||||
SECURITY_MR_URL = 'https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/new'.freeze
|
||||
SECURITY_MR_URL = 'https://gitlab.com/gitlab-org/security/gitlab/-/merge_requests/new'
|
||||
|
||||
class SecurityFix
|
||||
def initialize
|
||||
|
@ -27,12 +27,12 @@ module Secpick
|
|||
|
||||
def source_branch
|
||||
branch = "#{@options[:branch]}-#{@options[:version]}"
|
||||
branch.prepend("#{BRANCH_PREFIX}-") unless branch.start_with?("#{BRANCH_PREFIX}-")
|
||||
branch.freeze
|
||||
branch = "#{BRANCH_PREFIX}-#{branch}" unless branch.start_with?("#{BRANCH_PREFIX}-")
|
||||
branch
|
||||
end
|
||||
|
||||
def stable_branch
|
||||
"#{@options[:version]}-#{STABLE_SUFFIX}-ee".freeze
|
||||
"#{@options[:version]}-#{STABLE_SUFFIX}-ee"
|
||||
end
|
||||
|
||||
def git_commands
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Replace bootstrap modal in issuable_by_email HAML template
|
||||
merge_request: 53599
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Cleanup incorrect data in projects.has_external_wiki
|
||||
merge_request: 53790
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add updated_at output to search results
|
||||
merge_request: 53958
|
||||
author:
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add Security Orchestration Policy Configuration
|
||||
merge_request: 53743
|
||||
author:
|
||||
type: added
|
5
changelogs/unreleased/300546-add-new-plans.yml
Normal file
5
changelogs/unreleased/300546-add-new-plans.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Migration to add new Premium and Ultimate plan records
|
||||
merge_request: 53465
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix bug rendering snippet activity
|
||||
merge_request: 53993
|
||||
author:
|
||||
type: fixed
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# FIXME: git.info_for_file raises the following error
|
||||
# /usr/local/bundle/gems/git-1.4.0/lib/git/lib.rb:956:in `command': (Danger::DSLError)
|
||||
# [!] Invalid `Dangerfile` file:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Style/SignalException
|
||||
|
||||
THROUGHPUT_LABELS = [
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CreateSecurityOrchestrationPolicyConfigurations < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
INDEX_PREFIX = 'index_sop_configs_'
|
||||
|
||||
def up
|
||||
table_comment = { owner: 'group::container security', description: 'Configuration used to store relationship between project and security policy repository' }
|
||||
|
||||
create_table_with_constraints :security_orchestration_policy_configurations, comment: table_comment.to_json do |t|
|
||||
t.references :project, null: false, foreign_key: { to_table: :projects, on_delete: :cascade }, index: { name: INDEX_PREFIX + 'on_project_id', unique: true }
|
||||
t.references :security_policy_management_project, null: false, foreign_key: { to_table: :projects, on_delete: :restrict }, index: { name: INDEX_PREFIX + 'on_security_policy_management_project_id', unique: true }
|
||||
|
||||
t.timestamps_with_timezone
|
||||
end
|
||||
end
|
||||
|
||||
def down
|
||||
with_lock_retries do
|
||||
drop_table :security_orchestration_policy_configurations, force: :cascade
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,88 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CleanupProjectsWithBadHasExternalWikiData < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
TMP_INDEX_NAME = 'tmp_index_projects_on_id_where_has_external_wiki_is_true'.freeze
|
||||
BATCH_SIZE = 100
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
class Service < ActiveRecord::Base
|
||||
include EachBatch
|
||||
belongs_to :project
|
||||
|
||||
self.table_name = 'services'
|
||||
self.inheritance_column = :_type_disabled
|
||||
end
|
||||
|
||||
class Project < ActiveRecord::Base
|
||||
include EachBatch
|
||||
|
||||
self.table_name = 'projects'
|
||||
end
|
||||
|
||||
def up
|
||||
update_projects_with_active_external_wikis
|
||||
update_projects_without_active_external_wikis
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op : can't go back to incorrect data
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def update_projects_with_active_external_wikis
|
||||
# 11 projects are scoped in this query on GitLab.com.
|
||||
scope = Service.where(active: true, type: 'ExternalWikiService').where.not(project_id: nil)
|
||||
|
||||
scope.each_batch(of: BATCH_SIZE) do |relation|
|
||||
scope_with_projects = relation
|
||||
.joins(:project)
|
||||
.select('project_id')
|
||||
.merge(Project.where(has_external_wiki: false).where(pending_delete: false).where(archived: false))
|
||||
|
||||
execute(<<~SQL)
|
||||
WITH project_ids_to_update (id) AS (
|
||||
#{scope_with_projects.to_sql}
|
||||
)
|
||||
UPDATE projects SET has_external_wiki = true WHERE id IN (SELECT id FROM project_ids_to_update)
|
||||
SQL
|
||||
end
|
||||
end
|
||||
|
||||
def update_projects_without_active_external_wikis
|
||||
# Add a temporary index to speed up the scoping of projects.
|
||||
index_where = <<~SQL
|
||||
(
|
||||
"projects"."has_external_wiki" = TRUE
|
||||
)
|
||||
AND "projects"."pending_delete" = FALSE
|
||||
AND "projects"."archived" = FALSE
|
||||
SQL
|
||||
|
||||
add_concurrent_index(:projects, :id, where: index_where, name: TMP_INDEX_NAME)
|
||||
|
||||
services_sub_query = Service
|
||||
.select('1')
|
||||
.where('services.project_id = projects.id')
|
||||
.where(type: 'ExternalWikiService')
|
||||
.where(active: true)
|
||||
|
||||
# 322 projects are scoped in this query on GitLab.com.
|
||||
Project.where(index_where).each_batch(of: BATCH_SIZE) do |relation|
|
||||
relation_with_exists_query = relation.where('NOT EXISTS (?)', services_sub_query)
|
||||
execute(<<~SQL)
|
||||
WITH project_ids_to_update (id) AS (
|
||||
#{relation_with_exists_query.select(:id).to_sql}
|
||||
)
|
||||
UPDATE projects SET has_external_wiki = false WHERE id IN (SELECT id FROM project_ids_to_update)
|
||||
SQL
|
||||
end
|
||||
|
||||
# Drop the temporary index.
|
||||
remove_concurrent_index_by_name(:projects, TMP_INDEX_NAME)
|
||||
end
|
||||
end
|
14
db/post_migrate/20210205104425_add_new_post_eoa_plans.rb
Normal file
14
db/post_migrate/20210205104425_add_new_post_eoa_plans.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddNewPostEoaPlans < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
execute "INSERT INTO plans (name, title, created_at, updated_at) VALUES ('premium', 'Premium (Formerly Silver)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)"
|
||||
execute "INSERT INTO plans (name, title, created_at, updated_at) VALUES ('ultimate', 'Ultimate (Formerly Gold)', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)"
|
||||
end
|
||||
|
||||
def down
|
||||
execute "DELETE FROM plans WHERE name IN ('premium', 'ultimate')"
|
||||
end
|
||||
end
|
1
db/schema_migrations/20210105030125
Normal file
1
db/schema_migrations/20210105030125
Normal file
|
@ -0,0 +1 @@
|
|||
c5a780e5b5e62043fb04e77ebf89f7d04dfc9bfdc70df8d89c16a3f3fd960ea3
|
1
db/schema_migrations/20210205104425
Normal file
1
db/schema_migrations/20210205104425
Normal file
|
@ -0,0 +1 @@
|
|||
4df2229fca7652cb836c867b621da91f1194899c50bb0a8be2fbae58f29dc788
|
1
db/schema_migrations/20210209160510
Normal file
1
db/schema_migrations/20210209160510
Normal file
|
@ -0,0 +1 @@
|
|||
601d67a2911c461881064ec18a2246ef9e5b2835eb0fdf40e701c9360e19eca4
|
|
@ -16971,6 +16971,25 @@ CREATE SEQUENCE security_findings_id_seq
|
|||
|
||||
ALTER SEQUENCE security_findings_id_seq OWNED BY security_findings.id;
|
||||
|
||||
CREATE TABLE security_orchestration_policy_configurations (
|
||||
id bigint NOT NULL,
|
||||
project_id bigint NOT NULL,
|
||||
security_policy_management_project_id bigint NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
updated_at timestamp with time zone NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE security_orchestration_policy_configurations IS '{"owner":"group::container security","description":"Configuration used to store relationship between project and security policy repository"}';
|
||||
|
||||
CREATE SEQUENCE security_orchestration_policy_configurations_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
ALTER SEQUENCE security_orchestration_policy_configurations_id_seq OWNED BY security_orchestration_policy_configurations.id;
|
||||
|
||||
CREATE TABLE security_scans (
|
||||
id bigint NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
|
@ -19289,6 +19308,8 @@ ALTER TABLE ONLY scim_oauth_access_tokens ALTER COLUMN id SET DEFAULT nextval('s
|
|||
|
||||
ALTER TABLE ONLY security_findings ALTER COLUMN id SET DEFAULT nextval('security_findings_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY security_orchestration_policy_configurations ALTER COLUMN id SET DEFAULT nextval('security_orchestration_policy_configurations_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY security_scans ALTER COLUMN id SET DEFAULT nextval('security_scans_id_seq'::regclass);
|
||||
|
||||
ALTER TABLE ONLY self_managed_prometheus_alert_events ALTER COLUMN id SET DEFAULT nextval('self_managed_prometheus_alert_events_id_seq'::regclass);
|
||||
|
@ -20794,6 +20815,9 @@ ALTER TABLE ONLY scim_oauth_access_tokens
|
|||
ALTER TABLE ONLY security_findings
|
||||
ADD CONSTRAINT security_findings_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY security_orchestration_policy_configurations
|
||||
ADD CONSTRAINT security_orchestration_policy_configurations_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY security_scans
|
||||
ADD CONSTRAINT security_scans_pkey PRIMARY KEY (id);
|
||||
|
||||
|
@ -23353,6 +23377,10 @@ CREATE INDEX index_software_licenses_on_spdx_identifier ON software_licenses USI
|
|||
|
||||
CREATE UNIQUE INDEX index_software_licenses_on_unique_name ON software_licenses USING btree (name);
|
||||
|
||||
CREATE UNIQUE INDEX index_sop_configs_on_project_id ON security_orchestration_policy_configurations USING btree (project_id);
|
||||
|
||||
CREATE UNIQUE INDEX index_sop_configs_on_security_policy_management_project_id ON security_orchestration_policy_configurations USING btree (security_policy_management_project_id);
|
||||
|
||||
CREATE INDEX index_sprints_on_description_trigram ON sprints USING gin (description gin_trgm_ops);
|
||||
|
||||
CREATE INDEX index_sprints_on_due_date ON sprints USING btree (due_date);
|
||||
|
@ -24780,6 +24808,9 @@ ALTER TABLE ONLY ci_subscriptions_projects
|
|||
ALTER TABLE ONLY trending_projects
|
||||
ADD CONSTRAINT fk_rails_09feecd872 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY security_orchestration_policy_configurations
|
||||
ADD CONSTRAINT fk_rails_0a22dcd52d FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY project_deploy_tokens
|
||||
ADD CONSTRAINT fk_rails_0aca134388 FOREIGN KEY (deploy_token_id) REFERENCES deploy_tokens(id) ON DELETE CASCADE;
|
||||
|
||||
|
@ -25119,6 +25150,9 @@ ALTER TABLE ONLY epic_issues
|
|||
ALTER TABLE ONLY ci_refs
|
||||
ADD CONSTRAINT fk_rails_4249db8cc3 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY security_orchestration_policy_configurations
|
||||
ADD CONSTRAINT fk_rails_42ed6c25ec FOREIGN KEY (security_policy_management_project_id) REFERENCES projects(id) ON DELETE RESTRICT;
|
||||
|
||||
ALTER TABLE ONLY ci_resources
|
||||
ADD CONSTRAINT fk_rails_430336af2d FOREIGN KEY (resource_group_id) REFERENCES ci_resource_groups(id) ON DELETE CASCADE;
|
||||
|
||||
|
|
|
@ -93,8 +93,6 @@ which correspond to:
|
|||
|
||||
1. `elasticsearch_calls`: total number of calls to Elasticsearch
|
||||
1. `elasticsearch_duration_s`: total time taken by Elasticsearch calls
|
||||
1. `elasticsearch_timed_out_count`: total number of calls to Elasticsearch that
|
||||
timed out and therefore returned partial results
|
||||
|
||||
ActionCable connection and subscription events are also logged to this file and they follow the same
|
||||
format above. The `method`, `path`, and `format` fields are not applicable, and are always empty.
|
||||
|
|
|
@ -12,100 +12,110 @@ full list of reference architectures, see
|
|||
[Available reference architectures](index.md#available-reference-architectures).
|
||||
|
||||
> - **Supported users (approximate):** 10,000
|
||||
> - **High Availability:** Yes
|
||||
> - **Test requests per second (RPS) rates:** API: 200 RPS, Web: 20 RPS, Git: 20 RPS
|
||||
> - **High Availability:** Yes ([Praefect](#configure-praefect-postgresql) needs a third-party PostgreSQL solution for HA)
|
||||
> - **Test requests per second (RPS) rates:** API: 200 RPS, Web: 20 RPS, Git (Pull): 20 RPS, Git (Push): 4 RPS
|
||||
|
||||
| Service | Nodes | Configuration | GCP | AWS | Azure |
|
||||
|--------------------------------------------|-------------|-------------------------|-----------------|-------------|----------|
|
||||
| External load balancing node | 1 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | `c5.large` | F2s v2 |
|
||||
| Consul | 3 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | `c5.large` | F2s v2 |
|
||||
| PostgreSQL | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | `m5.xlarge` | D4s v3 |
|
||||
| PgBouncer | 3 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | `c5.large` | F2s v2 |
|
||||
| Internal load balancing node | 1 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | `c5.large` | F2s v2 |
|
||||
| Redis - Cache | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | `m5.xlarge` | D4s v3 |
|
||||
| Redis - Queues / Shared State | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | `m5.xlarge` | D4s v3 |
|
||||
| Redis Sentinel - Cache | 3 | 1 vCPU, 1.7 GB memory | g1-small | `t3.small` | B1MS |
|
||||
| Redis Sentinel - Queues / Shared State | 3 | 1 vCPU, 1.7 GB memory | g1-small | `t3.small` | B1MS |
|
||||
| Gitaly | 2 (minimum) | 16 vCPU, 60 GB memory | n1-standard-16 | `m5.4xlarge` | D16s v3 |
|
||||
| Sidekiq | 4 | 4 vCPU, 15 GB memory | n1-standard-4 | `m5.xlarge` | D4s v3 |
|
||||
| GitLab Rails | 3 | 32 vCPU, 28.8 GB memory | n1-highcpu-32 | `c5.9xlarge` | F32s v2 |
|
||||
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | `c5.xlarge` | F4s v2 |
|
||||
| External load balancing node | 1 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | c5.large | F2s v2 |
|
||||
| Consul | 3 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | c5.large | F2s v2 |
|
||||
| PostgreSQL | 3 | 8 vCPU, 30 GB memory | n1-standard-8 | m5.2xlarge | D8s v3 |
|
||||
| PgBouncer | 3 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | c5.large | F2s v2 |
|
||||
| Internal load balancing node | 1 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | c5.large | F2s v2 |
|
||||
| Redis - Cache | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
|
||||
| Redis - Queues / Shared State | 3 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
|
||||
| Redis Sentinel - Cache | 3 | 1 vCPU, 1.7 GB memory | g1-small | t3.small | B1MS |
|
||||
| Redis Sentinel - Queues / Shared State | 3 | 1 vCPU, 1.7 GB memory | g1-small | t3.small | B1MS |
|
||||
| Gitaly Cluster | 3 | 16 vCPU, 60 GB memory | n1-standard-16 | m5.4xlarge | D16s v3 |
|
||||
| Praefect | 3 | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | c5.large | F2s v2 |
|
||||
| Praefect PostgreSQL | 1+* | 2 vCPU, 1.8 GB memory | n1-highcpu-2 | c5.large | F2s v2 |
|
||||
| Sidekiq | 4 | 4 vCPU, 15 GB memory | n1-standard-4 | m5.xlarge | D4s v3 |
|
||||
| GitLab Rails | 3 | 32 vCPU, 28.8 GB memory | n1-highcpu-32 | c5.9xlarge | F32s v2 |
|
||||
| Monitoring node | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | c5.xlarge | F4s v2 |
|
||||
| Object storage | n/a | n/a | n/a | n/a | n/a |
|
||||
| NFS server | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | `c5.xlarge` | F4s v2 |
|
||||
| NFS server | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | c5.xlarge | F4s v2 |
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> LoadBalancer
|
||||
LoadBalancer --> ApplicationServer
|
||||
```plantuml
|
||||
@startuml 10k
|
||||
card "**External Load Balancer**" as elb #6a9be7
|
||||
card "**Internal Load Balancer**" as ilb #9370DB
|
||||
|
||||
ApplicationServer --> BackgroundJobs
|
||||
ApplicationServer --> Gitaly
|
||||
ApplicationServer --> Redis_Cache
|
||||
ApplicationServer --> Redis_Queues
|
||||
ApplicationServer --> PgBouncer
|
||||
PgBouncer --> Database
|
||||
ApplicationServer --> ObjectStorage
|
||||
BackgroundJobs --> ObjectStorage
|
||||
together {
|
||||
collections "**GitLab Rails** x3" as gitlab #32CD32
|
||||
collections "**Sidekiq** x4" as sidekiq #ff8dd1
|
||||
}
|
||||
|
||||
ApplicationMonitoring -->ApplicationServer
|
||||
ApplicationMonitoring -->PgBouncer
|
||||
ApplicationMonitoring -->Database
|
||||
ApplicationMonitoring -->BackgroundJobs
|
||||
together {
|
||||
card "**Prometheus + Grafana**" as monitor #7FFFD4
|
||||
collections "**Consul** x3" as consul #e76a9b
|
||||
}
|
||||
|
||||
ApplicationServer --> Consul
|
||||
card "Gitaly Cluster" as gitaly_cluster {
|
||||
collections "**Praefect** x3" as praefect #FF8C00
|
||||
collections "**Gitaly** x3" as gitaly #FF8C00
|
||||
card "**Praefect PostgreSQL***\n//Non fault-tolerant//" as praefect_postgres #FF8C00
|
||||
|
||||
Consul --> Database
|
||||
Consul --> PgBouncer
|
||||
Redis_Cache --> Consul
|
||||
Redis_Queues --> Consul
|
||||
BackgroundJobs --> Consul
|
||||
praefect -[#FF8C00]-> gitaly
|
||||
praefect -[#FF8C00]> praefect_postgres
|
||||
}
|
||||
|
||||
state Consul {
|
||||
"Consul_1..3"
|
||||
}
|
||||
card "Database" as database {
|
||||
collections "**PGBouncer** x3" as pgbouncer #4EA7FF
|
||||
card "**PostgreSQL** (Primary)" as postgres_primary #4EA7FF
|
||||
collections "**PostgreSQL** (Secondary) x2" as postgres_secondary #4EA7FF
|
||||
|
||||
state Database {
|
||||
"PG_Primary_Node"
|
||||
"PG_Secondary_Node_1..2"
|
||||
}
|
||||
pgbouncer -[#4EA7FF]-> postgres_primary
|
||||
postgres_primary .[#4EA7FF]> postgres_secondary
|
||||
}
|
||||
|
||||
state Redis_Cache {
|
||||
"R_Cache_Primary_Node"
|
||||
"R_Cache_Replica_Node_1..2"
|
||||
"R_Cache_Sentinel_1..3"
|
||||
}
|
||||
card "redis" as redis {
|
||||
collections "**Redis Persistent** x3" as redis_persistent #FF6347
|
||||
collections "**Redis Cache** x3" as redis_cache #FF6347
|
||||
collections "**Redis Persistent Sentinel** x3" as redis_persistent_sentinel #FF6347
|
||||
collections "**Redis Cache Sentinel** x3"as redis_cache_sentinel #FF6347
|
||||
|
||||
state Redis_Queues {
|
||||
"R_Queues_Primary_Node"
|
||||
"R_Queues_Replica_Node_1..2"
|
||||
"R_Queues_Sentinel_1..3"
|
||||
}
|
||||
redis_persistent <.[#FF6347]- redis_persistent_sentinel
|
||||
redis_cache <.[#FF6347]- redis_cache_sentinel
|
||||
}
|
||||
|
||||
state Gitaly {
|
||||
"Gitaly_1..2"
|
||||
}
|
||||
cloud "**Object Storage**" as object_storage #white
|
||||
|
||||
state BackgroundJobs {
|
||||
"Sidekiq_1..4"
|
||||
}
|
||||
elb -[#6a9be7]-> gitlab
|
||||
elb -[#6a9be7]--> monitor
|
||||
|
||||
state ApplicationServer {
|
||||
"GitLab_Rails_1..3"
|
||||
}
|
||||
gitlab -[#32CD32]> sidekiq
|
||||
gitlab -[#32CD32]--> ilb
|
||||
gitlab -[#32CD32]-> object_storage
|
||||
gitlab -[#32CD32]---> redis
|
||||
gitlab -[hidden]-> monitor
|
||||
gitlab -[hidden]-> consul
|
||||
|
||||
state LoadBalancer {
|
||||
"LoadBalancer_1"
|
||||
}
|
||||
sidekiq -[#ff8dd1]--> ilb
|
||||
sidekiq -[#ff8dd1]-> object_storage
|
||||
sidekiq -[#ff8dd1]---> redis
|
||||
sidekiq -[hidden]-> monitor
|
||||
sidekiq -[hidden]-> consul
|
||||
|
||||
state ApplicationMonitoring {
|
||||
"Prometheus"
|
||||
"Grafana"
|
||||
}
|
||||
ilb -[#9370DB]-> gitaly_cluster
|
||||
ilb -[#9370DB]-> database
|
||||
|
||||
state PgBouncer {
|
||||
"Internal_Load_Balancer"
|
||||
"PgBouncer_1..3"
|
||||
}
|
||||
consul .[#e76a9b]u-> gitlab
|
||||
consul .[#e76a9b]u-> sidekiq
|
||||
consul .[#e76a9b]> monitor
|
||||
consul .[#e76a9b]-> database
|
||||
consul .[#e76a9b]-> gitaly_cluster
|
||||
consul .[#e76a9b,norank]--> redis
|
||||
|
||||
monitor .[#7FFFD4]u-> gitlab
|
||||
monitor .[#7FFFD4]u-> sidekiq
|
||||
monitor .[#7FFFD4]> consul
|
||||
monitor .[#7FFFD4]-> database
|
||||
monitor .[#7FFFD4]-> gitaly_cluster
|
||||
monitor .[#7FFFD4,norank]--> redis
|
||||
monitor .[#7FFFD4]> ilb
|
||||
monitor .[#7FFFD4,norank]u--> elb
|
||||
|
||||
@enduml
|
||||
```
|
||||
|
||||
The Google Cloud Platform (GCP) architectures were built and tested using the
|
||||
|
@ -120,19 +130,25 @@ uploads, or artifacts), using an [object storage service](#configure-the-object-
|
|||
is recommended instead of using NFS. Using an object storage service also
|
||||
doesn't require you to provision and maintain a node.
|
||||
|
||||
It's also worth noting that at this time [Praefect requires its own database server](../gitaly/praefect.md#postgresql) and
|
||||
that to achieve full High Availability a third party PostgreSQL database solution will be required.
|
||||
We hope to offer a built in solutions for these restrictions in the future but in the meantime a non HA PostgreSQL server
|
||||
can be set up via Omnibus GitLab, which the above specs reflect. Refer to the following issues for more information: [`omnibus-gitlab#5919`](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5919) & [`gitaly#3398`](https://gitlab.com/gitlab-org/gitaly/-/issues/3398)
|
||||
|
||||
## Setup components
|
||||
|
||||
To set up GitLab and its components to accommodate up to 10,000 users:
|
||||
|
||||
1. [Configure the external load balancing node](#configure-the-external-load-balancer)
|
||||
1. [Configure the external load balancer](#configure-the-external-load-balancer)
|
||||
to handle the load balancing of the GitLab application services nodes.
|
||||
1. [Configure the internal load balancer](#configure-the-internal-load-balancer).
|
||||
to handle the load balancing of GitLab application internal connections.
|
||||
1. [Configure Consul](#configure-consul).
|
||||
1. [Configure PostgreSQL](#configure-postgresql), the database for GitLab.
|
||||
1. [Configure PgBouncer](#configure-pgbouncer).
|
||||
1. [Configure the internal load balancing node](#configure-the-internal-load-balancer).
|
||||
1. [Configure Redis](#configure-redis).
|
||||
1. [Configure Gitaly](#configure-gitaly),
|
||||
which provides access to the Git repositories.
|
||||
1. [Configure Gitaly Cluster](#configure-gitaly-cluster),
|
||||
provides access to the Git repositories.
|
||||
1. [Configure Sidekiq](#configure-sidekiq).
|
||||
1. [Configure the main GitLab Rails application](#configure-gitlab-rails)
|
||||
to run Puma/Unicorn, Workhorse, GitLab Shell, and to serve all frontend
|
||||
|
@ -178,6 +194,11 @@ The following list includes descriptions of each server and its assigned IP:
|
|||
- `10.6.0.83`: Sentinel - Queues 3
|
||||
- `10.6.0.91`: Gitaly 1
|
||||
- `10.6.0.92`: Gitaly 2
|
||||
- `10.6.0.93`: Gitaly 3
|
||||
- `10.6.0.131`: Praefect 1
|
||||
- `10.6.0.132`: Praefect 2
|
||||
- `10.6.0.133`: Praefect 3
|
||||
- `10.6.0.141`: Praefect PostgreSQL 1 (non HA)
|
||||
- `10.6.0.101`: Sidekiq 1
|
||||
- `10.6.0.102`: Sidekiq 2
|
||||
- `10.6.0.103`: Sidekiq 3
|
||||
|
@ -308,6 +329,71 @@ Configure DNS for an alternate SSH hostname such as `altssh.gitlab.example.com`.
|
|||
</a>
|
||||
</div>
|
||||
|
||||
## Configure the internal load balancer
|
||||
|
||||
The Internal Load Balancer is used to balance any internal connections the GitLab environment requires
|
||||
such as connections to [PgBouncer](#configure-pgbouncer) and [Praefect](#configure-praefect) (Gitaly Cluster).
|
||||
|
||||
Note that it's a separate node from the External Load Balancer and shouldn't have any access externally.
|
||||
|
||||
The following IP will be used as an example:
|
||||
|
||||
- `10.6.0.40`: Internal Load Balancer
|
||||
|
||||
Here's how you could do it with [HAProxy](https://www.haproxy.org/):
|
||||
|
||||
```plaintext
|
||||
global
|
||||
log /dev/log local0
|
||||
log localhost local1 notice
|
||||
log stdout format raw local0
|
||||
|
||||
defaults
|
||||
log global
|
||||
default-server inter 10s fall 3 rise 2
|
||||
balance leastconn
|
||||
|
||||
frontend internal-pgbouncer-tcp-in
|
||||
bind *:6432
|
||||
mode tcp
|
||||
option tcplog
|
||||
|
||||
default_backend pgbouncer
|
||||
|
||||
frontend internal-praefect-tcp-in
|
||||
bind *:2305
|
||||
mode tcp
|
||||
option tcplog
|
||||
option clitcpka
|
||||
|
||||
default_backend praefect
|
||||
|
||||
backend pgbouncer
|
||||
mode tcp
|
||||
option tcp-check
|
||||
|
||||
server pgbouncer1 10.6.0.21:6432 check
|
||||
server pgbouncer2 10.6.0.22:6432 check
|
||||
server pgbouncer3 10.6.0.23:6432 check
|
||||
|
||||
backend praefect
|
||||
mode tcp
|
||||
option tcp-check
|
||||
option srvtcpka
|
||||
|
||||
server praefect1 10.6.0.131:2305 check
|
||||
server praefect2 10.6.0.132:2305 check
|
||||
server praefect3 10.6.0.133:2305 check
|
||||
```
|
||||
|
||||
Refer to your preferred Load Balancer's documentation for further guidance.
|
||||
|
||||
<div align="right">
|
||||
<a type="button" class="btn btn-default" href="#setup-components">
|
||||
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Configure Consul
|
||||
|
||||
The following IPs will be used as an example:
|
||||
|
@ -662,52 +748,6 @@ The following IPs will be used as an example:
|
|||
</a>
|
||||
</div>
|
||||
|
||||
### Configure the internal load balancer
|
||||
|
||||
If you're running more than one PgBouncer node as recommended, then at this time you'll need to set
|
||||
up a TCP internal load balancer to serve each correctly.
|
||||
|
||||
The following IP will be used as an example:
|
||||
|
||||
- `10.6.0.40`: Internal Load Balancer
|
||||
|
||||
Here's how you could do it with [HAProxy](https://www.haproxy.org/):
|
||||
|
||||
```plaintext
|
||||
global
|
||||
log /dev/log local0
|
||||
log localhost local1 notice
|
||||
log stdout format raw local0
|
||||
|
||||
defaults
|
||||
log global
|
||||
default-server inter 10s fall 3 rise 2
|
||||
balance leastconn
|
||||
|
||||
frontend internal-pgbouncer-tcp-in
|
||||
bind *:6432
|
||||
mode tcp
|
||||
option tcplog
|
||||
|
||||
default_backend pgbouncer
|
||||
|
||||
backend pgbouncer
|
||||
mode tcp
|
||||
option tcp-check
|
||||
|
||||
server pgbouncer1 10.6.0.21:6432 check
|
||||
server pgbouncer2 10.6.0.22:6432 check
|
||||
server pgbouncer3 10.6.0.23:6432 check
|
||||
```
|
||||
|
||||
Refer to your preferred Load Balancer's documentation for further guidance.
|
||||
|
||||
<div align="right">
|
||||
<a type="button" class="btn btn-default" href="#setup-components">
|
||||
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Configure Redis
|
||||
|
||||
Using [Redis](https://redis.io/) in scalable environment is possible using a **Primary** x **Replica**
|
||||
|
@ -1302,19 +1342,283 @@ To configure the Sentinel Queues server:
|
|||
</a>
|
||||
</div>
|
||||
|
||||
## Configure Gitaly
|
||||
## Configure Gitaly Cluster
|
||||
|
||||
NOTE:
|
||||
[Gitaly Cluster](../gitaly/praefect.md) support
|
||||
for the Reference Architectures is being
|
||||
worked on as a [collaborative effort](https://gitlab.com/gitlab-org/quality/reference-architectures/-/issues/1) between the Quality Engineering and Gitaly teams. When this component has been verified
|
||||
some Architecture specs will likely change as a result to support the new
|
||||
and improved designed.
|
||||
[Gitaly Cluster](../gitaly/praefect.md) is a GitLab provided and recommended fault tolerant solution for storing Git repositories.
|
||||
In this configuration, every Git repository is stored on every Gitaly node in the cluster, with one being designated the primary, and failover occurs automatically if the primary node goes down.
|
||||
|
||||
[Gitaly](../gitaly/index.md) server node requirements are dependent on data,
|
||||
specifically the number of projects and those projects' sizes. It's recommended
|
||||
that a Gitaly server node stores no more than 5 TB of data. Depending on your
|
||||
repository storage requirements, you may require additional Gitaly server nodes.
|
||||
The recommended cluster setup includes the following components:
|
||||
|
||||
- 3 Gitaly nodes: Replicated storage of Git repositories.
|
||||
- 3 Praefect nodes: Router and transaction manager for Gitaly Cluster.
|
||||
- 1 Praefect PostgreSQL node: Database server for Praefect. A third-party solution
|
||||
is required for Praefect database connections to be made highly available.
|
||||
- 1 load balancer: A load balancer is required for Praefect. The
|
||||
[internal load balancer](#configure-the-internal-load-balancer) will be used.
|
||||
|
||||
This section will detail how to configure the recommended standard setup in order.
|
||||
For more advanced setups refer to the [standalone Gitaly Cluster documentation](../gitaly/praefect.md).
|
||||
|
||||
### Configure Praefect PostgreSQL
|
||||
|
||||
Praefect, the routing and transaction manager for Gitaly Cluster, requires its own database server to store data on Gitaly Cluster status.
|
||||
|
||||
If you want to have a highly available setup, Praefect requires a third-party PostgreSQL database.
|
||||
A built-in solution is being [worked on](https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/5919).
|
||||
|
||||
#### Praefect non-HA PostgreSQL standalone using Omnibus GitLab
|
||||
|
||||
The following IPs will be used as an example:
|
||||
|
||||
- `10.6.0.141`: Praefect PostgreSQL
|
||||
|
||||
First, make sure to [install](https://about.gitlab.com/install/)
|
||||
the Linux GitLab package in the Praefect PostgreSQL node. Following the steps,
|
||||
install the necessary dependencies from step 1, and add the
|
||||
GitLab package repository from step 2. When installing GitLab
|
||||
in the second step, do not supply the `EXTERNAL_URL` value.
|
||||
|
||||
1. SSH in to the Praefect PostgreSQL node.
|
||||
1. Create a strong password to be used for the Praefect PostgreSQL user. Take note of this password as `<praefect_postgresql_password>`.
|
||||
1. Generate the password hash for the Praefect PostgreSQL username/password pair. This assumes you will use the default
|
||||
username of `praefect` (recommended). The command will request the password `<praefect_postgresql_password>`
|
||||
and confirmation. Use the value that is output by this command in the next
|
||||
step as the value of `<praefect_postgresql_password_hash>`:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl pg-password-md5 praefect
|
||||
```
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section:
|
||||
|
||||
```ruby
|
||||
# Disable all components except PostgreSQL and Consul
|
||||
roles ['postgres_role']
|
||||
repmgr['enable'] = false
|
||||
patroni['enable'] = false
|
||||
|
||||
# PostgreSQL configuration
|
||||
postgresql['listen_address'] = '0.0.0.0'
|
||||
postgresql['max_connections'] = 200
|
||||
|
||||
gitlab_rails['auto_migrate'] = false
|
||||
|
||||
# Configure the Consul agent
|
||||
consul['enable'] = true
|
||||
## Enable service discovery for Prometheus
|
||||
consul['monitoring_service_discovery'] = true
|
||||
|
||||
# START user configuration
|
||||
# Please set the real values as explained in Required Information section
|
||||
#
|
||||
# Replace PRAEFECT_POSTGRESQL_PASSWORD_HASH with a generated md5 value
|
||||
postgresql['sql_user_password'] = "<praefect_postgresql_password_hash>"
|
||||
|
||||
# Replace XXX.XXX.XXX.XXX/YY with Network Address
|
||||
postgresql['trust_auth_cidr_addresses'] = %w(10.6.0.0/24)
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
postgres_exporter['listen_address'] = '0.0.0.0:9187'
|
||||
|
||||
## The IPs of the Consul server nodes
|
||||
## You can also use FQDNs and intermix them with IPs
|
||||
consul['configuration'] = {
|
||||
retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13),
|
||||
}
|
||||
#
|
||||
# END user configuration
|
||||
```
|
||||
|
||||
1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
1. Follow the [post configuration](#praefect-postgresql-post-configuration).
|
||||
|
||||
<div align="right">
|
||||
<a type="button" class="btn btn-default" href="#setup-components">
|
||||
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
#### Praefect HA PostgreSQL third-party solution
|
||||
|
||||
[As noted](#configure-praefect-postgresql), a third-party PostgreSQL solution for
|
||||
Praefect's database is recommended if aiming for full High Availability.
|
||||
|
||||
There are many third-party solutions for PostgreSQL HA. The solution selected must have the following to work with Praefect:
|
||||
|
||||
- A static IP for all connections that doesn't change on failover.
|
||||
- [`LISTEN`](https://www.postgresql.org/docs/12/sql-listen.html) SQL functionality must be supported.
|
||||
|
||||
Examples of the above could include [Google's Cloud SQL](https://cloud.google.com/sql/docs/postgres/high-availability#normal) or [Amazon RDS](https://aws.amazon.com/rds/).
|
||||
|
||||
Once the database is set up, follow the [post configuration](#praefect-postgresql-post-configuration).
|
||||
|
||||
#### Praefect PostgreSQL post-configuration
|
||||
|
||||
After the Praefect PostgreSQL server has been set up, you'll then need to configure the user and database for Praefect to use.
|
||||
|
||||
We recommend the user be named `praefect` and the database `praefect_production`, and these can be configured as standard in PostgreSQL.
|
||||
The password for the user is the same as the one you configured earlier as `<praefect_postgresql_password>`.
|
||||
|
||||
This is how this would work with a Omnibus GitLab PostgreSQL setup:
|
||||
|
||||
1. SSH in to the Praefect PostgreSQL node.
|
||||
1. Connect to the PostgreSQL server with administrative access.
|
||||
The `gitlab-psql` user should be used here for this as it's added by default in Omnibus.
|
||||
The database `template1` is used because it is created by default on all PostgreSQL servers.
|
||||
|
||||
```shell
|
||||
/opt/gitlab/embedded/bin/psql -U gitlab-psql -d template1 -h POSTGRESQL_SERVER_ADDRESS
|
||||
```
|
||||
|
||||
1. Create the new user `praefect`, replacing `<praefect_postgresql_password>`:
|
||||
|
||||
```shell
|
||||
CREATE ROLE praefect WITH LOGIN CREATEDB PASSWORD <praefect_postgresql_password>;
|
||||
```
|
||||
|
||||
1. Reconnect to the PostgreSQL server, this time as the `praefect` user:
|
||||
|
||||
```shell
|
||||
/opt/gitlab/embedded/bin/psql -U praefect -d template1 -h POSTGRESQL_SERVER_ADDRESS
|
||||
```
|
||||
|
||||
1. Create a new database `praefect_production`:
|
||||
|
||||
```shell
|
||||
CREATE DATABASE praefect_production WITH ENCODING=UTF8;
|
||||
```
|
||||
|
||||
<div align="right">
|
||||
<a type="button" class="btn btn-default" href="#setup-components">
|
||||
Back to setup components <i class="fa fa-angle-double-up" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
### Configure Praefect
|
||||
|
||||
Praefect is the router and transaction manager for Gitaly Cluster and all connections to Gitaly go through
|
||||
it. This section details how to configure it.
|
||||
|
||||
Praefect requires several secret tokens to secure communications across the Cluster:
|
||||
|
||||
- `<praefect_external_token>`: Used for repositories hosted on your Gitaly cluster and can only be accessed by Gitaly clients that carry this token.
|
||||
- `<praefect_internal_token>`: Used for replication traffic inside your Gitaly cluster. This is distinct from `praefect_external_token` because Gitaly clients must not be able to access internal nodes of the Praefect cluster directly; that could lead to data loss.
|
||||
- `<praefect_postgresql_password>`: The Praefect PostgreSQL password defined in the previous section is also required as part of this setup.
|
||||
|
||||
Gitaly Cluster nodes are configured in Praefect via a `virtual storage`. Each storage contains
|
||||
the details of each Gitaly node that makes up the cluster. Each storage is also given a name
|
||||
and this name is used in several areas of the config. In this guide, the name of the storage will be
|
||||
`default`. Also, this guide is geared towards new installs, if upgrading an existing environment
|
||||
to use Gitaly Cluster, you may need to use a different name.
|
||||
Refer to the [Praefect documentation](../gitaly/praefect.md#praefect) for more info.
|
||||
|
||||
The following IPs will be used as an example:
|
||||
|
||||
- `10.6.0.131`: Praefect 1
|
||||
- `10.6.0.132`: Praefect 2
|
||||
- `10.6.0.133`: Praefect 3
|
||||
|
||||
To configure the Praefect nodes, on each one:
|
||||
|
||||
1. SSH in to the Praefect server.
|
||||
1. [Download and install](https://about.gitlab.com/install/) the Omnibus GitLab
|
||||
package of your choice. Be sure to follow _only_ installation steps 1 and 2
|
||||
on the page.
|
||||
1. Edit the `/etc/gitlab/gitlab.rb` file to configure Praefect:
|
||||
|
||||
```ruby
|
||||
# Avoid running unnecessary services on the Gitaly server
|
||||
postgresql['enable'] = false
|
||||
redis['enable'] = false
|
||||
nginx['enable'] = false
|
||||
puma['enable'] = false
|
||||
unicorn['enable'] = false
|
||||
sidekiq['enable'] = false
|
||||
gitlab_workhorse['enable'] = false
|
||||
grafana['enable'] = false
|
||||
|
||||
# If you run a separate monitoring node you can disable these services
|
||||
alertmanager['enable'] = false
|
||||
prometheus['enable'] = false
|
||||
|
||||
# Praefect Configuration
|
||||
praefect['enable'] = true
|
||||
praefect['listen_addr'] = '0.0.0.0:2305'
|
||||
|
||||
gitlab_rails['rake_cache_clear'] = false
|
||||
gitlab_rails['auto_migrate'] = false
|
||||
|
||||
# Configure the Consul agent
|
||||
consul['enable'] = true
|
||||
## Enable service discovery for Prometheus
|
||||
consul['monitoring_service_discovery'] = true
|
||||
|
||||
# START user configuration
|
||||
# Please set the real values as explained in Required Information section
|
||||
#
|
||||
|
||||
# Praefect External Token
|
||||
# This is needed by clients outside the cluster (like GitLab Shell) to communicate with the Praefect cluster
|
||||
praefect['auth_token'] = '<praefect_external_token>'
|
||||
|
||||
# Praefect Database Settings
|
||||
praefect['database_host'] = '10.6.0.141'
|
||||
praefect['database_port'] = 5432
|
||||
# `no_proxy` settings must always be a direct connection for caching
|
||||
praefect['database_host_no_proxy'] = '10.6.0.141'
|
||||
praefect['database_port_no_proxy'] = 5432
|
||||
praefect['database_dbname'] = 'praefect_production'
|
||||
praefect['database_user'] = 'praefect'
|
||||
praefect['database_password'] = '<praefect_postgresql_password>'
|
||||
|
||||
# Praefect Virtual Storage config
|
||||
# Name of storage hash must match storage name in git_data_dirs on GitLab
|
||||
# server ('praefect') and in git_data_dirs on Gitaly nodes ('gitaly-1')
|
||||
praefect['virtual_storages'] = {
|
||||
'default' => {
|
||||
'gitaly-1' => {
|
||||
'address' => 'tcp://10.6.0.91:8075',
|
||||
'token' => '<praefect_internal_token>',
|
||||
'primary' => true
|
||||
},
|
||||
'gitaly-2' => {
|
||||
'address' => 'tcp://10.6.0.92:8075',
|
||||
'token' => '<praefect_internal_token>'
|
||||
},
|
||||
'gitaly-3' => {
|
||||
'address' => 'tcp://10.6.0.93:8075',
|
||||
'token' => '<praefect_internal_token>'
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# Set the network addresses that the exporters will listen on for monitoring
|
||||
node_exporter['listen_address'] = '0.0.0.0:9100'
|
||||
praefect['prometheus_listen_addr'] = '0.0.0.0:9652'
|
||||
|
||||
## The IPs of the Consul server nodes
|
||||
## You can also use FQDNs and intermix them with IPs
|
||||
consul['configuration'] = {
|
||||
retry_join: %w(10.6.0.11 10.6.0.12 10.6.0.13),
|
||||
}
|
||||
#
|
||||
# END user configuration
|
||||
```
|
||||
|
||||
1. Copy the `/etc/gitlab/gitlab-secrets.json` file from your Consul server, and
|
||||
then replace the file of the same name on this server. If that file isn't on
|
||||
this server, add the file from your Consul server to this server.
|
||||
|
||||
1. Save the file, and then [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||
|
||||
### Configure Gitaly
|
||||
|
||||
The [Gitaly](../gitaly/index.md) server nodes that make up the cluster have
|
||||
requirements that are dependent on data, specifically the number of projects
|
||||
and those projects' sizes. It's recommended that a Gitaly Cluster stores
|
||||
no more than 5 TB of data on each node. Depending on your
|
||||
repository storage requirements, you may require additional Gitaly Clusters.
|
||||
|
||||
Due to Gitaly having notable input and output requirements, we strongly
|
||||
recommend that all Gitaly nodes use solid-state drives (SSDs). These SSDs
|
||||
|
@ -1325,36 +1629,21 @@ adjusted to greater or lesser values depending on the scale of your
|
|||
environment's workload. If you're running the environment on a Cloud provider,
|
||||
refer to their documentation about how to configure IOPS correctly.
|
||||
|
||||
Be sure to note the following items:
|
||||
Gitaly servers must not be exposed to the public internet, as Gitaly's network
|
||||
traffic is unencrypted by default. The use of a firewall is highly recommended
|
||||
to restrict access to the Gitaly server. Another option is to
|
||||
[use TLS](#gitaly-cluster-tls-support).
|
||||
|
||||
- The GitLab Rails application shards repositories into
|
||||
[repository storage paths](../repository_storage_paths.md).
|
||||
- A Gitaly server can host one or more storage paths.
|
||||
- A GitLab server can use one or more Gitaly server nodes.
|
||||
- Gitaly addresses must be specified to be correctly resolvable for all Gitaly
|
||||
clients.
|
||||
- Gitaly servers must not be exposed to the public internet, as Gitaly's network
|
||||
traffic is unencrypted by default. The use of a firewall is highly recommended
|
||||
to restrict access to the Gitaly server. Another option is to
|
||||
[use TLS](#gitaly-tls-support).
|
||||
For configuring Gitaly you should note the following:
|
||||
|
||||
NOTE:
|
||||
The token referred to throughout the Gitaly documentation is an arbitrary
|
||||
password selected by the administrator. This token is unrelated to tokens
|
||||
created for the GitLab API or other similar web API tokens.
|
||||
- `git_data_dirs` should be configured to reflect the storage path for the specific Gitaly node
|
||||
- `auth_token` should be the same as `praefect_internal_token`
|
||||
|
||||
This section describes how to configure two Gitaly servers, with the following
|
||||
IPs and domain names:
|
||||
The following IPs will be used as an example:
|
||||
|
||||
- `10.6.0.91`: Gitaly 1 (`gitaly1.internal`)
|
||||
- `10.6.0.92`: Gitaly 2 (`gitaly2.internal`)
|
||||
|
||||
Assumptions about your servers include having the secret token be `gitalysecret`,
|
||||
and that your GitLab installation has three repository storages:
|
||||
|
||||
- `default` on Gitaly 1
|
||||
- `storage1` on Gitaly 1
|
||||
- `storage2` on Gitaly 2
|
||||
- `10.6.0.91`: Gitaly 1
|
||||
- `10.6.0.92`: Gitaly 2
|
||||
- `10.6.0.93`: Gitaly 3
|
||||
|
||||
On each node:
|
||||
|
||||
|
@ -1364,21 +1653,9 @@ On each node:
|
|||
1. Edit the Gitaly server node's `/etc/gitlab/gitlab.rb` file to configure
|
||||
storage paths, enable the network listener, and to configure the token:
|
||||
|
||||
<!--
|
||||
updates to following example must also be made at
|
||||
https://gitlab.com/gitlab-org/charts/gitlab/blob/master/doc/advanced/external-gitaly/external-omnibus-gitaly.md#configure-omnibus-gitlab
|
||||
-->
|
||||
|
||||
```ruby
|
||||
# /etc/gitlab/gitlab.rb
|
||||
|
||||
# Gitaly and GitLab use two shared secrets for authentication, one to authenticate gRPC requests
|
||||
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
|
||||
# The following two values must be the same as their respective values
|
||||
# of the GitLab Rails application setup
|
||||
gitaly['auth_token'] = 'gitalysecret'
|
||||
gitlab_shell['secret_token'] = 'shellsecret'
|
||||
|
||||
# Avoid running unnecessary services on the Gitaly server
|
||||
postgresql['enable'] = false
|
||||
redis['enable'] = false
|
||||
|
@ -1407,36 +1684,42 @@ On each node:
|
|||
# firewalls to restrict access to this address/port.
|
||||
# Comment out following line if you only want to support TLS connections
|
||||
gitaly['listen_addr'] = "0.0.0.0:8075"
|
||||
|
||||
# Gitaly Auth Token
|
||||
# Should be the same as praefect_internal_token
|
||||
gitaly['auth_token'] = '<praefect_internal_token>'
|
||||
```
|
||||
|
||||
1. Append the following to `/etc/gitlab/gitlab.rb` for each respective server:
|
||||
- On `gitaly1.internal`:
|
||||
- On Gitaly node 1:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
'default' => {
|
||||
'path' => '/var/opt/gitlab/git-data'
|
||||
},
|
||||
'storage1' => {
|
||||
'path' => '/mnt/gitlab/git-data'
|
||||
},
|
||||
"gitaly-1" => {
|
||||
"path" => "/var/opt/gitlab/git-data"
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
- On `gitaly2.internal`:
|
||||
- On Gitaly node 2:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
'storage2' => {
|
||||
'path' => '/mnt/gitlab/git-data'
|
||||
},
|
||||
"gitaly-2" => {
|
||||
"path" => "/var/opt/gitlab/git-data"
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
<!--
|
||||
updates to following example must also be made at
|
||||
https://gitlab.com/gitlab-org/charts/gitlab/blob/master/doc/advanced/external-gitaly/external-omnibus-gitaly.md#configure-omnibus-gitlab
|
||||
-->
|
||||
- On Gitaly node 3:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
"gitaly-3" => {
|
||||
"path" => "/var/opt/gitlab/git-data"
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
1. Copy the `/etc/gitlab/gitlab-secrets.json` file from your Consul server, and
|
||||
then replace the file of the same name on this server. If that file isn't on
|
||||
|
@ -1444,34 +1727,44 @@ On each node:
|
|||
|
||||
1. Save the file, and then [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||
|
||||
### Gitaly TLS support
|
||||
### Gitaly Cluster TLS support
|
||||
|
||||
Gitaly supports TLS encryption. To be able to communicate
|
||||
with a Gitaly instance that listens for secure connections you will need to use `tls://` URL
|
||||
scheme in the `gitaly_address` of the corresponding storage entry in the GitLab configuration.
|
||||
Praefect supports TLS encryption. To communicate with a Praefect instance that listens
|
||||
for secure connections, you must:
|
||||
|
||||
You will need to bring your own certificates as this isn't provided automatically.
|
||||
The certificate, or its certificate authority, must be installed on all Gitaly
|
||||
nodes (including the Gitaly node using the certificate) and on all client nodes
|
||||
that communicate with it following the procedure described in
|
||||
[GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
|
||||
- Use a `tls://` URL scheme in the `gitaly_address` of the corresponding storage entry
|
||||
in the GitLab configuration.
|
||||
- Bring your own certificates because this isn't provided automatically. The certificate
|
||||
corresponding to each Praefect server must be installed on that Praefect server.
|
||||
|
||||
NOTE:
|
||||
The self-signed certificate must specify the address you use to access the
|
||||
Gitaly server. If you are addressing the Gitaly server by a hostname, you can
|
||||
either use the Common Name field for this, or add it as a Subject Alternative
|
||||
Name. If you are addressing the Gitaly server by its IP address, you must add it
|
||||
as a Subject Alternative Name to the certificate.
|
||||
[gRPC does not support using an IP address as Common Name in a certificate](https://github.com/grpc/grpc/issues/2691).
|
||||
Additionally the certificate, or its certificate authority, must be installed on all Gitaly servers
|
||||
and on all Praefect clients that communicate with it following the procedure described in
|
||||
[GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates) (and repeated below).
|
||||
|
||||
It's possible to configure Gitaly servers with both an unencrypted listening
|
||||
address (`listen_addr`) and an encrypted listening address (`tls_listen_addr`)
|
||||
at the same time. This allows you to do a gradual transition from unencrypted to
|
||||
encrypted traffic, if necessary.
|
||||
Note the following:
|
||||
|
||||
To configure Gitaly with TLS:
|
||||
- The certificate must specify the address you use to access the Praefect server. If
|
||||
addressing the Praefect server by:
|
||||
|
||||
1. Create the `/etc/gitlab/ssl` directory and copy your key and certificate there:
|
||||
- Hostname, you can either use the Common Name field for this, or add it as a Subject
|
||||
Alternative Name.
|
||||
- IP address, you must add it as a Subject Alternative Name to the certificate.
|
||||
|
||||
- You can configure Praefect servers with both an unencrypted listening address
|
||||
`listen_addr` and an encrypted listening address `tls_listen_addr` at the same time.
|
||||
This allows you to do a gradual transition from unencrypted to encrypted traffic, if
|
||||
necessary.
|
||||
|
||||
- The Internal Load Balancer will also access to the certificates and need to be configured
|
||||
to allow for TLS passthrough.
|
||||
Refer to the load balancers documentation on how to configure this.
|
||||
|
||||
To configure Praefect with TLS:
|
||||
|
||||
1. Create certificates for Praefect servers.
|
||||
|
||||
1. On the Praefect servers, create the `/etc/gitlab/ssl` directory and copy your key
|
||||
and certificate there:
|
||||
|
||||
```shell
|
||||
sudo mkdir -p /etc/gitlab/ssl
|
||||
|
@ -1480,27 +1773,34 @@ To configure Gitaly with TLS:
|
|||
sudo chmod 644 key.pem cert.pem
|
||||
```
|
||||
|
||||
1. Copy the cert to `/etc/gitlab/trusted-certs` so Gitaly will trust the cert when
|
||||
calling into itself:
|
||||
|
||||
```shell
|
||||
sudo cp /etc/gitlab/ssl/cert.pem /etc/gitlab/trusted-certs/
|
||||
```
|
||||
|
||||
1. Edit `/etc/gitlab/gitlab.rb` and add:
|
||||
|
||||
<!--
|
||||
updates to following example must also be made at
|
||||
https://gitlab.com/gitlab-org/charts/gitlab/blob/master/doc/advanced/external-gitaly/external-omnibus-gitaly.md#configure-omnibus-gitlab
|
||||
-->
|
||||
|
||||
```ruby
|
||||
gitaly['tls_listen_addr'] = "0.0.0.0:9999"
|
||||
gitaly['certificate_path'] = "/etc/gitlab/ssl/cert.pem"
|
||||
gitaly['key_path'] = "/etc/gitlab/ssl/key.pem"
|
||||
praefect['tls_listen_addr'] = "0.0.0.0:3305"
|
||||
praefect['certificate_path'] = "/etc/gitlab/ssl/cert.pem"
|
||||
praefect['key_path'] = "/etc/gitlab/ssl/key.pem"
|
||||
```
|
||||
|
||||
1. Delete `gitaly['listen_addr']` to allow only encrypted connections.
|
||||
1. Save the file and [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||
|
||||
1. On the Praefect clients (including each Gitaly server), copy the certificates,
|
||||
or their certificate authority, into `/etc/gitlab/trusted-certs`:
|
||||
|
||||
```shell
|
||||
sudo cp cert.pem /etc/gitlab/trusted-certs/
|
||||
```
|
||||
|
||||
1. On the Praefect clients (except Gitaly servers), edit `git_data_dirs` in
|
||||
`/etc/gitlab/gitlab.rb` as follows:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
"default" => {
|
||||
"gitaly_address" => 'tls://LOAD_BALANCER_SERVER_ADDRESS:2305',
|
||||
"gitaly_token" => 'PRAEFECT_EXTERNAL_TOKEN'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||
|
||||
|
@ -1586,17 +1886,20 @@ To configure the Sidekiq nodes, on each one:
|
|||
### Gitaly ###
|
||||
#######################################
|
||||
|
||||
# git_data_dirs get configured for the Praefect virtual storage
|
||||
# Address is Internal Load Balancer for Praefect
|
||||
# Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
'default' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
|
||||
'storage1' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
|
||||
'storage2' => { 'gitaly_address' => 'tcp://gitaly2.internal:8075' },
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
gitlab_rails['gitaly_token'] = 'YOUR_TOKEN'
|
||||
|
||||
#######################################
|
||||
### Postgres ###
|
||||
#######################################
|
||||
gitlab_rails['db_host'] = '10.6.0.20' # internal load balancer IP
|
||||
gitlab_rails['db_host'] = '10.6.0.40' # internal load balancer IP
|
||||
gitlab_rails['db_port'] = 6432
|
||||
gitlab_rails['db_password'] = '<postgresql_user_password>'
|
||||
gitlab_rails['db_adapter'] = 'postgresql'
|
||||
|
@ -1669,17 +1972,14 @@ On each node perform the following:
|
|||
```ruby
|
||||
external_url 'https://gitlab.example.com'
|
||||
|
||||
# Gitaly and GitLab use two shared secrets for authentication, one to authenticate gRPC requests
|
||||
# to Gitaly, and a second for authentication callbacks from GitLab-Shell to the GitLab internal API.
|
||||
# The following two values must be the same as their respective values
|
||||
# of the Gitaly setup
|
||||
gitlab_rails['gitaly_token'] = 'gitalysecret'
|
||||
gitlab_shell['secret_token'] = 'shellsecret'
|
||||
|
||||
# git_data_dirs get configured for the Praefect virtual storage
|
||||
# Address is Interal Load Balancer for Praefect
|
||||
# Token is praefect_external_token
|
||||
git_data_dirs({
|
||||
'default' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
|
||||
'storage1' => { 'gitaly_address' => 'tcp://gitaly1.internal:8075' },
|
||||
'storage2' => { 'gitaly_address' => 'tcp://gitaly2.internal:8075' },
|
||||
"default" => {
|
||||
"gitaly_address" => "tcp://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
|
||||
## Disable components that will not be on the GitLab application server
|
||||
|
@ -1739,14 +2039,15 @@ On each node perform the following:
|
|||
```
|
||||
|
||||
1. Save the file and [reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure).
|
||||
1. If you're using [Gitaly with TLS support](#gitaly-tls-support), make sure the
|
||||
1. If you're using [Gitaly with TLS support](#gitaly-cluster-tls-support), make sure the
|
||||
`git_data_dirs` entry is configured with `tls` instead of `tcp`:
|
||||
|
||||
```ruby
|
||||
git_data_dirs({
|
||||
'default' => { 'gitaly_address' => 'tls://gitaly1.internal:9999' },
|
||||
'storage1' => { 'gitaly_address' => 'tls://gitaly1.internal:9999' },
|
||||
'storage2' => { 'gitaly_address' => 'tls://gitaly2.internal:9999' },
|
||||
"default" => {
|
||||
"gitaly_address" => "tls://10.6.0.40:2305", # internal load balancer IP
|
||||
"gitaly_token" => '<praefect_external_token>'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ many organizations .
|
|||
> - **Supported users (approximate):** 1,000
|
||||
> - **High Availability:** No. For a highly-available environment, you can
|
||||
> follow the [3K reference architecture](3k_users.md).
|
||||
> - **Test requests per second (RPS) rates:** API: 20 RPS, Web: 2 RPS, Git (Pull): 2 RPS, Git (Push): 1 RPS
|
||||
|
||||
| Users | Configuration | GCP | AWS | Azure |
|
||||
|--------------|-------------------------|----------------|-----------------|----------------|
|
||||
|
|
|
@ -13,7 +13,7 @@ full list of reference architectures, see
|
|||
|
||||
> - **Supported users (approximate):** 25,000
|
||||
> - **High Availability:** Yes
|
||||
> - **Test requests per second (RPS) rates:** API: 500 RPS, Web: 50 RPS, Git: 50 RPS
|
||||
> - **Test requests per second (RPS) rates:** API: 500 RPS, Web: 50 RPS, Git (Pull): 50 RPS, Git (Push): 10 RPS
|
||||
|
||||
| Service | Nodes | Configuration | GCP | AWS | Azure |
|
||||
|-----------------------------------------|-------------|-------------------------|-----------------|-------------|----------|
|
||||
|
|
|
@ -14,7 +14,7 @@ For a full list of reference architectures, see
|
|||
> - **Supported users (approximate):** 2,000
|
||||
> - **High Availability:** No. For a highly-available environment, you can
|
||||
> follow the [3K reference architecture](3k_users.md).
|
||||
> - **Test requests per second (RPS) rates:** API: 40 RPS, Web: 4 RPS, Git: 4 RPS
|
||||
> - **Test requests per second (RPS) rates:** API: 40 RPS, Web: 4 RPS, Git (Pull): 4 RPS, Git (Push): 1 RPS
|
||||
|
||||
| Service | Nodes | Configuration | GCP | AWS | Azure |
|
||||
|------------------------------------------|--------|-------------------------|----------------|--------------|---------|
|
||||
|
@ -27,44 +27,32 @@ For a full list of reference architectures, see
|
|||
| Object storage | n/a | n/a | n/a | n/a | n/a |
|
||||
| NFS server (optional, not recommended) | 1 | 4 vCPU, 3.6 GB memory | n1-highcpu-4 | `c5.xlarge` | F4s v2 |
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> LoadBalancer
|
||||
LoadBalancer --> ApplicationServer
|
||||
```plantuml
|
||||
@startuml 2k
|
||||
card "**External Load Balancer**" as elb #6a9be7
|
||||
|
||||
ApplicationServer --> Gitaly
|
||||
ApplicationServer --> Redis
|
||||
ApplicationServer --> Database
|
||||
ApplicationServer --> ObjectStorage
|
||||
collections "**GitLab Rails** x3" as gitlab #32CD32
|
||||
card "**Prometheus + Grafana**" as monitor #7FFFD4
|
||||
card "**Gitaly**" as gitaly #FF8C00
|
||||
card "**PostgreSQL**" as postgres #4EA7FF
|
||||
card "**Redis**" as redis #FF6347
|
||||
cloud "**Object Storage**" as object_storage #white
|
||||
|
||||
ApplicationMonitoring -->ApplicationServer
|
||||
ApplicationMonitoring -->Redis
|
||||
ApplicationMonitoring -->Database
|
||||
elb -[#6a9be7]-> gitlab
|
||||
elb -[#6a9be7]--> monitor
|
||||
|
||||
gitlab -[#32CD32]--> gitaly
|
||||
gitlab -[#32CD32]--> postgres
|
||||
gitlab -[#32CD32]-> object_storage
|
||||
gitlab -[#32CD32]--> redis
|
||||
|
||||
state Database {
|
||||
"PG_Node"
|
||||
}
|
||||
state Redis {
|
||||
"Redis_Node"
|
||||
}
|
||||
monitor .[#7FFFD4]u-> gitlab
|
||||
monitor .[#7FFFD4]-> gitaly
|
||||
monitor .[#7FFFD4]-> postgres
|
||||
monitor .[#7FFFD4,norank]--> redis
|
||||
monitor .[#7FFFD4,norank]u--> elb
|
||||
|
||||
state Gitaly {
|
||||
"Gitaly"
|
||||
}
|
||||
|
||||
state ApplicationServer {
|
||||
"AppServ_1..2"
|
||||
}
|
||||
|
||||
state LoadBalancer {
|
||||
"LoadBalancer"
|
||||
}
|
||||
|
||||
state ApplicationMonitoring {
|
||||
"Prometheus"
|
||||
"Grafana"
|
||||
}
|
||||
@enduml
|
||||
```
|
||||
|
||||
The Google Cloud Platform (GCP) architectures were built and tested using the
|
||||
|
|
|
@ -21,7 +21,7 @@ For a full list of reference architectures, see
|
|||
|
||||
> - **Supported users (approximate):** 3,000
|
||||
> - **High Availability:** Yes
|
||||
> - **Test requests per second (RPS) rates:** API: 60 RPS, Web: 6 RPS, Git: 6 RPS
|
||||
> - **Test requests per second (RPS) rates:** API: 60 RPS, Web: 6 RPS, Git (Pull): 6 RPS, Git (Push): 1 RPS
|
||||
|
||||
| Service | Nodes | Configuration | GCP | AWS | Azure |
|
||||
|--------------------------------------------|-------------|-----------------------|----------------|-------------|---------|
|
||||
|
|
|
@ -13,7 +13,7 @@ full list of reference architectures, see
|
|||
|
||||
> - **Supported users (approximate):** 50,000
|
||||
> - **High Availability:** Yes
|
||||
> - **Test requests per second (RPS) rates:** API: 1000 RPS, Web: 100 RPS, Git: 100 RPS
|
||||
> - **Test requests per second (RPS) rates:** API: 1000 RPS, Web: 100 RPS, Git (Pull): 100 RPS, Git (Push): 20 RPS
|
||||
|
||||
| Service | Nodes | Configuration | GCP | AWS | Azure |
|
||||
|-----------------------------------------|-------------|-------------------------|-----------------|--------------|----------|
|
||||
|
|
|
@ -20,7 +20,7 @@ costly-to-operate environment by using the
|
|||
|
||||
> - **Supported users (approximate):** 5,000
|
||||
> - **High Availability:** Yes
|
||||
> - **Test requests per second (RPS) rates:** API: 100 RPS, Web: 10 RPS, Git: 10 RPS
|
||||
> - **Test requests per second (RPS) rates:** API: 100 RPS, Web: 10 RPS, Git (Pull): 10 RPS, Git (Push): 2 RPS
|
||||
|
||||
| Service | Nodes | Configuration | GCP | AWS | Azure |
|
||||
|--------------------------------------------|-------------|-------------------------|----------------|-------------|----------|
|
||||
|
|
|
@ -29,7 +29,8 @@ per 1,000 users:
|
|||
|
||||
- API: 20 RPS
|
||||
- Web: 2 RPS
|
||||
- Git: 2 RPS
|
||||
- Git (Pull): 2 RPS
|
||||
- Git (Push): 0.4 RPS (rounded to nearest integer)
|
||||
|
||||
For GitLab instances with less than 2,000 users, it's recommended that you use
|
||||
the [default setup](#automated-backups) by
|
||||
|
|
|
@ -173,7 +173,7 @@ delete the files:
|
|||
sudo gitlab-rake gitlab:cleanup:orphan_job_artifact_files DRY_RUN=false
|
||||
```
|
||||
|
||||
You can also limit the number of files to delete with `LIMIT`:
|
||||
You can also limit the number of files to delete with `LIMIT` (default `100`):
|
||||
|
||||
```shell
|
||||
sudo gitlab-rake gitlab:cleanup:orphan_job_artifact_files LIMIT=100
|
||||
|
|
|
@ -534,14 +534,16 @@ limiting responses](#rate-limiting-responses).
|
|||
The following table describes the rate limits for GitLab.com, both before and
|
||||
after the limits change in January, 2021:
|
||||
|
||||
| Rate limit | Before 2021-01-18 | From 2021-01-18 |
|
||||
|:--------------------------------------------------------------------------|:----------------------------|:------------------------------|
|
||||
| **Protected paths** (for a given **IP address**) | **10** requests per minute | **10** requests per minute |
|
||||
| **Raw endpoint** traffic (for a given **project, commit, and file path**) | **300** requests per minute | **300** requests per minute |
|
||||
| **Unauthenticated** traffic (from a given **IP address**) | No specific limit | **500** requests per minute |
|
||||
| **Authenticated** API traffic (for a given **user**) | No specific limit | **2,000** requests per minute |
|
||||
| **Authenticated** non-API HTTP traffic (for a given **user**) | No specific limit | **1,000** requests per minute |
|
||||
| **All** traffic (from a given **IP address**) | **600** requests per minute | **2,000** requests per minute |
|
||||
| Rate limit | Before 2021-01-18 | From 2021-01-18 | From 2021-02-12 |
|
||||
|:--------------------------------------------------------------------------|:----------------------------|:------------------------------|:------------------------------|
|
||||
| **Protected paths** (for a given **IP address**) | **10** requests per minute | **10** requests per minute | **10** requests per minute |
|
||||
| **Raw endpoint** traffic (for a given **project, commit, and file path**) | **300** requests per minute | **300** requests per minute | **300** requests per minute |
|
||||
| **Unauthenticated** traffic (from a given **IP address**) | No specific limit | **500** requests per minute | **500** requests per minute |
|
||||
| **Authenticated** API traffic (for a given **user**) | No specific limit | **2,000** requests per minute | **2,000** requests per minute |
|
||||
| **Authenticated** non-API HTTP traffic (for a given **user**) | No specific limit | **1,000** requests per minute | **1,000** requests per minute |
|
||||
| **All** traffic (from a given **IP address**) | **600** requests per minute | **2,000** requests per minute | **2,000** requests per minute |
|
||||
| **Issue creation** | | **300** requests per minute | **300** requests per minute |
|
||||
| **Note creation** (on issues and merge requests) | | **300** requests per minute | **60** requests per minute |
|
||||
|
||||
More details are available on the rate limits for [protected
|
||||
paths](#protected-paths-throttle) and [raw
|
||||
|
|
|
@ -9,15 +9,12 @@ module Gitlab
|
|||
start = Time.now
|
||||
headers = (headers || {})
|
||||
.reverse_merge({ 'X-Opaque-Id': Labkit::Correlation::CorrelationId.current_or_new_id })
|
||||
response = super
|
||||
super
|
||||
ensure
|
||||
if ::Gitlab::SafeRequestStore.active?
|
||||
duration = (Time.now - start)
|
||||
|
||||
::Gitlab::Instrumentation::ElasticsearchTransport.increment_request_count
|
||||
|
||||
::Gitlab::Instrumentation::ElasticsearchTransport.increment_timed_out_count if response&.body&.dig('timed_out')
|
||||
|
||||
::Gitlab::Instrumentation::ElasticsearchTransport.add_duration(duration)
|
||||
::Gitlab::Instrumentation::ElasticsearchTransport.add_call_details(duration, method, path, params, body)
|
||||
end
|
||||
|
@ -28,7 +25,6 @@ module Gitlab
|
|||
ELASTICSEARCH_REQUEST_COUNT = :elasticsearch_request_count
|
||||
ELASTICSEARCH_CALL_DURATION = :elasticsearch_call_duration
|
||||
ELASTICSEARCH_CALL_DETAILS = :elasticsearch_call_details
|
||||
ELASTICSEARCH_TIMED_OUT_COUNT = :elasticsearch_timed_out_count
|
||||
|
||||
def self.get_request_count
|
||||
::Gitlab::SafeRequestStore[ELASTICSEARCH_REQUEST_COUNT] || 0
|
||||
|
@ -53,15 +49,6 @@ module Gitlab
|
|||
::Gitlab::SafeRequestStore[ELASTICSEARCH_CALL_DURATION] += duration
|
||||
end
|
||||
|
||||
def self.increment_timed_out_count
|
||||
::Gitlab::SafeRequestStore[ELASTICSEARCH_TIMED_OUT_COUNT] ||= 0
|
||||
::Gitlab::SafeRequestStore[ELASTICSEARCH_TIMED_OUT_COUNT] += 1
|
||||
end
|
||||
|
||||
def self.get_timed_out_count
|
||||
::Gitlab::SafeRequestStore[ELASTICSEARCH_TIMED_OUT_COUNT] || 0
|
||||
end
|
||||
|
||||
def self.add_call_details(duration, method, path, params, body)
|
||||
return unless Gitlab::PerformanceBar.enabled_for_request?
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ module Gitlab
|
|||
:rugged_duration_s,
|
||||
:elasticsearch_calls,
|
||||
:elasticsearch_duration_s,
|
||||
:elasticsearch_timed_out_count,
|
||||
*::Gitlab::Memory::Instrumentation::KEY_MAPPING.values,
|
||||
*::Gitlab::Instrumentation::Redis.known_payload_keys,
|
||||
*::Gitlab::Metrics::Subscribers::ActiveRecord::DB_COUNTERS,
|
||||
|
@ -80,7 +79,6 @@ module Gitlab
|
|||
|
||||
payload[:elasticsearch_calls] = elasticsearch_calls
|
||||
payload[:elasticsearch_duration_s] = Gitlab::Instrumentation::ElasticsearchTransport.query_time
|
||||
payload[:elasticsearch_timed_out_count] = Gitlab::Instrumentation::ElasticsearchTransport.get_timed_out_count
|
||||
end
|
||||
|
||||
def instrument_external_http(payload)
|
||||
|
|
|
@ -16,7 +16,7 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
|
||||
|
||||
msgid " %{project_name}#%{issuable_iid} · opened %{issuable_created} by %{author}"
|
||||
msgid " %{project_name}#%{issuable_iid} · opened %{issuable_created} by %{author} · updated %{issuable_updated}"
|
||||
msgstr ""
|
||||
|
||||
msgid " %{start} to %{end}"
|
||||
|
@ -8481,6 +8481,9 @@ msgstr ""
|
|||
msgid "Create new"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create new %{name} by email"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create new Value Stream"
|
||||
msgstr ""
|
||||
|
||||
|
@ -10863,6 +10866,9 @@ msgstr ""
|
|||
msgid "Email Notification"
|
||||
msgstr ""
|
||||
|
||||
msgid "Email a new %{name} to this project"
|
||||
msgstr ""
|
||||
|
||||
msgid "Email address to use for Support Desk"
|
||||
msgstr ""
|
||||
|
||||
|
@ -10935,12 +10941,6 @@ msgstr ""
|
|||
msgid "EmailParticipantsWarning|and %{moreCount} more"
|
||||
msgstr ""
|
||||
|
||||
msgid "EmailToken|reset it"
|
||||
msgstr ""
|
||||
|
||||
msgid "EmailToken|resetting..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Emails"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11226,21 +11226,15 @@ msgstr ""
|
|||
msgid "Enter one or more user ID separated by commas"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the %{name} description"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the %{name} title"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes."
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the issue description"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the issue title"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the merge request description"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the merge request title"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the name of your application, and we'll return a unique %{type}."
|
||||
msgstr ""
|
||||
|
||||
|
@ -23934,6 +23928,9 @@ msgstr ""
|
|||
msgid "Protip:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Protip: %{linkStart}Auto DevOps%{linkEnd} uses Kubernetes clusters to deploy your code!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Protocol"
|
||||
msgstr ""
|
||||
|
||||
|
@ -29320,6 +29317,9 @@ msgstr ""
|
|||
msgid "The status of the table below only applies to the default branch and is based on the %{linkStart}latest pipeline%{linkEnd}. Once you've enabled a scan for the default branch, any subsequent feature branch you create will include the scan."
|
||||
msgstr ""
|
||||
|
||||
msgid "The subject will be used as the title of the new issue, and the message will be the description. %{quickActionsLinkStart}Quick actions%{quickActionsLinkEnd} and styling with %{markdownLinkStart}Markdown%{markdownLinkEnd} are supported."
|
||||
msgstr ""
|
||||
|
||||
msgid "The tag name can't be changed for an existing release."
|
||||
msgstr ""
|
||||
|
||||
|
@ -29926,6 +29926,9 @@ msgstr ""
|
|||
msgid "This is a merge train pipeline"
|
||||
msgstr ""
|
||||
|
||||
msgid "This is a private email address %{helpIcon} generated just for you. Anyone who gets ahold of it can create issues or merge requests as if they were you. You should %{resetLinkStart}reset it%{resetLinkEnd} if that ever happens."
|
||||
msgstr ""
|
||||
|
||||
msgid "This is a security log of important events involving your account."
|
||||
msgstr ""
|
||||
|
||||
|
@ -30223,6 +30226,9 @@ msgstr ""
|
|||
msgid "Threat Monitoring"
|
||||
msgstr ""
|
||||
|
||||
msgid "ThreatMonitoring|Alert Details"
|
||||
msgstr ""
|
||||
|
||||
msgid "ThreatMonitoring|Alerts"
|
||||
msgstr ""
|
||||
|
||||
|
@ -33349,6 +33355,9 @@ msgstr ""
|
|||
msgid "You can create a new %{link}."
|
||||
msgstr ""
|
||||
|
||||
msgid "You can create a new %{name} inside this project by sending an email to the following email address:"
|
||||
msgstr ""
|
||||
|
||||
msgid "You can create a new Personal Access Token by visiting %{link}"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
# lib/rspec_flaky/flaky_examples_collection.rb is requiring
|
||||
# `active_support/hash_with_indifferent_access`, and we install the `activesupport`
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
abort "usage: #{__FILE__} <memory_bundle_objects_file_name>" unless ARGV.length == 1
|
||||
memory_bundle_objects_file_name = ARGV.first
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
abort "usage: #{__FILE__} <memory_bundle_mem_file_name>" unless ARGV.length == 1
|
||||
memory_bundle_mem_file_name = ARGV.first
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
abort "usage: #{__FILE__} <memory_bundle_mem_file_name>" unless ARGV.length == 1
|
||||
memory_bundle_mem_file_name = ARGV.first
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require_relative '../tooling/lib/tooling/test_map_generator'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'fileutils'
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
# This script is used both in CI and in local development 'rspec' runs.
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This file contains environment settings for gitaly when it's running
|
||||
# as part of the gitlab-ce/ee test suite.
|
||||
#
|
||||
|
@ -52,7 +54,7 @@ module GitalyTest
|
|||
|
||||
if ENV['CI']
|
||||
bundle_path = File.expand_path('../vendor/gitaly-ruby', __dir__)
|
||||
env_hash['BUNDLE_FLAGS'] << " --path=#{bundle_path}"
|
||||
env_hash['BUNDLE_FLAGS'] += " --path=#{bundle_path}"
|
||||
end
|
||||
|
||||
env_hash
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'csv'
|
||||
require 'rspec_profiling'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
ALLOWED = [
|
||||
# https://gitlab.com/gitlab-org/gitaly/issues/760
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'nokogiri'
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative '../spec/simplecov_env'
|
||||
SimpleCovEnv.configure_profile
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
ee_path = File.join(File.expand_path(__dir__), '../ee')
|
||||
|
||||
if Dir.exist?(ee_path)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require_relative '../tooling/lib/tooling/test_map_packer'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
# We don't have auto-loading here
|
||||
require_relative '../lib/gitlab'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'rubygems'
|
||||
require 'fog/aws'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
require_relative '../tooling/lib/tooling/test_map_packer'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'uri'
|
||||
require 'net/http'
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'set'
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'set'
|
||||
|
||||
|
|
|
@ -18,9 +18,9 @@ RSpec.describe 'Issue Boards', :js do
|
|||
project.add_maintainer(user)
|
||||
project.add_maintainer(user2)
|
||||
|
||||
set_cookie('sidebar_collapsed', 'true')
|
||||
|
||||
sign_in(user)
|
||||
|
||||
set_cookie('sidebar_collapsed', 'true')
|
||||
end
|
||||
|
||||
context 'no lists' do
|
||||
|
|
|
@ -12,7 +12,7 @@ RSpec.describe 'Issues > User creates issue by email' do
|
|||
project.add_developer(user)
|
||||
end
|
||||
|
||||
describe 'new issue by email' do
|
||||
describe 'new issue by email', :js do
|
||||
shared_examples 'show the email in the modal' do
|
||||
let(:issue) { create(:issue, project: project) }
|
||||
|
||||
|
@ -28,7 +28,7 @@ RSpec.describe 'Issues > User creates issue by email' do
|
|||
page.within '#issuable-email-modal' do
|
||||
email = project.new_issuable_address(user, 'issue')
|
||||
|
||||
expect(page).to have_selector("input[value='#{email}']")
|
||||
expect(page.find('input[type="text"]').value).to eq email
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,17 +16,17 @@ RSpec.describe 'Issues > User resets their incoming email token' do
|
|||
end
|
||||
|
||||
it 'changes incoming email address token', :js do
|
||||
find('.issuable-email-modal-btn').click
|
||||
previous_token = find('input#issuable_email').value
|
||||
find('.incoming-email-token-reset').click
|
||||
page.find('[data-testid="issuable-email-modal-btn"]').click
|
||||
|
||||
wait_for_requests
|
||||
page.within '#issuable-email-modal' do
|
||||
previous_token = page.find('input[type="text"]').value
|
||||
page.find('[data-testid="incoming-email-token-reset"]').click
|
||||
|
||||
expect(page).to have_no_field('issuable_email', with: previous_token)
|
||||
new_token = project.new_issuable_address(user.reload, 'issue')
|
||||
expect(page).to have_field(
|
||||
'issuable_email',
|
||||
with: new_token
|
||||
)
|
||||
wait_for_requests
|
||||
|
||||
expect(page.find('input[type="text"]').value).not_to eq previous_token
|
||||
new_token = project.new_issuable_address(user.reload, 'issue')
|
||||
expect(page.find('input[type="text"]').value).to eq new_token
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,7 +38,9 @@ jest.mock('@gitlab/ui/dist/components/base/popover/popover.js', () => ({
|
|||
required: false,
|
||||
default: () => [],
|
||||
},
|
||||
...Object.fromEntries(['target', 'triggers', 'placement'].map((prop) => [prop, {}])),
|
||||
...Object.fromEntries(
|
||||
['target', 'triggers', 'placement', 'boundary', 'container'].map((prop) => [prop, {}]),
|
||||
),
|
||||
},
|
||||
render(h) {
|
||||
return h(
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue