Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-08-18 15:09:32 +00:00
parent 10adf63c4b
commit 133b8a64b6
110 changed files with 3581 additions and 1775 deletions

View File

@ -1 +1 @@
5a12604c5f91622dca8f7f84112a8cccb39c687c
0183d30e998cfab0b4f93fd1aa60200fba8e3771

View File

@ -249,7 +249,12 @@ export default {
:description="$options.translations.addTokenNameDescription"
label-for="deploy_token_name"
>
<gl-form-input id="deploy_token_name" v-model="name" name="deploy_token_name" />
<gl-form-input
id="deploy_token_name"
v-model="name"
class="gl-form-input-xl"
name="deploy_token_name"
/>
</gl-form-group>
<gl-form-group
:label="$options.translations.addTokenExpiryLabel"
@ -258,6 +263,7 @@ export default {
>
<gl-form-input
id="deploy_token_expires_at"
class="gl-form-input-xl"
name="deploy_token_expires_at"
:value="formattedExpiryDate"
data-qa-selector="deploy_token_expires_at_field"
@ -277,7 +283,7 @@ export default {
</template>
</gl-sprintf>
</template>
<gl-form-input id="deploy_token_username" v-model="username" />
<gl-form-input id="deploy_token_username" v-model="username" class="gl-form-input-xl" />
</gl-form-group>
<gl-form-group
:label="$options.translations.addTokenScopesLabel"

View File

@ -24,8 +24,8 @@ import * as utils from './diff_row_utils';
export default {
DiffGutterAvatars,
InlineFindingsGutterIcon: () =>
import('ee_component/diffs/components/inline_findings_gutter_icon.vue'),
InlineFindingsFlagSwitcher: () =>
import('ee_component/diffs/components/inline_findings_flag_switcher.vue'),
// Temporary mixin for migration from Vue.js 2 to @vue/compat
mixins: [compatFunctionalMixin],
@ -338,10 +338,11 @@ export default {
:class="$options.parallelViewLeftLineType(props)"
>
<component
:is="$options.InlineFindingsGutterIcon"
:is="$options.InlineFindingsFlagSwitcher"
v-if="$options.showCodequalityLeft(props) || $options.showSecurityLeft(props)"
:inline-findings-expanded="props.inlineFindingsExpanded"
:codequality="props.line.left.codequality"
:code-quality="props.line.left.codequality"
:sast="props.line.left.sast"
:file-path="props.filePath"
@showInlineFindings="
listeners.toggleCodeQualityFindings(
@ -479,9 +480,10 @@ export default {
:class="$options.classNameMapCellRight(props)"
>
<component
:is="$options.InlineFindingsGutterIcon"
:is="$options.InlineFindingsFlagSwitcher"
v-if="$options.showCodequalityRight(props) || $options.showSecurityRight(props)"
:codequality="props.line.right.codequality"
:code-quality="props.line.right.codequality"
:sast="props.line.right.sast"
:file-path="props.filePath"
data-testid="inlineFindingsIcon"
@showInlineFindings="

View File

@ -1,45 +1,62 @@
import * as Sentry from '@sentry/browser';
import axios from '~/lib/utils/axios_utils';
// import mockData from './mock_traces.json';
function enableTraces() {
// TODO remove mocks https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2271
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, 1000);
});
function reportErrorAndThrow(e) {
Sentry.captureException(e);
throw e;
}
// Provisioning API spec: https://gitlab.com/gitlab-org/opstrace/opstrace/-/blob/main/provisioning-api/pkg/provisioningapi/routes.go#L59
async function enableTraces(provisioningUrl) {
try {
// Note: axios.put(url, undefined, {withCredentials: true}) does not send cookies properly, so need to use the API below for the correct behaviour
return await axios(provisioningUrl, {
method: 'put',
withCredentials: true,
});
} catch (e) {
return reportErrorAndThrow(e);
}
}
function isTracingEnabled() {
// TODO remove mocks https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2271
return new Promise((resolve) => {
setTimeout(() => {
// Currently relying on manual provisioning, hence assuming tracing is enabled
resolve(true);
}, 1000);
});
// Provisioning API spec: https://gitlab.com/gitlab-org/opstrace/opstrace/-/blob/main/provisioning-api/pkg/provisioningapi/routes.go#L37
async function isTracingEnabled(provisioningUrl) {
try {
const { data } = await axios.get(provisioningUrl, { withCredentials: true });
if (data && data.status) {
// we currently ignore the 'status' payload and just check if the request was successful
// We might improve this as part of https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2315
return true;
}
} catch (e) {
if (e.response.status === 404) {
return false;
}
return reportErrorAndThrow(e);
}
return reportErrorAndThrow(new Error('Failed to check provisioning')); // eslint-disable-line @gitlab/require-i18n-strings
}
async function fetchTrace(tracingUrl, traceId) {
if (!traceId) {
throw new Error('traceId is required.');
try {
if (!traceId) {
throw new Error('traceId is required.');
}
const { data } = await axios.get(tracingUrl, {
withCredentials: true,
params: {
trace_id: traceId,
},
});
if (!Array.isArray(data.traces) || data.traces.length === 0) {
throw new Error('traces are missing/invalid in the response'); // eslint-disable-line @gitlab/require-i18n-strings
}
return data.traces[0];
} catch (e) {
return reportErrorAndThrow(e);
}
const { data } = await axios.get(tracingUrl, {
withCredentials: true,
params: {
trace_id: traceId,
},
});
// TODO: Improve local GDK dev experience with tracing https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2308
// const data = { traces: [mockData.traces.find((t) => t.trace_id === traceId)] };
if (!Array.isArray(data.traces) || data.traces.length === 0) {
throw new Error('traces are missing/invalid in the response.'); // eslint-disable-line @gitlab/require-i18n-strings
}
return data.traces[0];
}
/**
@ -152,18 +169,18 @@ function filterObjToQueryParams(filterObj) {
async function fetchTraces(tracingUrl, filters = {}) {
const filterParams = filterObjToQueryParams(filters);
const { data } = await axios.get(tracingUrl, {
withCredentials: true,
params: filterParams,
});
// TODO: Improve local GDK dev experience with tracing https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2308
// Uncomment the line below to test this locally
// const data = mockData;
if (!Array.isArray(data.traces)) {
throw new Error('traces are missing/invalid in the response.'); // eslint-disable-line @gitlab/require-i18n-strings
try {
const { data } = await axios.get(tracingUrl, {
withCredentials: true,
params: filterParams,
});
if (!Array.isArray(data.traces)) {
throw new Error('traces are missing/invalid in the response'); // eslint-disable-line @gitlab/require-i18n-strings
}
return data.traces;
} catch (e) {
return reportErrorAndThrow(e);
}
return data.traces;
}
export function buildClient({ provisioningUrl, tracingUrl }) {

View File

@ -0,0 +1,20 @@
<script>
import OrganizationAvatar from './organization_avatar.vue';
export default {
name: 'OrganizationShowApp',
components: { OrganizationAvatar },
props: {
organization: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div class="gl-py-6">
<organization-avatar :organization="organization" />
</div>
</template>

View File

@ -0,0 +1,71 @@
<script>
import { GlAvatar, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import {
VISIBILITY_TYPE_ICON,
ORGANIZATION_VISIBILITY_TYPE,
VISIBILITY_LEVEL_PUBLIC_STRING,
} from '~/visibility_level/constants';
export default {
name: 'OrganizationAvatar',
AVATAR_SHAPE_OPTION_RECT,
i18n: {
copyButtonText: s__('Organization|Copy organization ID'),
orgId: s__('Organization|Org ID'),
},
components: { GlAvatar, GlIcon, ClipboardButton },
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
organization: {
type: Object,
required: true,
},
},
computed: {
visibilityIcon() {
return VISIBILITY_TYPE_ICON[VISIBILITY_LEVEL_PUBLIC_STRING];
},
visibilityTooltip() {
return ORGANIZATION_VISIBILITY_TYPE[VISIBILITY_LEVEL_PUBLIC_STRING];
},
},
};
</script>
<template>
<div class="gl-display-flex gl-align-items-center">
<gl-avatar
:entity-id="organization.id"
:entity-name="organization.name"
:shape="$options.AVATAR_SHAPE_OPTION_RECT"
:size="64"
/>
<div class="gl-ml-3">
<div class="gl-display-flex gl-align-items-center">
<h1 class="gl-m-0 gl-font-size-h1">{{ organization.name }}</h1>
<gl-icon
v-gl-tooltip="visibilityTooltip"
:name="visibilityIcon"
class="gl-text-secondary gl-ml-3"
/>
</div>
<div class="gl-display-flex gl-align-items-center">
<span class="gl-text-secondary gl-font-sm"
>{{ $options.i18n.orgId }}: {{ organization.id }}</span
>
<clipboard-button
class="gl-ml-2"
category="tertiary"
size="small"
:title="$options.i18n.copyButtonText"
:text="organization.id.toString()"
/>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,21 @@
import Vue from 'vue';
import App from './components/app.vue';
export const initOrganizationsShow = () => {
const el = document.getElementById('js-organizations-show');
if (!el) return false;
const {
dataset: { appData },
} = el;
const { organization } = JSON.parse(appData);
return new Vue({
el,
name: 'OrganizationShowRoot',
render(createElement) {
return createElement(App, { props: { organization } });
},
});
};

View File

@ -0,0 +1,3 @@
import { initOrganizationsShow } from '~/organizations/show';
initOrganizationsShow();

View File

@ -1,4 +1,4 @@
import { __ } from '~/locale';
import { __, s__ } from '~/locale';
export const VISIBILITY_LEVEL_PRIVATE_STRING = 'private';
export const VISIBILITY_LEVEL_INTERNAL_STRING = 'internal';
@ -45,6 +45,12 @@ export const PROJECT_VISIBILITY_TYPE = {
),
};
export const ORGANIZATION_VISIBILITY_TYPE = {
[VISIBILITY_LEVEL_PUBLIC_STRING]: s__(
'Organization|Public - The organization can be accessed without any authentication.',
),
};
export const VISIBILITY_TYPE_ICON = {
[VISIBILITY_LEVEL_PUBLIC_STRING]: 'earth',
[VISIBILITY_LEVEL_INTERNAL_STRING]: 'shield',

View File

@ -283,75 +283,79 @@ $color-ranges: (
// GitLab themes
$indigo-50: #f7f7ff;
$indigo-100: #ebebfa;
$indigo-200: #d1d1f0;
$indigo-300: #a6a6de;
$indigo-400: #7c7ccc;
$indigo-50: #f1f1ff;
$indigo-100: #dbdbf8;
$indigo-200: #c7c7f2;
$indigo-300: #a2a2e6;
$indigo-400: #8181d7;
$indigo-500: #6666c4;
$indigo-600: #5b5bbd;
$indigo-700: #4b4ba3;
$indigo-800: #393982;
$indigo-900: #292961;
$indigo-950: #1a1a40;
$indigo-600: #5252b5;
$indigo-700: #41419f;
$indigo-800: #303083;
$indigo-900: #222261;
$indigo-950: #14143d;
// To do this variant right for darkmode, we need to create a variable for it.
$indigo-900-alpha-008: rgba($indigo-900, 0.08);
$theme-blue-50: #f4f8fc;
$theme-blue-100: #e6edf5;
$theme-blue-200: #c8d7e6;
$theme-blue-300: #97b3cf;
$theme-blue-400: #648cb4;
$theme-blue-500: #4a79a8;
$theme-blue-600: #3e6fa0;
$theme-blue-700: #305c88;
$theme-blue-800: #25496e;
$theme-blue-900: #1a3652;
$theme-blue-950: #0f2235;
$theme-blue-50: #cdd8e3;
$theme-blue-100: #b9cadc;
$theme-blue-200: #a6bdd5;
$theme-blue-300: #81a5c9;
$theme-blue-400: #628eb9;
$theme-blue-500: #4977a5;
$theme-blue-600: #346596;
$theme-blue-700: #235180;
$theme-blue-800: #153c63;
$theme-blue-900: #0b2640;
$theme-blue-950: #04101c;
$theme-light-blue-50: #f2f7fc;
$theme-light-blue-100: #ebf1f7;
$theme-light-blue-200: #c9dcf2;
$theme-light-blue-300: #83abd4;
$theme-light-blue-400: #4d86bf;
$theme-light-blue-500: #367cc2;
$theme-light-blue-600: #3771ab;
$theme-light-blue-700: #2261a1;
$theme-light-blue-50: #dde6ee;
$theme-light-blue-100: #c1d4e6;
$theme-light-blue-200: #a0bedc;
$theme-light-blue-300: #74a3d3;
$theme-light-blue-400: #4f8bc7;
$theme-light-blue-500: #3476b9;
$theme-light-blue-600: #2268ae;
$theme-light-blue-700: #145aa1;
$theme-light-blue-800: #0e4d8d;
$theme-light-blue-900: #0c4277;
$theme-light-blue-950: #0a3764;
$theme-green-50: #f2faf6;
$theme-green-100: #e4f3ea;
$theme-green-200: #c0dfcd;
$theme-green-300: #8ac2a1;
$theme-green-400: #52a274;
$theme-green-500: #35935c;
$theme-green-600: #288a50;
$theme-green-700: #1c7441;
$theme-green-800: #145d33;
$theme-green-900: #0d4524;
$theme-green-950: #072d16;
$theme-green-50: #dde9de;
$theme-green-100: #b1d6b5;
$theme-green-200: #8cc497;
$theme-green-300: #69af7d;
$theme-green-400: #499767;
$theme-green-500: #308258;
$theme-green-600: #25744c;
$theme-green-700: #1b653f;
$theme-green-800: #155635;
$theme-green-900: #0e4328;
$theme-green-950: #052e19;
$theme-light-green-700: #156b39;
$theme-red-50: #fcf4f2;
$theme-red-100: #fae9e6;
$theme-red-200: #ebcac5;
$theme-red-300: #d99b91;
$theme-red-400: #b0655a;
$theme-red-50: #f4e9e7;
$theme-red-100: #ecd3d0;
$theme-red-200: #e3bab5;
$theme-red-300: #d59086;
$theme-red-400: #c66e60;
$theme-red-500: #ad4a3b;
$theme-red-600: #9e4133;
$theme-red-700: #912f20;
$theme-red-800: #78291d;
$theme-red-900: #691a16;
$theme-red-950: #36140f;
$theme-red-600: #a13322;
$theme-red-700: #8f2110;
$theme-red-800: #761405;
$theme-red-900: #580d02;
$theme-red-950: #380700;
$theme-light-red-50: #fff6f5;
$theme-light-red-100: #fae2de;
$theme-light-red-200: #f7d5d0;
$theme-light-red-300: #d9796a;
$theme-light-red-400: #cf604e;
$theme-light-red-50: #faf2f1;
$theme-light-red-100: #f6d9d5;
$theme-light-red-200: #ebada2;
$theme-light-red-300: #e07f6f;
$theme-light-red-400: #d36250;
$theme-light-red-500: #c24b38;
$theme-light-red-600: #b03927;
$theme-light-red-700: #a62e21;
$theme-light-red-600: #b53a26;
$theme-light-red-700: #a02e1c;
$theme-light-red-800: #8b2212;
$theme-light-red-900: #751709;
$theme-light-red-950: #5c1105;
// Data visualization color palette

View File

@ -1,9 +1,9 @@
@import 'page_bundles/mixins_and_variables_and_functions';
.application-theme {
$ui-gray-bg: #303030;
$ui-light-gray-bg: #f0f0f0;
$ui-dark-mode-bg: #1f1f1f;
$ui-gray-bg: $gray-900;
$ui-light-gray-bg: $gray-50;
$ui-dark-mode-bg: $gray-950;
.preview {
font-size: 0;
@ -33,7 +33,7 @@
}
&.ui-light-green {
background-color: $theme-light-green-700;
background-color: $theme-green-700;
}
&.ui-red {

View File

@ -6,7 +6,7 @@ body {
$theme-green-200,
$theme-green-500,
$theme-green-500,
$theme-light-green-700,
$theme-green-700,
$white
);

View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
module Organizations
module OrganizationHelper
def organization_show_app_data(organization)
{
organization: organization.slice(:id, :name)
}.to_json
end
end
end

View File

@ -9,6 +9,8 @@ class PagesDomain < ApplicationRecord
VERIFICATION_THRESHOLD = 3.days.freeze
SSL_RENEWAL_THRESHOLD = 30.days.freeze
MAX_CERTIFICATE_KEY_LENGTH = 8192
enum certificate_source: { user_provided: 0, gitlab_provided: 1 }, _prefix: :certificate
enum scope: { instance: 0, group: 1, project: 2 }, _prefix: :scope, _default: :project
enum usage: { pages: 0, serverless: 1 }, _prefix: :usage, _default: :pages
@ -34,6 +36,7 @@ class PagesDomain < ApplicationRecord
validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? }
validate :validate_intermediates, if: ->(domain) { domain.certificate.present? && domain.certificate_changed? }
validate :validate_custom_domain_count_per_project, on: :create
validate :max_certificate_key_length, if: ->(domain) { domain.key.present? }
attribute :auto_ssl_enabled, default: -> { ::Gitlab::LetsEncrypt.enabled? }
attribute :wildcard, default: false
@ -234,6 +237,16 @@ class PagesDomain < ApplicationRecord
private
def max_certificate_key_length
return unless pkey.is_a?(OpenSSL::PKey::RSA)
return if pkey.to_s.bytesize <= MAX_CERTIFICATE_KEY_LENGTH
errors.add(
:key,
s_("PagesDomain|Certificate Key is too long. (Max %d bytes)") % MAX_CERTIFICATE_KEY_LENGTH
)
end
def set_verification_code
return if self.verification_code.present?

View File

@ -76,7 +76,7 @@ class BlobPresenter < Gitlab::View::Presenter::Delegated
end
def pipeline_editor_path
project_ci_pipeline_editor_path(project, branch_name: blob.commit_id) if can_collaborate_with_project?(project) && blob.path == project.ci_config_path_or_default
project_ci_pipeline_editor_path(project, branch_name: commit_id) if can_collaborate_with_project?(project) && blob.path == project.ci_config_path_or_default
end
def gitpod_blob_url

View File

@ -1,2 +1,4 @@
- page_title s_('Organization|Organization overview')
- @skip_current_level_breadcrumb = true
#js-organizations-show{ data: { app_data: organization_show_app_data(@organization) } }

View File

@ -6,7 +6,7 @@
.form-group
.gl-mb-2
= f.text_field :issue_branch_template, class: 'form-control gl-mb-2', placeholder: "%{id}-%{title}"
= f.text_field :issue_branch_template, class: 'form-control gl-mb-2 gl-form-input-xl', placeholder: "%{id}-%{title}"
%p.form-text.text-muted
= s_('ProjectSettings|Leave empty to use default template.')
= sprintf(s_('ProjectSettings|Maximum %{maxLength} characters.'), { maxLength: Issue::MAX_BRANCH_TEMPLATE })

View File

@ -6,7 +6,8 @@
.form-group
= f.label :default_branch, _("Default branch"), class: 'label-bold'
%p= s_('ProjectSettings|All merge requests and commits are made against this branch unless you specify a different one.')
.js-select-default-branch{ data: { default_branch: @project.default_branch, project_id: @project.id } }
.gl-form-input-xl
.js-select-default-branch{ data: { default_branch: @project.default_branch, project_id: @project.id } }
.form-group
- help_text = _("When merge requests and commits in the default branch close, any issues they reference also close.")

View File

@ -5,10 +5,10 @@
= f.label :auth_method, _('Authentication method'), class: 'label-bold'
= f.select :auth_method,
options_for_select(auth_options, mirror.auth_method),
{}, { class: "custom-select gl-form-select js-mirror-auth-type", data: { qa_selector: 'authentication_method_field' } }
{}, { class: "custom-select gl-form-select js-mirror-auth-type gl-max-w-34 gl-display-block", data: { qa_selector: 'authentication_method_field' } }
= f.hidden_field :auth_method, value: "password", class: "js-hidden-mirror-auth-type"
.form-group
.well-password-auth.collapse.js-well-password-auth
= f.label :password, _("Password"), class: "label-bold"
= f.password_field :password, class: 'form-control gl-form-input js-mirror-password-field', autocomplete: 'off', data: { qa_selector: 'password_field' }
= f.password_field :password, class: 'form-control gl-form-input js-mirror-password-field gl-form-input-xl', autocomplete: 'off', data: { qa_selector: 'password_field' }

View File

@ -35,7 +35,7 @@
%div= form_errors(@project)
.form-group.has-feedback
= label_tag :url, _('Git repository URL'), class: 'label-light'
= text_field_tag :url, nil, class: 'form-control gl-form-input js-mirror-url js-repo-url', placeholder: _('Input the remote repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", autocomplete: 'new-password', data: { qa_selector: 'mirror_repository_url_field' }
= text_field_tag :url, nil, class: 'form-control gl-form-input js-mirror-url js-repo-url gl-form-input-xl', placeholder: _('Input the remote repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", autocomplete: 'new-password', data: { qa_selector: 'mirror_repository_url_field' }
= render 'projects/mirrors/instructions'

View File

@ -1,7 +1,7 @@
.form-group
= label_tag :mirror_direction, _('Mirror direction'), class: 'label-light'
.select-wrapper
= select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control gl-form-select select-control js-mirror-direction', disabled: true, data: { qa_selector: 'mirror_direction_field' }
= select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control gl-form-select select-control js-mirror-direction gl-max-w-34 gl-display-block', disabled: true, data: { qa_selector: 'mirror_direction_field' }
= sprite_icon('chevron-down', css_class: "gl-icon gl-absolute gl-top-3 gl-right-3 gl-text-gray-200")
= render partial: "projects/mirrors/mirror_repos_push", locals: { f: f }

View File

@ -1,30 +1,30 @@
= gitlab_ui_form_for [@project.namespace, @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input container" } do |f|
= gitlab_ui_form_for [@project.namespace, @project, @deploy_keys.new_key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input" } do |f|
= form_errors(@deploy_keys.new_key)
.form-group.row
.form-group
%h4.gl-my-0= s_('DeployKeys|Add new deploy key')
.form-group.row
.form-group
= f.label :title, class: "label-bold"
= f.text_field :title, class: 'form-control gl-form-input', required: true, data: { testid: 'deploy-key-title-field' }
.form-group.row
= f.text_field :title, class: 'form-control gl-form-input gl-form-input-xl', required: true, data: { testid: 'deploy-key-title-field' }
.form-group
= f.label :key, class: "label-bold"
= f.text_area :key, class: 'form-control gl-form-input', rows: 5, required: true, data: { testid: 'deploy-key-field' }
.form-group.row
%p.light.gl-mb-0
= f.text_area :key, class: 'form-control gl-form-input gl-form-input-xl gl-h-auto!', rows: 5, required: true, data: { testid: 'deploy-key-field' }
.form-text.text-muted
= _('Paste a public key here.')
= link_to _('How do I generate it?'), help_page_path("user/ssh")
= f.fields_for :deploy_keys_projects do |deploy_keys_project_form|
.form-group.row
.form-group
= deploy_keys_project_form.gitlab_ui_checkbox_component :can_push, _('Grant write permissions to this key'),
help_text: _('Allow this key to push to this repository')
.form-group.row
.form-group
= f.label :expires_at, _('Expiration date (optional)'), class: 'label-bold'
= f.gitlab_ui_datepicker :expires_at, data: { testid: 'deploy-key-expires-at-field' }, value: f.object.expires_at
%p.form-text.text-muted= ssh_key_expires_field_description
.gl-form-input-xl
= f.gitlab_ui_datepicker :expires_at, data: { testid: 'deploy-key-expires-at-field' }, value: f.object.expires_at
.form-text.text-muted= ssh_key_expires_field_description
.form-group.row.gl-mb-0
.form-group.gl-mb-0
= f.submit _("Add key"), data: { testid: "add-deploy-key-button"}, pajamas_button: true
= render Pajamas::ButtonComponent.new(button_options: { type: 'reset', class: 'gl-ml-3 js-toggle-button' }) do
= _('Cancel')

View File

@ -583,6 +583,8 @@
- 1
- - search_zoekt_namespace_indexer
- 1
- - search_zoekt_project_transfer
- 1
- - security_auto_fix
- 1
- - security_generate_policy_violation_comment

View File

@ -0,0 +1,23 @@
# frozen_string_literal: true
class EnsureIssueUserMentionsBigintBackfillIsFinishedForSelfManaged < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
restrict_gitlab_migration gitlab_schema: :gitlab_main
disable_ddl_transaction!
def up
return if com_or_dev_or_test_but_not_jh?
ensure_batched_background_migration_is_finished(
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
table_name: 'issue_user_mentions',
column_name: 'id',
job_arguments: [['note_id'], ['note_id_convert_to_bigint']]
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,74 @@
# frozen_string_literal: true
class SwapIssueUserMentionsNoteIdToBigintForSelfManaged < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
disable_ddl_transaction!
TABLE_NAME = 'issue_user_mentions'
def up
return if com_or_dev_or_test_but_not_jh?
return if temp_column_removed?(TABLE_NAME, :note_id)
return if columns_swapped?(TABLE_NAME, :note_id)
swap
end
def down
return if com_or_dev_or_test_but_not_jh?
return if temp_column_removed?(TABLE_NAME, :note_id)
return unless columns_swapped?(TABLE_NAME, :note_id)
swap
end
def swap
# This will replace the existing index_issue_user_mentions_on_note_id
add_concurrent_index TABLE_NAME, :note_id_convert_to_bigint, unique: true,
name: 'index_issue_user_mentions_on_note_id_convert_to_bigint',
where: 'note_id_convert_to_bigint IS NOT NULL'
# This will replace the existing issue_user_mentions_on_issue_id_and_note_id_index
add_concurrent_index TABLE_NAME, [:issue_id, :note_id_convert_to_bigint], unique: true,
name: 'tmp_issue_user_mentions_on_issue_id_and_note_id_index'
# This will replace the existing issue_user_mentions_on_issue_id_index
add_concurrent_index TABLE_NAME, :issue_id, unique: true,
name: 'tmp_issue_user_mentions_on_issue_id_index',
where: 'note_id_convert_to_bigint IS NULL'
# This will replace the existing fk_rails_3861d9fefa
add_concurrent_foreign_key TABLE_NAME, :notes, column: :note_id_convert_to_bigint,
name: 'fk_issue_user_mentions_note_id_convert_to_bigint',
on_delete: :cascade
with_lock_retries(raise_on_exhaustion: true) do
execute "LOCK TABLE notes, #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN note_id TO note_id_tmp"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN note_id_convert_to_bigint TO note_id"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN note_id_tmp TO note_id_convert_to_bigint"
function_name = Gitlab::Database::UnidirectionalCopyTrigger
.on_table(TABLE_NAME, connection: connection)
.name(:note_id, :note_id_convert_to_bigint)
execute "ALTER FUNCTION #{quote_table_name(function_name)} RESET ALL"
execute 'DROP INDEX IF EXISTS index_issue_user_mentions_on_note_id'
rename_index TABLE_NAME, 'index_issue_user_mentions_on_note_id_convert_to_bigint',
'index_issue_user_mentions_on_note_id'
execute 'DROP INDEX IF EXISTS issue_user_mentions_on_issue_id_and_note_id_index'
rename_index TABLE_NAME, 'tmp_issue_user_mentions_on_issue_id_and_note_id_index',
'issue_user_mentions_on_issue_id_and_note_id_index'
execute 'DROP INDEX IF EXISTS issue_user_mentions_on_issue_id_index'
rename_index TABLE_NAME, 'tmp_issue_user_mentions_on_issue_id_index',
'issue_user_mentions_on_issue_id_index'
execute "ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT IF EXISTS fk_rails_3861d9fefa"
rename_constraint(TABLE_NAME, 'fk_issue_user_mentions_note_id_convert_to_bigint', 'fk_rails_3861d9fefa')
end
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class EnsureNoteDiffFilesBigintBackfillIsFinishedForSelfHosts < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
restrict_gitlab_migration gitlab_schema: :gitlab_main
disable_ddl_transaction!
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
table_name: 'note_diff_files',
column_name: 'id',
job_arguments: [['diff_note_id'], ['diff_note_id_convert_to_bigint']]
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,64 @@
# frozen_string_literal: true
class SwapNoteDiffFilesNoteIdToBigintForSelfHosts < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
disable_ddl_transaction!
TABLE_NAME = 'note_diff_files'
def up
return if should_skip?
return if temp_column_removed?(TABLE_NAME, :diff_note_id)
return if columns_swapped?(TABLE_NAME, :diff_note_id)
swap
end
def down
return if should_skip?
return if temp_column_removed?(TABLE_NAME, :diff_note_id)
return unless columns_swapped?(TABLE_NAME, :diff_note_id)
swap
end
def swap
# This will replace the existing index_note_diff_files_on_diff_note_id
add_concurrent_index TABLE_NAME, :diff_note_id_convert_to_bigint, unique: true,
name: 'index_note_diff_files_on_diff_note_id_convert_to_bigint'
# This will replace the existing fk_rails_3d66047aeb
add_concurrent_foreign_key TABLE_NAME, :notes, column: :diff_note_id_convert_to_bigint,
name: 'fk_note_diff_files_diff_note_id_convert_to_bigint',
on_delete: :cascade
with_lock_retries(raise_on_exhaustion: true) do
execute "LOCK TABLE notes, #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN diff_note_id TO diff_note_id_tmp"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN diff_note_id_convert_to_bigint TO diff_note_id"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN diff_note_id_tmp TO diff_note_id_convert_to_bigint"
function_name = Gitlab::Database::UnidirectionalCopyTrigger
.on_table(TABLE_NAME, connection: connection)
.name(:diff_note_id, :diff_note_id_convert_to_bigint)
execute "ALTER FUNCTION #{quote_table_name(function_name)} RESET ALL"
# Swap defaults
change_column_default TABLE_NAME, :diff_note_id, nil
change_column_default TABLE_NAME, :diff_note_id_convert_to_bigint, 0
execute 'DROP INDEX IF EXISTS index_note_diff_files_on_diff_note_id'
rename_index TABLE_NAME, 'index_note_diff_files_on_diff_note_id_convert_to_bigint',
'index_note_diff_files_on_diff_note_id'
execute "ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT IF EXISTS fk_rails_3d66047aeb"
rename_constraint(TABLE_NAME, 'fk_note_diff_files_diff_note_id_convert_to_bigint', 'fk_rails_3d66047aeb')
end
end
def should_skip?
com_or_dev_or_test_but_not_jh?
end
end

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
class EnsureEventsBigintBackfillIsFinishedForSelfHosts < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
restrict_gitlab_migration gitlab_schema: :gitlab_main
disable_ddl_transaction!
def up
ensure_batched_background_migration_is_finished(
job_class_name: 'CopyColumnUsingBackgroundMigrationJob',
table_name: 'events',
column_name: 'id',
job_arguments: [['target_id'], ['target_id_convert_to_bigint']]
)
end
def down
# no-op
end
end

View File

@ -0,0 +1,51 @@
# frozen_string_literal: true
class SwapEventsTargetIdToBigintForSelfHosts < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
disable_ddl_transaction!
TABLE_NAME = 'events'
def up
return if com_or_dev_or_test_but_not_jh?
return if temp_column_removed?(TABLE_NAME, :target_id)
return if columns_swapped?(TABLE_NAME, :target_id)
swap
end
def down
return if com_or_dev_or_test_but_not_jh?
return if temp_column_removed?(TABLE_NAME, :target_id)
return unless columns_swapped?(TABLE_NAME, :target_id)
swap
end
private
def swap
# This will replace the existing index_events_on_target_type_and_target_id_and_fingerprint
add_concurrent_index TABLE_NAME, [:target_type, :target_id_convert_to_bigint, :fingerprint],
name: :index_events_on_target_type_and_target_id_bigint_fingerprint,
unique: true
with_lock_retries(raise_on_exhaustion: true) do
execute "LOCK TABLE #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN target_id TO target_id_tmp"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN target_id_convert_to_bigint TO target_id"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN target_id_tmp TO target_id_convert_to_bigint"
function_name = Gitlab::Database::UnidirectionalCopyTrigger
.on_table(TABLE_NAME, connection: connection)
.name(:target_id, :target_id_convert_to_bigint)
execute "ALTER FUNCTION #{quote_table_name(function_name)} RESET ALL"
execute 'DROP INDEX IF EXISTS index_events_on_target_type_and_target_id_and_fingerprint'
rename_index TABLE_NAME, 'index_events_on_target_type_and_target_id_bigint_fingerprint',
'index_events_on_target_type_and_target_id_and_fingerprint'
end
end
end

View File

@ -0,0 +1 @@
19058c7442d020d7db5f0a8ec386adc27ce157254a9e00b42b1fa49551c2e585

View File

@ -0,0 +1 @@
b9a09d4b6cdfe5ac7e380af43b9e9ee508f90aecdaf03dbfbc348a966a4332fc

View File

@ -0,0 +1 @@
2176e2862d6445962f34c537dd00025fb5efbb6a55af90ca54f41dd7de18b897

View File

@ -0,0 +1 @@
e18df398f3e6146fd59bde7070d9b082be66ea315976e85817dc7ef63b8ab3bd

View File

@ -0,0 +1 @@
707bddb67e80158cf8f6703a87b6b7a98a651db5df7c356142e8512b0e4d7cde

View File

@ -0,0 +1 @@
c59a6161044f4b75e031159e84028eb8c823dfc8f32b6b743fb7278820b41ed9

View File

@ -70,10 +70,39 @@ You should only use LDAP integration if your LDAP users cannot:
## Configure LDAP
LDAP users must have a set email address, regardless of whether or not it's used
LDAP users must have an email address, regardless of whether or not it's used
to sign in.
Here's an example of setting up LDAP with only the required options.
After configuring LDAP, to test the configuration, use the
[LDAP check Rake task](../../raketasks/ldap.md#check).
### Basic configuration settings
> The `hosts` configuration setting was [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/139) in GitLab 14.7.
The following configuration settings are available:
| Setting | Required | Type | Description |
|--------------------|-------------|------|-------------|
| `label` | **{check-circle}** Yes | String | A human-friendly name for your LDAP server. It is displayed on your sign-in page. Example: `'Paris'` or `'Acme, Ltd.'` |
| `host` | **{check-circle}** Yes | String | IP address or domain name of your LDAP server. Ignored when `hosts` is defined. Example: `'ldap.mydomain.com'` |
| `port` | **{check-circle}** Yes | Integer | The port to connect with on your LDAP server. Ignored when `hosts` is defined. Example: `389` or `636` (for SSL) |
| `uid` | **{check-circle}** Yes | String | The LDAP attribute that maps to the username that users use to sign in. Should be the attribute, not the value that maps to the `uid`. Does not affect the GitLab username (see [attributes section](#attribute-configuration-settings)). Example: `'sAMAccountName'` or `'uid'` or `'userPrincipalName'` |
| `base` | **{check-circle}** Yes | String | Base where we can search for users. Example: `'ou=people,dc=gitlab,dc=example'` or `'DC=mydomain,DC=com'` |
| `encryption` | **{check-circle}** Yes | String | Encryption method (the `method` key is deprecated in favor of `encryption`). It can have one of three values: `'start_tls'`, `'simple_tls'`, or `'plain'`. `simple_tls` corresponds to 'Simple TLS' in the LDAP library. `start_tls` corresponds to StartTLS, not to be confused with regular TLS. If you specify `simple_tls`, usually it's on port 636, while `start_tls` (StartTLS) would be on port 389. `plain` also operates on port 389. |
| `hosts` | **{dotted-circle}** No | Array of strings and integers | An array of host and port pairs to open connections. Each configured server should have an identical data set. This is not meant to configure multiple distinct LDAP servers, but to configure failover. Hosts are tried in the order they are configured. Example: `[['ldap1.mydomain.com', 636], ['ldap2.mydomain.com', 636]]` |
| `bind_dn` | **{dotted-circle}** No | String | The full DN of the user you bind with. Example: `'america\momo'` or `'CN=Gitlab,OU=Users,DC=domain,DC=com'` |
| `password` | **{dotted-circle}** No | String | The password of the bind user. |
| `verify_certificates` | **{dotted-circle}** No | Boolean | Defaults to `true`. Enables SSL certificate verification if encryption method is `start_tls` or `simple_tls`. If set to `false`, no validation of the LDAP server's SSL certificate is performed. |
| `timeout` | **{dotted-circle}** No | Integer | Defaults to `10`. Set a timeout, in seconds, for LDAP queries. This helps avoid blocking a request if the LDAP server becomes unresponsive. A value of `0` means there is no timeout. |
| `active_directory` | **{dotted-circle}** No | Boolean | This setting specifies if LDAP server is Active Directory LDAP server. For non-AD servers it skips the AD specific queries. If your LDAP server is not AD, set this to false. |
| `allow_username_or_email_login` | **{dotted-circle}** No | Boolean | Defaults to `false`. If enabled, GitLab ignores everything after the first `@` in the LDAP username submitted by the user on sign-in. If you are using `uid: 'userPrincipalName'` on ActiveDirectory you must disable this setting, because the userPrincipalName contains an `@`. |
| `block_auto_created_users` | **{dotted-circle}** No | Boolean | Defaults to `false`. To maintain tight control over the number of billable users on your GitLab installation, enable this setting to keep new users blocked until they have been cleared by an administrator . |
| `user_filter` | **{dotted-circle}** No | String | Filter LDAP users. Follows the format of [RFC 4515](https://www.rfc-editor.org/rfc/rfc4515.html). GitLab does not support `omniauth-ldap`'s custom filter syntax. Examples of the `user_filter` field syntax:<br/><br/>- `'(employeeType=developer)'`<br/>- `'(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))'` |
| `lowercase_usernames` | **{dotted-circle}** No | Boolean | If enabled, GitLab converts the name to lower case. |
| `retry_empty_result_with_codes` | **{dotted-circle}** No | Array | An array of LDAP query response code that attempt to retry the operation if the result/content is empty. For Google Secure LDAP, set this value to `[80]`. |
Here's an example of setting up LDAP with the basic configuration settings.
::Tabs
@ -89,8 +118,18 @@ Here's an example of setting up LDAP with only the required options.
'host' => 'ldap.mydomain.com',
'port' => 636,
'uid' => 'sAMAccountName',
'bind_dn' => 'CN=Gitlab,OU=Users,DC=domain,DC=com',
'password' => '<bind_user_password>',
'encryption' => 'simple_tls',
'base' => 'dc=example,dc=com'
'verify_certificates' => true,
'timeout' => 10,
'active_directory' => false,
'user_filter' => '(employeeType=developer)',
'base' => 'dc=example,dc=com',
'lowercase_usernames' => 'false',
'retry_empty_result_with_codes' => [80],
'allow_username_or_email_login' => false,
'block_auto_created_users' => false
}
}
```
@ -122,7 +161,18 @@ Here's an example of setting up LDAP with only the required options.
port: 636
uid: 'sAMAccountName'
base: 'dc=example,dc=com'
bind_dn: 'CN=Gitlab,OU=Users,DC=domain,DC=com'
password: '<bind_user_password>'
encryption: 'simple_tls'
verify_certificates: true
timeout: 10
active_directory: false
user_filter: '(employeeType=developer)'
base: 'dc=example,dc=com'
lowercase_usernames: false
retry_empty_result_with_codes: [80]
allow_username_or_email_login: false
block_auto_created_users: false
```
1. Save the file and apply the new values:
@ -154,8 +204,19 @@ For more information, see
'host' => 'ldap.mydomain.com',
'port' => 636,
'uid' => 'sAMAccountName',
'encryption' => 'simple_tls',
'base' => 'dc=example,dc=com'
'bind_dn' => 'CN=Gitlab,OU=Users,DC=domain,DC=com',
'password' => '<bind_user_password>',
'encryption' => 'simple_tls',
'verify_certificates' => true,
'timeout' => 10,
'active_directory' => false,
'user_filter' => '(employeeType=developer)',
'base' => 'dc=example,dc=com',
'lowercase_usernames' => 'false',
'retry_empty_result_with_codes' => [80],
'allow_username_or_email_login' => false,
'block_auto_created_users' => false
}
}
```
@ -180,8 +241,19 @@ For more information, see
host: 'ldap.mydomain.com'
port: 636
uid: 'sAMAccountName'
encryption: 'simple_tls'
base: 'dc=example,dc=com'
bind_dn: 'CN=Gitlab,OU=Users,DC=domain,DC=com'
password: '<bind_user_password>'
encryption: 'simple_tls'
verify_certificates: true
timeout: 10
active_directory: false
user_filter: '(employeeType=developer)'
base: 'dc=example,dc=com'
lowercase_usernames: false
retry_empty_result_with_codes: [80]
allow_username_or_email_login: false
block_auto_created_users: false
```
1. Save the file and restart GitLab:
@ -199,42 +271,6 @@ For more information about the various LDAP options, see the `ldap` setting in
::EndTabs
After configuring LDAP, to test the configuration, use the
[LDAP check Rake task](../../raketasks/ldap.md#check).
### Basic configuration settings
> `hosts` configuration setting [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/139) in GitLab 14.7.
You can configure either:
- A single LDAP server using `host` and `port`.
- Many LDAP servers using `hosts`. This setting takes precedence over `host` and `port`. GitLab attempts to use the
LDAP servers in the order specified, and the first reachable LDAP server is used.
These configuration settings are available:
| Setting | Description | Required | Examples |
|--------------------|-------------|----------|----------|
| `label` | A human-friendly name for your LDAP server. It is displayed on your sign-in page. | **{check-circle}** Yes | `'Paris'` or `'Acme, Ltd.'` |
| `host` | IP address or domain name of your LDAP server. Ignored when `hosts` is defined. | **{check-circle}** Yes | `'ldap.mydomain.com'` |
| `port` | The port to connect with on your LDAP server. Always an integer, not a string. Ignored when `hosts` is defined. | **{check-circle}** Yes | `389` or `636` (for SSL) |
| `hosts` (GitLab 14.7 and later) | An array of host and port pairs to open connections. | **{dotted-circle}** No | `[['ldap1.mydomain.com', 636], ['ldap2.mydomain.com', 636]]` |
| `uid` | The LDAP attribute that maps to the username that users use to sign in. Should be the attribute, not the value that maps to the `uid`. Does not affect the GitLab username (see [attributes section](#attribute-configuration-settings)). | **{check-circle}** Yes | `'sAMAccountName'` or `'uid'` or `'userPrincipalName'` |
| `bind_dn` | The full DN of the user you bind with. | **{dotted-circle}** No | `'america\momo'` or `'CN=Gitlab,OU=Users,DC=domain,DC=com'` |
| `password` | The password of the bind user. | **{dotted-circle}** No | `'your_great_password'` |
| `encryption` | Encryption method. The `method` key is deprecated in favor of `encryption`. | **{check-circle}** Yes | `'start_tls'`, `'simple_tls'`, or `'plain'`. `simple_tls` corresponds to 'Simple TLS' in the LDAP library. `start_tls` corresponds to StartTLS, not to be confused with regular TLS. If you specify `simple_tls`, usually it's on port 636, while `start_tls` (StartTLS) would be on port 389. `plain` also operates on port 389. |
| `verify_certificates` | Enables SSL certificate verification if encryption method is `start_tls` or `simple_tls`. If set to false, no validation of the LDAP server's SSL certificate is performed. Defaults to true. | **{dotted-circle}** No | boolean |
| `timeout` | Set a timeout, in seconds, for LDAP queries. This helps avoid blocking a request if the LDAP server becomes unresponsive. A value of `0` means there is no timeout. (default: `10`) | **{dotted-circle}** No | `10` or `30` |
| `active_directory` | This setting specifies if LDAP server is Active Directory LDAP server. For non-AD servers it skips the AD specific queries. If your LDAP server is not AD, set this to false. | **{dotted-circle}** No | boolean |
| `allow_username_or_email_login` | If enabled, GitLab ignores everything after the first `@` in the LDAP username submitted by the user on sign-in. If you are using `uid: 'userPrincipalName'` on ActiveDirectory you must disable this setting, because the userPrincipalName contains an `@`. | **{dotted-circle}** No | boolean |
| `block_auto_created_users` | To maintain tight control over the number of billable users on your GitLab installation, enable this setting to keep new users blocked until they have been cleared by an administrator (default: false). | **{dotted-circle}** No | boolean |
| `base` | Base where we can search for users. | **{check-circle}** Yes | `'ou=people,dc=gitlab,dc=example'` or `'DC=mydomain,DC=com'` |
| `user_filter` | Filter LDAP users. Format: [RFC 4515](https://www.rfc-editor.org/rfc/rfc4515.html) Note: GitLab does not support `omniauth-ldap`'s custom filter syntax. | **{dotted-circle}** No | Some examples of the `user_filter` field syntax:<br/><br/>- `'(employeeType=developer)'`<br/>- `'(&(objectclass=user)(|(samaccountname=momo)(samaccountname=toto)))'` |
| `lowercase_usernames` | If enabled, GitLab converts the name to lower case. | **{dotted-circle}** No | boolean |
| `retry_empty_result_with_codes` | An array of LDAP query response code that attempt to retry the operation if the result/content is empty. For Google Secure LDAP, set this value to `[80]`. | **{dotted-circle}** No | `[80]` |
| `attributes` | A hash of attribute mappings to LDAP for GitLab to use ([see attributes section](#attribute-configuration-settings)). | **{dotted-circle}** No | `'attributes' => { 'username' => ['uid'], 'email' => ['mail', 'email'] },` |
### SSL configuration settings
SSL configuration settings can be configured under `tls_options` name/value

View File

@ -67,7 +67,7 @@ must disable the **primary** site.
- Physically disconnect a machine.
If you plan to [update the primary domain DNS record](#step-4-optional-updating-the-primary-domain-dns-record),
you may wish to lower the TTL now to speed up propagation.
you may wish to maintain a low TTL to ensure fast propagation of DNS changes.
### Step 3. Promoting a **secondary** site

View File

@ -90,6 +90,11 @@ gitlab-ctl promotion-preflight-checks
Each step is described in more detail below.
### DNS TTL
If you plan to [update the primary domain DNS record](index.md#step-4-optional-updating-the-primary-domain-dns-record),
you may wish to maintain a low TTL to ensure fast propagation of DNS changes.
### Object storage
If you have a large GitLab installation or cannot tolerate downtime, consider

View File

@ -1,81 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Admin Area'
redirect_to: 'impacted_features/admin-area.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Admin Area
In our Cells architecture proposal we plan to share all admin related tables in GitLab.
This allows for simpler management of all Cells in one interface and reduces the risk of settings diverging in different Cells.
This introduces challenges with Admin Area pages that allow you to manage data that will be spread across all Cells.
## 1. Definition
There are consequences for Admin Area pages that contain data that span "the whole instance" as the Admin Area pages may be served by any Cell or possibly just one Cell.
There are already many parts of the Admin Area that will have data that span many Cells.
For example lists of all Groups, Projects, Topics, Jobs, Analytics, Applications and more.
There are also administrative monitoring capabilities in the Admin Area that will span many Cells such as the "Background Jobs" and "Background Migrations" pages.
## 2. Data flow
## 3. Proposal
We will need to decide how to handle these exceptions with a few possible
options:
1. Move all these pages out into a dedicated per-Cell admin section. Probably
the URL will need to be routable to a single Cell like `/cells/<cell_id>/admin`,
then we can display these data per Cell. These pages will be distinct from
other Admin Area pages which control settings that are shared across all Cells. We
will also need to consider how this impacts self-managed customers and
whether, or not, this should be visible for single-Cell instances of GitLab.
1. Build some aggregation interfaces for this data so that it can be fetched
from all Cells and presented in a single UI. This may be beneficial to an
administrator that needs to see and filter all data at a glance, especially
when they don't know which Cell the data is on. The downside, however, is
that building this kind of aggregation is very tricky when all Cells are
designed to be totally independent, and it does also enforce stricter
requirements on compatibility between Cells.
The following overview describes at what level each feature contained in the current Admin Area will be managed:
| Feature | Cluster | Cell | Organization |
| --- | --- | --- | --- |
| Abuse reports | | | |
| Analytics | | | |
| Applications | | | |
| Deploy keys | | | |
| Labels | | | |
| Messages | ✓ | | |
| Monitoring | | ✓ | |
| Subscription | | | |
| System hooks | | | |
| Overview | | | |
| Settings - General | ✓ | | |
| Settings - Integrations | ✓ | | |
| Settings - Repository | ✓ | | |
| Settings - CI/CD (1) | ✓ | ✓ | |
| Settings - Reporting | ✓ | | |
| Settings - Metrics | ✓ | | |
| Settings - Service usage data | | ✓ | |
| Settings - Network | ✓ | | |
| Settings - Appearance | ✓ | | |
| Settings - Preferences | ✓ | | |
(1) Depending on the specific setting, some will be managed at the cluster-level, and some at the Cell-level.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/admin-area.md).

View File

@ -1,30 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Agent for Kubernetes'
redirect_to: 'impacted_features/agent-for-kubernetes.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Agent for Kubernetes
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/agent-for-kubernetes.md).

View File

@ -1,53 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Backups'
redirect_to: 'impacted_features/backups.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Backups
Each Cell will take its own backups, and consequently have its own isolated backup/restore procedure.
## 1. Definition
GitLab backup takes a backup of the PostgreSQL database used by the application, and also Git repository data.
## 2. Data flow
Each Cell has a number of application databases to back up (for example, `main`, and `ci`).
Additionally, there may be cluster-wide metadata tables (for example, `users` table) which is directly accessible via PostgreSQL.
## 3. Proposal
### 3.1. Cluster-wide metadata
It is currently unknown how cluster-wide metadata tables will be accessible.
We may choose to have cluster-wide metadata tables backed up separately, or have each Cell back up its copy of cluster-wide metadata tables.
### 3.2 Consistency
#### 3.2.1 Take backups independently
As each Cell will communicate with each other via API, and there will be no joins to the `users` table, it should be acceptable for each Cell to take a backup independently of each other.
#### 3.2.2 Enforce snapshots
We can require that each Cell take a snapshot for the PostgreSQL databases at around the same time to allow for a consistent enough backup.
## 4. Evaluation
As the number of Cells increases, it will likely not be feasible to take a snapshot at the same time for all Cells.
Hence taking backups independently is the better option.
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/backups.md).

View File

@ -1,144 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: CI Runners'
redirect_to: 'impacted_features/ci-runners.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: CI Runners
GitLab executes CI jobs via [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner/), very often managed by customers in their infrastructure.
All CI jobs created as part of the CI pipeline are run in the context of a Project.
This poses a challenge how to manage GitLab Runners.
## 1. Definition
There are 3 different types of runners:
- Instance-wide: Runners that are registered globally with specific tags (selection criteria)
- Group runners: Runners that execute jobs from a given top-level Group or Projects in that Group
- Project runners: Runners that execute jobs from one Projects or many Projects: some runners might
have Projects assigned from Projects in different top-level Groups.
This, alongside with the existing data structure where `ci_runners` is a table describing all types of runners, poses a challenge as to how the `ci_runners` should be managed in a Cells environment.
## 2. Data flow
GitLab runners use a set of globally scoped endpoints to:
- Register a new runner via registration token `https://gitlab.com/api/v4/runners`
([subject for removal](../runner_tokens/index.md)) (`registration token`)
- Create a new runner in the context of a user `https://gitlab.com/api/v4/user/runners` (`runner token`)
- Request jobs via an authenticated `https://gitlab.com/api/v4/jobs/request` endpoint (`runner token`)
- Upload job status via `https://gitlab.com/api/v4/jobs/:job_id` (`build token`)
- Upload trace via `https://gitlab.com/api/v4/jobs/:job_id/trace` (`build token`)
- Download and upload artifacts via `https://gitlab.com/api/v4/jobs/:job_id/artifacts` (`build token`)
Currently three types of authentication tokens are used:
- Runner registration token ([subject for removal](../runner_tokens/index.md))
- Runner token representing a registered runner in a system with specific configuration (`tags`, `locked`, etc.)
- Build token representing an ephemeral token giving limited access to updating a specific job, uploading artifacts, downloading dependent artifacts, downloading and uploading container registry images
Each of those endpoints receive an authentication token via header (`JOB-TOKEN` for `/trace`) or body parameter (`token` all other endpoints).
Since the CI pipeline would be created in the context of a specific Cell, it would be required that pick of a build would have to be processed by that particular Cell.
This requires that build picking depending on a solution would have to be either:
- Routed to the correct Cell for the first time
- Be two-phased: Request build from global pool, claim build on a specific Cell using a Cell specific URL
## 3. Proposal
### 3.1. Authentication tokens
Even though the paths for CI runners are not routable, they can be made routable with these two possible solutions:
- The `https://gitlab.com/api/v4/jobs/request` uses a long polling mechanism with
a ticketing mechanism (based on `X-GitLab-Last-Update` header). When the runner first
starts, it sends a request to GitLab to which GitLab responds with either a build to pick
by runner. This value is completely controlled by GitLab. This allows GitLab
to use JWT or any other means to encode a `cell` identifier that could be easily
decodable by Router.
- The majority of communication (in terms of volume) is using `build token`, making it
the easiest target to change since GitLab is the sole owner of the token that the runner later
uses for a specific job. There were prior discussions about not storing the `build token`
but rather using a `JWT` token with defined scopes. Such a token could encode the `cell`
to which the Router could route all requests.
### 3.2. Request body
- The most used endpoints pass the authentication token in the request body. It might be desired
to use HTTP headers as an easier way to access this information by Router without
a need to proxy requests.
### 3.3. Instance-wide are Cell-local
We can pick a design where all runners are always registered and local to a given Cell:
- Each Cell has its own set of instance-wide runners that are updated at its own pace
- The Project runners can only be linked to Projects from the same Organization, creating strong isolation.
- In this model the `ci_runners` table is local to the Cell.
- In this model we would require the above endpoints to be scoped to a Cell in some way, or be made routable. It might be via prefixing them, adding additional Cell parameters, or providing much more robust ways to decode runner tokens and match it to a Cell.
- If a routable token is used, we could move away from cryptographic random stored in database to rather prefer to use JWT tokens.
- The Admin Area showing registered runners would have to be scoped to a Cell.
This model might be desired because it provides strong isolation guarantees.
This model does significantly increase maintenance overhead because each Cell is managed separately.
This model may require adjustments to the runner tags feature so that Projects have a consistent runner experience across Cells.
### 3.4. Instance-wide are cluster-wide
Contrary to the proposal where all runners are Cell-local, we can consider that runners
are global, or just instance-wide runners are global.
However, this requires significant overhaul of the system and we would have to change the following aspects:
- The `ci_runners` table would likely have to be decomposed into `ci_instance_runners`, ...
- All interfaces would have to be adopted to use the correct table.
- Build queuing would have to be reworked to be two-phased where each Cell would know of all pending and running builds, but the actual claim of a build would happen against a Cell containing data.
- It is likely that `ci_pending_builds` and `ci_running_builds` would have to be made `cluster-wide` tables, increasing the likelihood of creating hotspots in a system related to CI queueing.
This model is complex to implement from an engineering perspective.
Some data are shared between Cells.
It creates hotspots/scalability issues in a system that might impact the experience of Organizations on other Cells, for instance during abuse.
### 3.5. GitLab CI Daemon
Another potential solution to explore is to have a dedicated service responsible for builds queueing, owning its database and working in a model of either sharded or Cell-ed service.
There were prior discussions about [CI/CD Daemon](https://gitlab.com/gitlab-org/gitlab/-/issues/19435).
If the service is sharded:
- Depending on the model, if runners are cluster-wide or Cell-local, this service would have to fetch data from all Cells.
- If the sharded service is used we could adapt a model of sharing a database containing `ci_pending_builds/ci_running_builds` with the service.
- If the sharded service is used we could consider a push model where each Cell pushes to CI/CD Daemon builds that should be picked by runner.
- The sharded service would be aware which Cell is responsible for processing the given build and could route processing requests to the designated Cell.
If the service is Cell-ed:
- All expectations of routable endpoints are still valid.
In general usage of CI Daemon does not help significantly with the stated problem.
However, this offers a few upsides related to more efficient processing and decoupling model: push model and it opens a way to offer stateful communication with GitLab runners (ex. gRPC or Websockets).
## 4. Evaluation
Considering all options it appears that the most promising solution is to:
- Use [Instance-wide are Cell-local](#33-instance-wide-are-cell-local)
- Refine endpoints to have routable identities (either via specific paths, or better tokens)
Another potential upside is to get rid of `ci_builds.token` and rather use a `JWT token` that can much better and easier encode a wider set of scopes allowed by CI runner.
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/ci-runners.md).

View File

@ -1,114 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Container Registry'
redirect_to: 'impacted_features/container-registry.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Container Registry
GitLab [Container Registry](../../../user/packages/container_registry/index.md) is a feature allowing to store Docker container images in GitLab.
## 1. Definition
GitLab Container Registry is a complex service requiring usage of PostgreSQL, Redis and Object Storage dependencies.
Right now there's undergoing work to introduce [Container Registry Metadata](../container_registry_metadata_database/index.md) to optimize data storage and image retention policies of Container Registry.
GitLab Container Registry is serving as a container for stored data, but on its own does not authenticate `docker login`.
The `docker login` is executed with user credentials (can be `personal access token`) or CI build credentials (ephemeral `ci_builds.token`).
Container Registry uses data deduplication.
It means that the same blob (image layer) that is shared between many Projects is stored only once.
Each layer is hashed by `sha256`.
The `docker login` does request a JWT time-limited authentication token that is signed by GitLab, but validated by Container Registry service.
The JWT token does store all authorized scopes (`container repository images`) and operation types (`push` or `pull`).
A single JWT authentication token can have many authorized scopes.
This allows Container Registry and client to mount existing blobs from other scopes.
GitLab responds only with authorized scopes.
Then it is up to GitLab Container Registry to validate if the given operation can be performed.
The GitLab.com pages are always scoped to a Project.
Each Project can have many container registry images attached.
Currently, on GitLab.com the actual registry service is served via `https://registry.gitlab.com`.
The main identifiable problems are:
- The authentication request (`https://gitlab.com/jwt/auth`) that is processed by GitLab.com.
- The `https://registry.gitlab.com` that is run by an external service and uses its own data store.
- Data deduplication. The Cells architecture with registry run in a Cell would reduce efficiency of data storage.
## 2. Data flow
### 2.1. Authorization request that is send by `docker login`
```shell
curl \
--user "username:password" \
"https://gitlab/jwt/auth?client_id=docker&offline_token=true&service=container_registry&scope=repository:gitlab-org/gitlab-build-images:push,pull"
```
Result is encoded and signed JWT token. Second base64 encoded string (split by `.`) contains JSON with authorized scopes.
```json
{"auth_type":"none","access":[{"type":"repository","name":"gitlab-org/gitlab-build-images","actions":["pull"]}],"jti":"61ca2459-091c-4496-a3cf-01bac51d4dc8","aud":"container_registry","iss":"omnibus-gitlab-issuer","iat":1669309469,"nbf":166}
```
### 2.2. Docker client fetching tags
```shell
curl \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer token" \
https://registry.gitlab.com/v2/gitlab-org/gitlab-build-images/tags/list
curl \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer token" \
https://registry.gitlab.com/v2/gitlab-org/gitlab-build-images/manifests/danger-ruby-2.6.6
```
### 2.3. Docker client fetching blobs and manifests
```shell
curl \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer token" \
https://registry.gitlab.com/v2/gitlab-org/gitlab-build-images/blobs/sha256:a3f2e1afa377d20897e08a85cae089393daa0ec019feab3851d592248674b416
```
## 3. Proposal
### 3.1. Shard Container Registry separately to Cells architecture
Due to its extensive and in general highly scalable horizontal architecture it should be evaluated if the GitLab Container Registry should be run not in Cell, but in a Cluster and be scaled independently.
This might be easier, but would definitely not offer the same amount of data isolation.
### 3.2. Run Container Registry within a Cell
It appears that except `/jwt/auth` which would likely have to be processed by Router (to decode `scope`) the Container Registry could be run as a local service of a Cell.
The actual data at least in case of GitLab.com is not forwarded via registry, but rather served directly from Object Storage / CDN.
Its design encodes container repository image in a URL that is easily routable.
It appears that we could re-use the same stateless Router service in front of Container Registry to serve manifests and blobs redirect.
The only downside is increased complexity of managing standalone registry for each Cell, but this might be desired approach.
## 4. Evaluation
There do not seem to be any theoretical problems with running GitLab Container Registry in a Cell.
It seems that the service can be easily made routable to work well.
The practical complexities are around managing a complex service from an infrastructure side.
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/container-registry.md).

View File

@ -1,106 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Contributions: Forks'
redirect_to: 'impacted_features/contributions-forks.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Contributions: Forks
The [Forking workflow](../../../user/project/repository/forking_workflow.md) allows users to copy existing Project sources into their own namespace of choice (Personal or Group).
## 1. Definition
The [Forking workflow](../../../user/project/repository/forking_workflow.md) is a common workflow with various usage patterns:
- It allows users to contribute back to upstream Project.
- It persists repositories into their Personal Namespace.
- Users can copy to make changes and release as modified Project.
Forks allow users not having write access to a parent Project to make changes.
The forking workflow is especially important for the open source community to contribute back to public Projects.
However, it is equally important in some companies that prefer a strong split of responsibilities and tighter access control.
The access to a Project is restricted to a designated list of developers.
Forks enable:
- Tighter control of who can modify the upstream Project.
- Split of responsibilities: Parent Project might use CI configuration connecting to production systems.
- To run CI pipelines in the context of a fork in a much more restrictive environment.
- To consider all forks to be unvetted which reduces risks of leaking secrets, or any other information tied to the Project.
The forking model is problematic in a Cells architecture for the following reasons:
- Forks are clones of existing repositories. Forks could be created across different Organizations, Cells and Gitaly shards.
- Users can create merge requests and contribute back to an upstream Project. This upstream Project might in a different Organization and Cell.
- The merge request CI pipeline is executed in the context of the source Project, but presented in the context of the target Project.
## 2. Data flow
## 3. Proposals
### 3.1. Intra-Cluster forks
This proposal implements forks as intra-Cluster forks where communication is done via API between all trusted Cells of a cluster:
- Forks are created always in the context of a user's choice of Group.
- Forks are isolated from the Organization.
- Organization or Group owner could disable forking across Organizations, or forking in general.
- A merge request is created in the context of the target Project, referencing the external Project on another Cell.
- To target Project the merge reference is transferred that is used for presenting information in context of the target Project.
- CI pipeline is fetched in the context of the source Project as it is today, the result is fetched into the merge request of the target Project.
- The Cell holding the target Project internally uses GraphQL to fetch the status of the source Project and includes in context of the information for merge request.
Upsides:
- All existing forks continue to work as they are, as they are treated as intra-Cluster forks.
Downsides:
- The purpose of Organizations is to provide strong isolation between Organizations. Allowing to fork across does break security boundaries.
- However, this is no different to the ability of users today to clone a repository to a local computer and push it to any repository of choice.
- Access control of source Project can be lower than those of target Project. Today, the system requires that in order to contribute back, the access level needs to be the same for fork and upstream.
### 3.2. Forks are created in a Personal Namespace of the current Organization
Instead of creating Projects across Organizations, forks are created in a user's Personal Namespace tied to the Organization. Example:
- Each user that is part of an Organization receives their Personal Namespace. For example for `GitLab Inc.` it could be `gitlab.com/organization/gitlab-inc/@ayufan`.
- The user has to fork into their own Personal Namespace of the Organization.
- The user has as many Personal Namespaces as Organizations they belongs to.
- The Personal Namespace behaves similar to the currently offered Personal Namespace.
- The user can manage and create Projects within a Personal Namespace.
- The Organization can prevent or disable usage of Personal Namespaces, disallowing forks.
- All current forks are migrated into the Personal Namespace of user in an Organization.
- All forks are part of the Organization.
- Forks are not federated features.
- The Personal Namespace and forked Project do not share configuration with the parent Project.
### 3.3. Forks are created as internal Projects under current Projects
Instead of creating Projects across Organizations, forks are attachments to existing Projects.
Each user forking a Project receives their unique Project. Example:
- For Project: `gitlab.com/gitlab-org/gitlab`, forks would be created in `gitlab.com/gitlab-org/gitlab/@kamil-gitlab`.
- Forks are created in the context of the current Organization, they do not cross Organization boundaries and are managed by the Organization.
- Tied to the user (or any other user-provided name of the fork).
- Forks are not federated features.
Downsides:
- Does not answer how to handle and migrate all existing forks.
- Might share current Group/Project settings, which could be breaking some security boundaries.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/contributions-forks.md).

View File

@ -1,99 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Data migration'
redirect_to: 'impacted_features/data-migration.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Data migration
It is essential for a Cells architecture to provide a way to migrate data out of big Cells into smaller ones.
This document describes various approaches to provide this type of split.
We also need to handle cases where data is already violating the expected isolation constraints of Cells, for example references cannot span multiple Organizations.
We know that existing features like linked issues allowed users to link issues across any Projects regardless of their hierarchy.
There are many similar features.
All of this data will need to be migrated in some way before it can be split across different Cells.
This may mean some data needs to be deleted, or the feature needs to be changed and modelled slightly differently before we can properly split or migrate Organizations between Cells.
Having schema deviations across different Cells, which is a necessary consequence of different databases, will also impact our ability to migrate data between Cells.
Different schemas impact our ability to reliably replicate data across Cells and especially impact our ability to validate that the data is correctly replicated.
It might force us to only be able to move data between Cells when the schemas are all in sync (slowing down deployments and the rebalancing process) or possibly only migrate from newer to older schemas which would be complex.
## 1. Definition
## 2. Data flow
## 3. Proposal
### 3.1. Split large Cells
A single Cell can only be divided into many Cells.
This is based on the principle that it is easier to create an exact clone of an existing Cell in many replicas out of which some will be made authoritative once migrated.
Keeping those replicas up-to-date with Cell 0 is also much easier due to pre-existing replication solutions that can replicate the whole systems: Geo, PostgreSQL physical replication, etc.
1. All data of an Organization needs to not be divided across many Cells.
1. Split should be doable online.
1. New Cells cannot contain pre-existing data.
1. N Cells contain exact replica of Cell 0.
1. The data of Cell 0 is live replicated to as many Cells it needs to be split.
1. Once consensus is achieved between Cell 0 and N-Cells, the Organizations to be migrated away are marked as read-only cluster-wide.
1. The `routes` is updated on for all Organizations to be split to indicate an authoritative Cell holding the most recent data, like `gitlab-org` on `cell-100`.
1. The data for `gitlab-org` on Cell 0, and on other non-authoritative N-Cells are dormant and will be removed in the future.
1. All accesses to `gitlab-org` on a given Cell are validated about `cell_id` of `routes` to ensure that given Cell is authoritative to handle the data.
#### More challenges of this proposal
1. There is no streaming replication capability for Elasticsearch, but you could
snapshot the whole Elasticsearch index and recreate, but this takes hours.
It could be handled by pausing Elasticsearch indexing on the initial Cell during
the migration as indexing downtime is not a big issue, but this still needs
to be coordinated with the migration process.
1. Syncing Redis, Gitaly, CI Postgres, Main Postgres, registry Postgres, other
new data stores snapshots in an online system would likely lead to gaps
without a long downtime. You need to choose a sync point and at the sync
point you need to stop writes to perform the migration. The more data stores
there are to migrate at the same time the longer the write downtime for the
failover. We would also need to find a reliable place in the application to
actually block updates to all these systems with a high degree of
confidence. In the past we've only been confident by shutting down all Rails
services because any Rails process could write directly to any of these at
any time due to async workloads or other surprising code paths.
1. How to efficiently delete all the orphaned data. Locating all `ci_builds`
associated with half the Organizations would be very expensive if we have to
do joins. We haven't yet determined if we'd want to store an `organization_id`
column on every table, but this is the kind of thing it would be helpful for.
### 3.2. Migrate Organization from an existing Cell
This is different to split, as we intend to perform logical and selective replication of data belonging to a single Organization.
Today this type of selective replication is only implemented by Gitaly where we can migrate Git repository from a single Gitaly node to another with minimal downtime.
In this model we would require identifying all resources belonging to a given Organization: database rows, object storage files, Git repositories, etc. and selectively copy them over to another (likely) existing Cell importing data into it.
Ideally ensuring that we can perform logical replication live of all changed data, but change similarly to split which Cell is authoritative for this Organization.
1. It is hard to identify all resources belonging to an Organization.
1. It requires either downtime for the Organization or a robust system to identify live changes made.
1. It likely will require a full database structure analysis (more robust than Project import/export) to perform selective PostgreSQL logical replication.
#### More challenges of this proposal
1. Logical replication is still not performant enough to keep up with our
scale. Even if we could use logical replication we still don't have an
efficient way to filter data related to a single Organization without
joining all the way to the `organizations` table which will slow down
logical replication dramatically.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/data-migration.md).

View File

@ -1,74 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Database Sequences'
redirect_to: 'impacted_features/database-sequences.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Database Sequences
GitLab today ensures that every database row create has a unique ID, allowing to access a merge request, CI Job or Project by a known global ID.
Cells will use many distinct and not connected databases, each of them having a separate ID for most entities.
At a minimum, any ID referenced between a Cell and the shared schema will need to be unique across the cluster to avoid ambiguous references.
Further to required global IDs, it might also be desirable to retain globally unique IDs for all database rows to allow migrating resources between Cells in the future.
## 1. Definition
## 2. Data flow
## 3. Proposal
These are some preliminary ideas how we can retain unique IDs across the system.
### 3.1. UUID
Instead of using incremental sequences, use UUID (128 bit) that is stored in the database.
- This might break existing IDs and requires adding a UUID column for all existing tables.
- This makes all indexes larger as it requires storing 128 bit instead of 32/64 bit in index.
### 3.2. Use Cell index encoded in ID
Because a significant number of tables already use 64 bit ID numbers we could use MSB to encode the Cell ID:
- This might limit the amount of Cells that can be enabled in a system, as we might decide to only allocate 1024 possible Cell numbers.
- This would make it possible to migrate IDs between Cells, because even if an entity from Cell 1 is migrated to Cell 100 this ID would still be unique.
- If resources are migrated the ID itself will not be enough to decode the Cell number and we would need a lookup table.
- This requires updating all IDs to 32 bits.
### 3.3. Allocate sequence ranges from central place
Each Cell might receive its own range of sequences as they are consumed from a centrally managed place.
Once a Cell consumes all IDs assigned for a given table it would be replenished and a next range would be allocated.
Ranges would be tracked to provide a faster lookup table if a random access pattern is required.
- This might make IDs migratable between Cells, because even if an entity from Cell 1 is migrated to Cell 100 this ID would still be unique.
- If resources are migrated the ID itself will not be enough to decode the Cell number and we would need a much more robust lookup table as we could be breaking previously assigned sequence ranges.
- This does not require updating all IDs to 64 bits.
- This adds some performance penalty to all `INSERT` statements in Postgres or at least from Rails as we need to check for the sequence number and potentially wait for our range to be refreshed from the ID server.
- The available range will need to be stored and incremented in a centralized place so that concurrent transactions cannot possibly get the same value.
### 3.4. Define only some tables to require unique IDs
Maybe it is acceptable only for some tables to have a globally unique IDs. It could be Projects, Groups and other top-level entities.
All other tables like `merge_requests` would only offer a Cell-local ID, but when referenced outside it would rather use an IID (an ID that is monotonic in context of a given resource, like a Project).
- This makes the ID 10000 for `merge_requests` be present on all Cells, which might be sometimes confusing regarding the uniqueness of the resource.
- This might make random access by ID (if ever needed) impossible without using a composite key, like: `project_id+merge_request_id`.
- This would require us to implement a transformation/generation of new ID if we need to migrate records to another Cell. This can lead to very difficult migration processes when these IDs are also used as foreign keys for other records being migrated.
- If IDs need to change when moving between Cells this means that any links to records by ID would no longer work even if those links included the `project_id`.
- If we plan to allow these IDs to not be unique and change the unique constraint to be based on a composite key then we'd need to update all foreign key references to be based on the composite key.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/database-sequences.md).

View File

@ -1,71 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Explore'
redirect_to: 'impacted_features/explore.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Explore
Explore may not play a critical role in GitLab as it functions today, but GitLab today is not isolated. It is the isolation that makes Explore or some viable replacement necessary.
The existing Group and Project Explore will initially be scoped to an Organization. However, there is a need for a global Explore that spans across Organizations to support the discoverability of public Groups and Projects, in particular in the context of discovering open source Projects. See user feedback [here](https://gitlab.com/gitlab-org/gitlab/-/issues/21582#note_1458298192) and [here](https://gitlab.com/gitlab-org/gitlab/-/issues/418228#note_1470045468).
## 1. Definition
The Explore functionality helps users in discovering Groups and Projects. Unauthenticated Users are only able to explore public Groups and Projects, authenticated Users can see all the Groups and Projects that they have access to, including private and internal Groups and Projects.
## 2. Data flow
## 3. Proposal
The Explore feature problem falls under the broader umbrella of solving inter-Cell communication. [This topic warrants deeper research](index.md#can-different-cells-communicate-with-each-other).
Below are possible directions for further investigation.
### 3.1. Read only table mirror
- Create a `shared_projects` table in the shared cluster-wide database.
- The model for this table is read-only. No inserts/updates/deletes are allowed.
- The table is filled with data (or a subset of data) from the Projects Cell-local table.
- The write model Project (which is Cell-local) writes to the local database. We will primarily use this model for anything Cell-local.
- This data is synchronized with `shared_projects` via a background job any time something changes.
- The data in `shared_projects` is stored normalized, so that all the information necessary to display the Project Explore is there.
- The Project Explore (as of today) is part of an instance-wide functionality, since it's not namespaced to any organizations/groups.
- This section will read data using the read model for `shared_projects`.
- Once the user clicks on a Project, they are redirected to the Cell containing the Organization.
Downsides:
- Need to have an explicit pattern to access instance-wide data. This however may be useful for admin functionalities too.
- The Project Explore may not be as rich in features as it is today (various filtering options, role you have on that Project, etc.).
- Extra complexity in managing CQRS.
### 3.2 Explore scoped to an Organization
The Project Explore and Group Explore are scoped to an Organization.
Downsides:
- No global discoverability of Groups and Projects.
## 4. Evaluation
The existing Group and Project Explore will initially be scoped to an Organization. Considering the [current usage of the Explore feature](https://gitlab.com/gitlab-data/product-analytics/-/issues/1302#note_1491215521), we deem this acceptable. Since all existing Users, Groups and Projects will initially be part of the default Organization, Groups and Projects will remain explorable and accessible as they are today. Only once existing Groups and Projects are moved out of the default Organization into different Organizations will this become a noticeable problem. Solutions to mitigate this are discussed in [issue #418228](https://gitlab.com/gitlab-org/gitlab/-/issues/418228). Ultimately, Explore could be replaced with a better search experience altogether.
## 4.1. Pros
- Initially the lack of discoverability will not be a problem.
- Only around [1.5% of all exisiting Users are using the Explore functionality on a monthly basis](https://gitlab.com/gitlab-data/product-analytics/-/issues/1302#note_1491215521).
## 4.2. Cons
- The GitLab owned top-level Groups would be some of the first to be moved into their own Organization and thus be detached from the explorability of the default Organization.
This document was moved to [another location](impacted_features/explore.md).

View File

@ -1,156 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Git Access'
redirect_to: 'impacted_features/git-access.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Git Access
This document describes impact of Cells architecture on all Git access (over HTTPS and SSH) patterns providing explanation of how potentially those features should be changed to work well with Cells.
## 1. Definition
Git access is done throughout the application.
It can be an operation performed by the system (read Git repository) or by a user (create a new file via Web IDE, `git clone` or `git push` via command line).
The Cells architecture defines that all Git repositories will be local to the Cell, so no repository could be shared with another Cell.
The Cells architecture will require that any Git operation can only be handled by a Cell holding the data.
It means that any operation either via Web interface, API, or GraphQL needs to be routed to the correct Cell.
It means that any `git clone` or `git push` operation can only be performed in the context of a Cell.
## 2. Data flow
The are various operations performed today by GitLab on a Git repository.
This describes the data flow how they behave today to better represent the impact.
It appears that Git access does require changes only to a few endpoints that are scoped to a Project.
There appear to be different types of repositories:
- Project: assigned to Group
- Wiki: additional repository assigned to Project
- Design: similar to Wiki, additional repository assigned to Project
- Snippet: creates a virtual Project to hold repository, likely tied to the User
### 2.1. Git clone over HTTPS
Execution of: `git clone` over HTTPS
```mermaid
sequenceDiagram
User ->> Workhorse: GET /gitlab-org/gitlab.git/info/refs?service=git-upload-pack
Workhorse ->> Rails: GET /gitlab-org/gitlab.git/info/refs?service=git-upload-pack
Rails ->> Workhorse: 200 OK
Workhorse ->> Gitaly: RPC InfoRefsUploadPack
Gitaly ->> User: Response
User ->> Workhorse: POST /gitlab-org/gitlab.git/git-upload-pack
Workhorse ->> Gitaly: RPC PostUploadPackWithSidechannel
Gitaly ->> User: Response
```
### 2.2. Git clone over SSH
Execution of: `git clone` over SSH
```mermaid
sequenceDiagram
User ->> Git SSHD: ssh git@gitlab.com
Git SSHD ->> Rails: GET /api/v4/internal/authorized_keys
Rails ->> Git SSHD: 200 OK (list of accepted SSH keys)
Git SSHD ->> User: Accept SSH
User ->> Git SSHD: git clone over SSH
Git SSHD ->> Rails: POST /api/v4/internal/allowed?project=/gitlab-org/gitlab.git&service=git-upload-pack
Rails ->> Git SSHD: 200 OK
Git SSHD ->> Gitaly: RPC SSHUploadPackWithSidechannel
Gitaly ->> User: Response
```
### 2.3. Git push over HTTPS
Execution of: `git push` over HTTPS
```mermaid
sequenceDiagram
User ->> Workhorse: GET /gitlab-org/gitlab.git/info/refs?service=git-receive-pack
Workhorse ->> Rails: GET /gitlab-org/gitlab.git/info/refs?service=git-receive-pack
Rails ->> Workhorse: 200 OK
Workhorse ->> Gitaly: RPC PostReceivePack
Gitaly ->> Rails: POST /api/v4/internal/allowed?gl_repository=project-111&service=git-receive-pack
Gitaly ->> Rails: POST /api/v4/internal/pre_receive?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/post_receive?gl_repository=project-111
Gitaly ->> User: Response
```
### 2.4. Git push over SSHD
Execution of: `git clone` over SSH
```mermaid
sequenceDiagram
User ->> Git SSHD: ssh git@gitlab.com
Git SSHD ->> Rails: GET /api/v4/internal/authorized_keys
Rails ->> Git SSHD: 200 OK (list of accepted SSH keys)
Git SSHD ->> User: Accept SSH
User ->> Git SSHD: git clone over SSH
Git SSHD ->> Rails: POST /api/v4/internal/allowed?project=/gitlab-org/gitlab.git&service=git-receive-pack
Rails ->> Git SSHD: 200 OK
Git SSHD ->> Gitaly: RPC ReceivePack
Gitaly ->> Rails: POST /api/v4/internal/allowed?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/pre_receive?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/post_receive?gl_repository=project-111
Gitaly ->> User: Response
```
### 2.5. Create commit via Web
Execution of `Add CHANGELOG` to repository:
```mermaid
sequenceDiagram
Web ->> Puma: POST /gitlab-org/gitlab/-/create/main
Puma ->> Gitaly: RPC TreeEntry
Gitaly ->> Rails: POST /api/v4/internal/allowed?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/pre_receive?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/post_receive?gl_repository=project-111
Gitaly ->> Puma: Response
Puma ->> Web: See CHANGELOG
```
## 3. Proposal
The Cells stateless router proposal requires that any ambiguous path (that is not routable) will be made routable.
It means that at least the following paths will have to be updated to introduce a routable entity (Project, Group, or Organization).
Change:
- `/api/v4/internal/allowed` => `/api/v4/internal/projects/<gl_repository>/allowed`
- `/api/v4/internal/pre_receive` => `/api/v4/internal/projects/<gl_repository>/pre_receive`
- `/api/v4/internal/post_receive` => `/api/v4/internal/projects/<gl_repository>/post_receive`
- `/api/v4/internal/lfs_authenticate` => `/api/v4/internal/projects/<gl_repository>/lfs_authenticate`
Where:
- `gl_repository` can be `project-1111` (`Gitlab::GlRepository`)
- `gl_repository` in some cases might be a full path to repository as executed by GitLab Shell (`/gitlab-org/gitlab.git`)
## 4. Evaluation
Supporting Git repositories if a Cell can access only its own repositories does not appear to be complex.
The one major complication is supporting snippets, but this likely falls in the same category as for the approach to support a user's Personal Namespace.
## 4.1. Pros
1. The API used for supporting HTTPS/SSH and Hooks are well defined and can easily be made routable.
## 4.2. Cons
1. The sharing of repositories objects is limited to the given Cell and Gitaly node.
1. Cross-Cells forks are likely impossible to be supported (discover: How this works today across different Gitaly node).
This document was moved to [another location](impacted_features/git-access.md).

View File

@ -1,30 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: GitLab Pages'
redirect_to: 'impacted_features/gitlab-pages.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: GitLab Pages
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/gitlab-pages.md).

View File

@ -1,35 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Global search'
redirect_to: 'impacted_features/global-search.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Global search
When we introduce multiple Cells we intend to isolate all services related to those Cells.
This will include Elasticsearch which means our current global search functionality will not work.
It may be possible to implement aggregated search across all Cells, but it is unlikely to be performant to do fan-out searches across all Cells especially once you start to do pagination which requires setting the correct offset and page number for each search.
## 1. Definition
## 2. Data flow
## 3. Proposal
Likely the first versions of Cells will not support global searches.
Later, we may consider if building global searches to support popular use cases is worthwhile.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/global-search.md).

View File

@ -1,81 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: GraphQL'
redirect_to: 'impacted_features/graphql.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: GraphQL
GitLab extensively uses GraphQL to perform efficient data query operations.
GraphQL due to it's nature is not directly routable.
The way GitLab uses it calls the `/api/graphql` endpoint, and only the query or mutation of the body request might define where the data can be accessed.
## 1. Definition
## 2. Data flow
## 3. Proposal
There are at least two main ways to implement GraphQL in a Cells architecture.
### 3.1. GraphQL routable by endpoint
Change `/api/graphql` to `/api/organization/<organization>/graphql`.
- This breaks all existing usages of `/api/graphql` endpoint because the API URI is changed.
### 3.2. GraphQL routable by body
As part of router parse GraphQL body to find a routable entity, like `project`.
- This still makes the GraphQL query be executed only in context of a given Cell and not allowing the data to be merged.
```json
# Good example
{
project(fullPath:"gitlab-org/gitlab") {
id
description
}
}
# Bad example, since Merge Request is not routable
{
mergeRequest(id: 1111) {
iid
description
}
}
```
### 3.3. Merging GraphQL Proxy
Implement as part of router GraphQL Proxy which can parse body and merge results from many Cells.
- This might make pagination hard to achieve, or we might assume that we execute many queries of which results are merged across all Cells.
```json
{
project(fullPath:"gitlab-org/gitlab"){
id, description
}
group(fullPath:"gitlab-com") {
id, description
}
}
```
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/graphql.md).

View File

@ -1,36 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Organizations'
redirect_to: 'impacted_features/organizations.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Organizations
One of the major designs of a Cells architecture is strong isolation between Groups.
Organizations as described by the [Organization blueprint](../organization/index.md) provides a way to have plausible UX for joining together many Groups that are isolated from the rest of the system.
## 1. Definition
Cells do require that all Groups and Projects of a single Organization can only be stored on a single Cell because a Cell can only access data that it holds locally and has very limited capabilities to read information from other Cells.
Cells with Organizations do require strong isolation between Organizations.
It will have significant implications on various user-facing features, like Todos, dropdowns allowing to select Projects, references to other issues or Projects, or any other social functions present at GitLab.
Today those functions were able to reference anything in the whole system.
With the introduction of Organizations this will be forbidden.
This problem definition aims to answer effort and implications required to add strong isolation between Organizations to the system, including features affected and their data processing flow.
The purpose is to ensure that our solution when implemented consistently avoids data leakage between Organizations residing on a single Cell.
## 2. Proposal
See the [Organization blueprint](../organization/index.md).
This document was moved to [another location](impacted_features/organizations.md).

View File

@ -1,31 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Personal Access Tokens'
redirect_to: 'impacted_features/personal-access-tokens.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Personal Access Tokens
## 1. Definition
Personal Access Tokens associated with a User are a way for Users to interact with the API of GitLab to perform operations.
Personal Access Tokens today are scoped to the User, and can access all Groups that a User has access to.
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/personal-access-tokens.md).

View File

@ -1,30 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Personal Namespaces'
redirect_to: 'impacted_features/personal-namespaces.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Personal Namespaces
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/personal-namespaces.md).

View File

@ -1,34 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Router Endpoints Classification'
redirect_to: 'impacted_features/router-endpoints-classification.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Router Endpoints Classification
Classification of all endpoints is essential to properly route requests hitting the load balancer of a GitLab installation to a Cell that can serve it.
Each Cell should be able to decode each request and classify which Cell it belongs to.
GitLab currently implements hundreds of endpoints.
This document tries to describe various techniques that can be implemented to allow the Rails to provide this information efficiently.
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/router-endpoints-classification.md).

View File

@ -1,38 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Schema changes'
redirect_to: 'impacted_features/schema-changes.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Schema changes
When we introduce multiple Cells that own their own databases this will complicate the process of making schema changes to Postgres and Elasticsearch.
Today we already need to be careful to make changes comply with our zero downtime deployments.
For example, [when removing a column we need to make changes over 3 separate deployments](../../../development/database/avoiding_downtime_in_migrations.md#dropping-columns).
We have tooling like `post_migrate` that helps with these kinds of changes to reduce the number of merge requests needed, but these will be complicated when we are dealing with deploying multiple Rails applications that will be at different versions at any one time.
This problem will be particularly tricky to solve for shared databases like our plan to share the `users` related tables among all Cells.
A key benefit of Cells may be that it allows us to run different customers on different versions of GitLab.
We may choose to update our own Cell before all our customers giving us even more flexibility than our current canary architecture.
But doing this means that schema changes need to have even more versions of backward compatibility support which could slow down development as we need extra steps to make schema changes.
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/schema-changes.md).

View File

@ -1,43 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Secrets'
redirect_to: 'impacted_features/secrets.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Secrets
Where possible, each Cell should have its own distinct set of secrets.
However, there will be some secrets that will be required to be the same for all Cells in the cluster.
## 1. Definition
GitLab has a lot of [secrets](https://docs.gitlab.com/charts/installation/secrets.html) that need to be configured.
Some secrets are for inter-component communication, for example, `GitLab Shell secret`, and used only within a Cell.
Some secrets are used for features, for example, `ci_jwt_signing_key`.
## 2. Data flow
## 3. Proposal
1. Secrets used for features will need to be consistent across all Cells, so that the UX is consistent.
1. This is especially true for the `db_key_base` secret which is used for
encrypting data at rest in the database - so that Projects that are
transferred to another Cell will continue to work. We do not want to have
to re-encrypt such rows when we move Projects/Groups between Cells.
1. Secrets which are used for intra-Cell communication only should be uniquely generated
per Cell.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/secrets.md).

View File

@ -1,56 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Snippets'
redirect_to: 'impacted_features/snippets.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Snippets
Snippets will be scoped to an Organization. Initially it will not be possible to aggregate snippet collections across Organizations. See also [issue #416954](https://gitlab.com/gitlab-org/gitlab/-/issues/416954).
## 1. Definition
Two different types of snippets exist:
- [Project snippets](../../../api/project_snippets.md). These snippets have URLs
like `/<group>/<project>/-/snippets/123`
- [Personal snippets](../../../user/snippets.md). These snippets have URLs like
`/-/snippets/123`
Snippets are backed by a Git repository.
## 2. Data flow
## 3. Proposal
### 3.1. Scoped to an organization
Both project and personal snippets will be scoped to an Organization.
- Project snippets URLs will remain unchanged, as the URLs are routable.
- Personal snippets URLs will need to change to be `/-/organizations/<organization>/snippets/123`,
so that the URL is routeable
Creation of snippets will also be scoped to a User's current Organization. Because of that, we recommend renaming `personal snippets` to `organization snippets` once the Organization is rolled out. A User can create many independent snippet collections across multiple Organizations.
## 4. Evaluation
Snippets are scoped to an Organization because Gitaly is confined to a Cell.
## 4.1. Pros
- No need to have clusterwide Gitaly.
## 4.2. Cons
- We will break [snippet discovery](/ee/user/snippets.md#discover-snippets).
- Snippet access may become subordinate to the visibility of the Organization.
This document was moved to [another location](impacted_features/snippets.md).

View File

@ -1,30 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Problem A'
redirect_to: 'impacted_features/template.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: A
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/template.md).

View File

@ -1,30 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Uploads'
redirect_to: 'impacted_features/uploads.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Uploads
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons
This document was moved to [another location](impacted_features/uploads.md).

View File

@ -1,52 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: User Profile'
redirect_to: 'impacted_features/user-profile.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: User Profile
The existing User Profiles will initially be scoped to an Organization. Long-term, we should consider aggregating parts of the User activity across Organizations to enable Users a global view of their contributions.
## 1. Definition
Each GitLab account has a [User Profile](../../../user/profile/index.md), which contains information about the User and their GitLab activity.
## 2. Data flow
## 3. Proposal
User Profiles will be scoped to an Organization.
- Users can set a Home Organization as their main Organization.
- Users who do not exist in the database at all display a 404 not found error when trying to access their User Profile.
- User who haven't contributed to an Organization display their User Profile with an empty state.
- When displaying a User Profile empty state, if the profile has a Home Organization set to another Organization, we display a call-to-action allowing navigation to the main Organization.
- User Profile URLs will not reference the Organization and remain as: `/<username>`. We follow the same pattern as is used for `Your Work`, meaning that profiles are always seen in the context of an Organization.
- Breadcrumbs on the User Profile will present as `[Organization Name] / [Username]`.
See [issue #411931](https://gitlab.com/gitlab-org/gitlab/-/issues/411931) for design proposals.
## 4. Evaluation
We expect the [majority of Users to perform most of their activity in one single Organization](../organization/index.md#data-exploration).
This is why we deem it acceptable to scope the User Profile to an Organization at first.
More discovery is necessary to understand which aspects of the current User Profile are relevant to showcase contributions in a global context.
## 4.1. Pros
- Viewing a User Profile scoped to an Organization allows you to focus on contributions that are most relevant to your Organization, filtering out the User's other activities.
- Existing User Profile URLs do not break.
## 4.2. Cons
- Users will lose the ability to display their entire activity, which may lessen the effectiveness of using their User Profile as a resume of achievements when working across multiple Organizations.
This document was moved to [another location](impacted_features/user-profile.md).

View File

@ -1,58 +1,6 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Your Work'
redirect_to: 'impacted_features/your-work.md'
remove_date: '2023-11-17'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Your Work
Your Work will be scoped to an Organization.
Counts presented in the individual dashboards will relate to the selected Organization.
## 1. Definition
When accessing `gitlab.com/dashboard/`, users can find a [focused view of items that they have access to](../../../tutorials/left_sidebar/index.md#use-a-more-focused-view).
This overview contains dashboards relating to:
- Projects
- Groups
- Issues
- Merge requests
- To-Do list
- Milestones
- Snippets
- Activity
- Workspaces
- Environments
- Operations
- Security
## 2. Data flow
## 3. Proposal
Your Work will be scoped to an Organization, giving the user an overview of all the items they can access in the Organization they are currently viewing.
- Issue, Merge request and To-Do list counts will refer to the selected Organization.
## 4. Evaluation
Scoping Your Work to an Organization makes sense in the context of the [proposed Organization navigation](https://gitlab.com/gitlab-org/gitlab/-/issues/417778).
Considering that [we expect most users to work in a single Organization](../organization/index.md#data-exploration), we deem this impact acceptable.
## 4.1. Pros
- Viewing Your Work scoped to an Organization allows Users to focus on content that is most relevant to their currently selected Organization.
## 4.2. Cons
- Users working across multiple Organizations will have to navigate to each Organization to access all of their work items.
This document was moved to [another location](impacted_features/your-work.md).

View File

@ -0,0 +1,81 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Admin Area'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Admin Area
In our Cells architecture proposal we plan to share all admin related tables in GitLab.
This allows for simpler management of all Cells in one interface and reduces the risk of settings diverging in different Cells.
This introduces challenges with Admin Area pages that allow you to manage data that will be spread across all Cells.
## 1. Definition
There are consequences for Admin Area pages that contain data that span "the whole instance" as the Admin Area pages may be served by any Cell or possibly just one Cell.
There are already many parts of the Admin Area that will have data that span many Cells.
For example lists of all Groups, Projects, Topics, Jobs, Analytics, Applications and more.
There are also administrative monitoring capabilities in the Admin Area that will span many Cells such as the "Background Jobs" and "Background Migrations" pages.
## 2. Data flow
## 3. Proposal
We will need to decide how to handle these exceptions with a few possible
options:
1. Move all these pages out into a dedicated per-Cell admin section. Probably
the URL will need to be routable to a single Cell like `/cells/<cell_id>/admin`,
then we can display these data per Cell. These pages will be distinct from
other Admin Area pages which control settings that are shared across all Cells. We
will also need to consider how this impacts self-managed customers and
whether, or not, this should be visible for single-Cell instances of GitLab.
1. Build some aggregation interfaces for this data so that it can be fetched
from all Cells and presented in a single UI. This may be beneficial to an
administrator that needs to see and filter all data at a glance, especially
when they don't know which Cell the data is on. The downside, however, is
that building this kind of aggregation is very tricky when all Cells are
designed to be totally independent, and it does also enforce stricter
requirements on compatibility between Cells.
The following overview describes at what level each feature contained in the current Admin Area will be managed:
| Feature | Cluster | Cell | Organization |
| --- | --- | --- | --- |
| Abuse reports | | | |
| Analytics | | | |
| Applications | | | |
| Deploy keys | | | |
| Labels | | | |
| Messages | ✓ | | |
| Monitoring | | ✓ | |
| Subscription | | | |
| System hooks | | | |
| Overview | | | |
| Settings - General | ✓ | | |
| Settings - Integrations | ✓ | | |
| Settings - Repository | ✓ | | |
| Settings - CI/CD (1) | ✓ | ✓ | |
| Settings - Reporting | ✓ | | |
| Settings - Metrics | ✓ | | |
| Settings - Service usage data | | ✓ | |
| Settings - Network | ✓ | | |
| Settings - Appearance | ✓ | | |
| Settings - Preferences | ✓ | | |
(1) Depending on the specific setting, some will be managed at the cluster-level, and some at the Cell-level.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,30 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Agent for Kubernetes'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Agent for Kubernetes
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,53 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Backups'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Backups
Each Cell will take its own backups, and consequently have its own isolated backup/restore procedure.
## 1. Definition
GitLab backup takes a backup of the PostgreSQL database used by the application, and also Git repository data.
## 2. Data flow
Each Cell has a number of application databases to back up (for example, `main`, and `ci`).
Additionally, there may be cluster-wide metadata tables (for example, `users` table) which is directly accessible via PostgreSQL.
## 3. Proposal
### 3.1. Cluster-wide metadata
It is currently unknown how cluster-wide metadata tables will be accessible.
We may choose to have cluster-wide metadata tables backed up separately, or have each Cell back up its copy of cluster-wide metadata tables.
### 3.2 Consistency
#### 3.2.1 Take backups independently
As each Cell will communicate with each other via API, and there will be no joins to the `users` table, it should be acceptable for each Cell to take a backup independently of each other.
#### 3.2.2 Enforce snapshots
We can require that each Cell take a snapshot for the PostgreSQL databases at around the same time to allow for a consistent enough backup.
## 4. Evaluation
As the number of Cells increases, it will likely not be feasible to take a snapshot at the same time for all Cells.
Hence taking backups independently is the better option.
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,144 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: CI Runners'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: CI Runners
GitLab executes CI jobs via [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner/), very often managed by customers in their infrastructure.
All CI jobs created as part of the CI pipeline are run in the context of a Project.
This poses a challenge how to manage GitLab Runners.
## 1. Definition
There are 3 different types of runners:
- Instance-wide: Runners that are registered globally with specific tags (selection criteria)
- Group runners: Runners that execute jobs from a given top-level Group or Projects in that Group
- Project runners: Runners that execute jobs from one Projects or many Projects: some runners might
have Projects assigned from Projects in different top-level Groups.
This, alongside with the existing data structure where `ci_runners` is a table describing all types of runners, poses a challenge as to how the `ci_runners` should be managed in a Cells environment.
## 2. Data flow
GitLab runners use a set of globally scoped endpoints to:
- Register a new runner via registration token `https://gitlab.com/api/v4/runners`
([subject for removal](../../runner_tokens/index.md)) (`registration token`)
- Create a new runner in the context of a user `https://gitlab.com/api/v4/user/runners` (`runner token`)
- Request jobs via an authenticated `https://gitlab.com/api/v4/jobs/request` endpoint (`runner token`)
- Upload job status via `https://gitlab.com/api/v4/jobs/:job_id` (`build token`)
- Upload trace via `https://gitlab.com/api/v4/jobs/:job_id/trace` (`build token`)
- Download and upload artifacts via `https://gitlab.com/api/v4/jobs/:job_id/artifacts` (`build token`)
Currently three types of authentication tokens are used:
- Runner registration token ([subject for removal](../../runner_tokens/index.md))
- Runner token representing a registered runner in a system with specific configuration (`tags`, `locked`, etc.)
- Build token representing an ephemeral token giving limited access to updating a specific job, uploading artifacts, downloading dependent artifacts, downloading and uploading container registry images
Each of those endpoints receive an authentication token via header (`JOB-TOKEN` for `/trace`) or body parameter (`token` all other endpoints).
Since the CI pipeline would be created in the context of a specific Cell, it would be required that pick of a build would have to be processed by that particular Cell.
This requires that build picking depending on a solution would have to be either:
- Routed to the correct Cell for the first time
- Be two-phased: Request build from global pool, claim build on a specific Cell using a Cell specific URL
## 3. Proposal
### 3.1. Authentication tokens
Even though the paths for CI runners are not routable, they can be made routable with these two possible solutions:
- The `https://gitlab.com/api/v4/jobs/request` uses a long polling mechanism with
a ticketing mechanism (based on `X-GitLab-Last-Update` header). When the runner first
starts, it sends a request to GitLab to which GitLab responds with either a build to pick
by runner. This value is completely controlled by GitLab. This allows GitLab
to use JWT or any other means to encode a `cell` identifier that could be easily
decodable by Router.
- The majority of communication (in terms of volume) is using `build token`, making it
the easiest target to change since GitLab is the sole owner of the token that the runner later
uses for a specific job. There were prior discussions about not storing the `build token`
but rather using a `JWT` token with defined scopes. Such a token could encode the `cell`
to which the Router could route all requests.
### 3.2. Request body
- The most used endpoints pass the authentication token in the request body. It might be desired
to use HTTP headers as an easier way to access this information by Router without
a need to proxy requests.
### 3.3. Instance-wide are Cell-local
We can pick a design where all runners are always registered and local to a given Cell:
- Each Cell has its own set of instance-wide runners that are updated at its own pace
- The Project runners can only be linked to Projects from the same Organization, creating strong isolation.
- In this model the `ci_runners` table is local to the Cell.
- In this model we would require the above endpoints to be scoped to a Cell in some way, or be made routable. It might be via prefixing them, adding additional Cell parameters, or providing much more robust ways to decode runner tokens and match it to a Cell.
- If a routable token is used, we could move away from cryptographic random stored in database to rather prefer to use JWT tokens.
- The Admin Area showing registered runners would have to be scoped to a Cell.
This model might be desired because it provides strong isolation guarantees.
This model does significantly increase maintenance overhead because each Cell is managed separately.
This model may require adjustments to the runner tags feature so that Projects have a consistent runner experience across Cells.
### 3.4. Instance-wide are cluster-wide
Contrary to the proposal where all runners are Cell-local, we can consider that runners
are global, or just instance-wide runners are global.
However, this requires significant overhaul of the system and we would have to change the following aspects:
- The `ci_runners` table would likely have to be decomposed into `ci_instance_runners`, ...
- All interfaces would have to be adopted to use the correct table.
- Build queuing would have to be reworked to be two-phased where each Cell would know of all pending and running builds, but the actual claim of a build would happen against a Cell containing data.
- It is likely that `ci_pending_builds` and `ci_running_builds` would have to be made `cluster-wide` tables, increasing the likelihood of creating hotspots in a system related to CI queueing.
This model is complex to implement from an engineering perspective.
Some data are shared between Cells.
It creates hotspots/scalability issues in a system that might impact the experience of Organizations on other Cells, for instance during abuse.
### 3.5. GitLab CI Daemon
Another potential solution to explore is to have a dedicated service responsible for builds queueing, owning its database and working in a model of either sharded or Cell-ed service.
There were prior discussions about [CI/CD Daemon](https://gitlab.com/gitlab-org/gitlab/-/issues/19435).
If the service is sharded:
- Depending on the model, if runners are cluster-wide or Cell-local, this service would have to fetch data from all Cells.
- If the sharded service is used we could adapt a model of sharing a database containing `ci_pending_builds/ci_running_builds` with the service.
- If the sharded service is used we could consider a push model where each Cell pushes to CI/CD Daemon builds that should be picked by runner.
- The sharded service would be aware which Cell is responsible for processing the given build and could route processing requests to the designated Cell.
If the service is Cell-ed:
- All expectations of routable endpoints are still valid.
In general usage of CI Daemon does not help significantly with the stated problem.
However, this offers a few upsides related to more efficient processing and decoupling model: push model and it opens a way to offer stateful communication with GitLab runners (ex. gRPC or Websockets).
## 4. Evaluation
Considering all options it appears that the most promising solution is to:
- Use [Instance-wide are Cell-local](#33-instance-wide-are-cell-local)
- Refine endpoints to have routable identities (either via specific paths, or better tokens)
Another potential upside is to get rid of `ci_builds.token` and rather use a `JWT token` that can much better and easier encode a wider set of scopes allowed by CI runner.
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,114 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Container Registry'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Container Registry
GitLab [Container Registry](../../../../user/packages/container_registry/index.md) is a feature allowing to store Docker container images in GitLab.
## 1. Definition
GitLab Container Registry is a complex service requiring usage of PostgreSQL, Redis and Object Storage dependencies.
Right now there's undergoing work to introduce [Container Registry Metadata](../../container_registry_metadata_database/index.md) to optimize data storage and image retention policies of Container Registry.
GitLab Container Registry is serving as a container for stored data, but on its own does not authenticate `docker login`.
The `docker login` is executed with user credentials (can be `personal access token`) or CI build credentials (ephemeral `ci_builds.token`).
Container Registry uses data deduplication.
It means that the same blob (image layer) that is shared between many Projects is stored only once.
Each layer is hashed by `sha256`.
The `docker login` does request a JWT time-limited authentication token that is signed by GitLab, but validated by Container Registry service.
The JWT token does store all authorized scopes (`container repository images`) and operation types (`push` or `pull`).
A single JWT authentication token can have many authorized scopes.
This allows Container Registry and client to mount existing blobs from other scopes.
GitLab responds only with authorized scopes.
Then it is up to GitLab Container Registry to validate if the given operation can be performed.
The GitLab.com pages are always scoped to a Project.
Each Project can have many container registry images attached.
Currently, on GitLab.com the actual registry service is served via `https://registry.gitlab.com`.
The main identifiable problems are:
- The authentication request (`https://gitlab.com/jwt/auth`) that is processed by GitLab.com.
- The `https://registry.gitlab.com` that is run by an external service and uses its own data store.
- Data deduplication. The Cells architecture with registry run in a Cell would reduce efficiency of data storage.
## 2. Data flow
### 2.1. Authorization request that is send by `docker login`
```shell
curl \
--user "username:password" \
"https://gitlab/jwt/auth?client_id=docker&offline_token=true&service=container_registry&scope=repository:gitlab-org/gitlab-build-images:push,pull"
```
Result is encoded and signed JWT token. Second base64 encoded string (split by `.`) contains JSON with authorized scopes.
```json
{"auth_type":"none","access":[{"type":"repository","name":"gitlab-org/gitlab-build-images","actions":["pull"]}],"jti":"61ca2459-091c-4496-a3cf-01bac51d4dc8","aud":"container_registry","iss":"omnibus-gitlab-issuer","iat":1669309469,"nbf":166}
```
### 2.2. Docker client fetching tags
```shell
curl \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer token" \
https://registry.gitlab.com/v2/gitlab-org/gitlab-build-images/tags/list
curl \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer token" \
https://registry.gitlab.com/v2/gitlab-org/gitlab-build-images/manifests/danger-ruby-2.6.6
```
### 2.3. Docker client fetching blobs and manifests
```shell
curl \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer token" \
https://registry.gitlab.com/v2/gitlab-org/gitlab-build-images/blobs/sha256:a3f2e1afa377d20897e08a85cae089393daa0ec019feab3851d592248674b416
```
## 3. Proposal
### 3.1. Shard Container Registry separately to Cells architecture
Due to its extensive and in general highly scalable horizontal architecture it should be evaluated if the GitLab Container Registry should be run not in Cell, but in a Cluster and be scaled independently.
This might be easier, but would definitely not offer the same amount of data isolation.
### 3.2. Run Container Registry within a Cell
It appears that except `/jwt/auth` which would likely have to be processed by Router (to decode `scope`) the Container Registry could be run as a local service of a Cell.
The actual data at least in case of GitLab.com is not forwarded via registry, but rather served directly from Object Storage / CDN.
Its design encodes container repository image in a URL that is easily routable.
It appears that we could re-use the same stateless Router service in front of Container Registry to serve manifests and blobs redirect.
The only downside is increased complexity of managing standalone registry for each Cell, but this might be desired approach.
## 4. Evaluation
There do not seem to be any theoretical problems with running GitLab Container Registry in a Cell.
It seems that the service can be easily made routable to work well.
The practical complexities are around managing a complex service from an infrastructure side.
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,106 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Contributions: Forks'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Contributions: Forks
The [Forking workflow](../../../../user/project/repository/forking_workflow.md) allows users to copy existing Project sources into their own namespace of choice (Personal or Group).
## 1. Definition
The [Forking workflow](../../../../user/project/repository/forking_workflow.md) is a common workflow with various usage patterns:
- It allows users to contribute back to upstream Project.
- It persists repositories into their Personal Namespace.
- Users can copy to make changes and release as modified Project.
Forks allow users not having write access to a parent Project to make changes.
The forking workflow is especially important for the open source community to contribute back to public Projects.
However, it is equally important in some companies that prefer a strong split of responsibilities and tighter access control.
The access to a Project is restricted to a designated list of developers.
Forks enable:
- Tighter control of who can modify the upstream Project.
- Split of responsibilities: Parent Project might use CI configuration connecting to production systems.
- To run CI pipelines in the context of a fork in a much more restrictive environment.
- To consider all forks to be unvetted which reduces risks of leaking secrets, or any other information tied to the Project.
The forking model is problematic in a Cells architecture for the following reasons:
- Forks are clones of existing repositories. Forks could be created across different Organizations, Cells and Gitaly shards.
- Users can create merge requests and contribute back to an upstream Project. This upstream Project might in a different Organization and Cell.
- The merge request CI pipeline is executed in the context of the source Project, but presented in the context of the target Project.
## 2. Data flow
## 3. Proposals
### 3.1. Intra-Cluster forks
This proposal implements forks as intra-Cluster forks where communication is done via API between all trusted Cells of a cluster:
- Forks are created always in the context of a user's choice of Group.
- Forks are isolated from the Organization.
- Organization or Group owner could disable forking across Organizations, or forking in general.
- A merge request is created in the context of the target Project, referencing the external Project on another Cell.
- To target Project the merge reference is transferred that is used for presenting information in context of the target Project.
- CI pipeline is fetched in the context of the source Project as it is today, the result is fetched into the merge request of the target Project.
- The Cell holding the target Project internally uses GraphQL to fetch the status of the source Project and includes in context of the information for merge request.
Upsides:
- All existing forks continue to work as they are, as they are treated as intra-Cluster forks.
Downsides:
- The purpose of Organizations is to provide strong isolation between Organizations. Allowing to fork across does break security boundaries.
- However, this is no different to the ability of users today to clone a repository to a local computer and push it to any repository of choice.
- Access control of source Project can be lower than those of target Project. Today, the system requires that in order to contribute back, the access level needs to be the same for fork and upstream.
### 3.2. Forks are created in a Personal Namespace of the current Organization
Instead of creating Projects across Organizations, forks are created in a user's Personal Namespace tied to the Organization. Example:
- Each user that is part of an Organization receives their Personal Namespace. For example for `GitLab Inc.` it could be `gitlab.com/organization/gitlab-inc/@ayufan`.
- The user has to fork into their own Personal Namespace of the Organization.
- The user has as many Personal Namespaces as Organizations they belongs to.
- The Personal Namespace behaves similar to the currently offered Personal Namespace.
- The user can manage and create Projects within a Personal Namespace.
- The Organization can prevent or disable usage of Personal Namespaces, disallowing forks.
- All current forks are migrated into the Personal Namespace of user in an Organization.
- All forks are part of the Organization.
- Forks are not federated features.
- The Personal Namespace and forked Project do not share configuration with the parent Project.
### 3.3. Forks are created as internal Projects under current Projects
Instead of creating Projects across Organizations, forks are attachments to existing Projects.
Each user forking a Project receives their unique Project. Example:
- For Project: `gitlab.com/gitlab-org/gitlab`, forks would be created in `gitlab.com/gitlab-org/gitlab/@kamil-gitlab`.
- Forks are created in the context of the current Organization, they do not cross Organization boundaries and are managed by the Organization.
- Tied to the user (or any other user-provided name of the fork).
- Forks are not federated features.
Downsides:
- Does not answer how to handle and migrate all existing forks.
- Might share current Group/Project settings, which could be breaking some security boundaries.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,99 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Data migration'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Data migration
It is essential for a Cells architecture to provide a way to migrate data out of big Cells into smaller ones.
This document describes various approaches to provide this type of split.
We also need to handle cases where data is already violating the expected isolation constraints of Cells, for example references cannot span multiple Organizations.
We know that existing features like linked issues allowed users to link issues across any Projects regardless of their hierarchy.
There are many similar features.
All of this data will need to be migrated in some way before it can be split across different Cells.
This may mean some data needs to be deleted, or the feature needs to be changed and modelled slightly differently before we can properly split or migrate Organizations between Cells.
Having schema deviations across different Cells, which is a necessary consequence of different databases, will also impact our ability to migrate data between Cells.
Different schemas impact our ability to reliably replicate data across Cells and especially impact our ability to validate that the data is correctly replicated.
It might force us to only be able to move data between Cells when the schemas are all in sync (slowing down deployments and the rebalancing process) or possibly only migrate from newer to older schemas which would be complex.
## 1. Definition
## 2. Data flow
## 3. Proposal
### 3.1. Split large Cells
A single Cell can only be divided into many Cells.
This is based on the principle that it is easier to create an exact clone of an existing Cell in many replicas out of which some will be made authoritative once migrated.
Keeping those replicas up-to-date with Cell 0 is also much easier due to pre-existing replication solutions that can replicate the whole systems: Geo, PostgreSQL physical replication, etc.
1. All data of an Organization needs to not be divided across many Cells.
1. Split should be doable online.
1. New Cells cannot contain pre-existing data.
1. N Cells contain exact replica of Cell 0.
1. The data of Cell 0 is live replicated to as many Cells it needs to be split.
1. Once consensus is achieved between Cell 0 and N-Cells, the Organizations to be migrated away are marked as read-only cluster-wide.
1. The `routes` is updated on for all Organizations to be split to indicate an authoritative Cell holding the most recent data, like `gitlab-org` on `cell-100`.
1. The data for `gitlab-org` on Cell 0, and on other non-authoritative N-Cells are dormant and will be removed in the future.
1. All accesses to `gitlab-org` on a given Cell are validated about `cell_id` of `routes` to ensure that given Cell is authoritative to handle the data.
#### More challenges of this proposal
1. There is no streaming replication capability for Elasticsearch, but you could
snapshot the whole Elasticsearch index and recreate, but this takes hours.
It could be handled by pausing Elasticsearch indexing on the initial Cell during
the migration as indexing downtime is not a big issue, but this still needs
to be coordinated with the migration process.
1. Syncing Redis, Gitaly, CI Postgres, Main Postgres, registry Postgres, other
new data stores snapshots in an online system would likely lead to gaps
without a long downtime. You need to choose a sync point and at the sync
point you need to stop writes to perform the migration. The more data stores
there are to migrate at the same time the longer the write downtime for the
failover. We would also need to find a reliable place in the application to
actually block updates to all these systems with a high degree of
confidence. In the past we've only been confident by shutting down all Rails
services because any Rails process could write directly to any of these at
any time due to async workloads or other surprising code paths.
1. How to efficiently delete all the orphaned data. Locating all `ci_builds`
associated with half the Organizations would be very expensive if we have to
do joins. We haven't yet determined if we'd want to store an `organization_id`
column on every table, but this is the kind of thing it would be helpful for.
### 3.2. Migrate Organization from an existing Cell
This is different to split, as we intend to perform logical and selective replication of data belonging to a single Organization.
Today this type of selective replication is only implemented by Gitaly where we can migrate Git repository from a single Gitaly node to another with minimal downtime.
In this model we would require identifying all resources belonging to a given Organization: database rows, object storage files, Git repositories, etc. and selectively copy them over to another (likely) existing Cell importing data into it.
Ideally ensuring that we can perform logical replication live of all changed data, but change similarly to split which Cell is authoritative for this Organization.
1. It is hard to identify all resources belonging to an Organization.
1. It requires either downtime for the Organization or a robust system to identify live changes made.
1. It likely will require a full database structure analysis (more robust than Project import/export) to perform selective PostgreSQL logical replication.
#### More challenges of this proposal
1. Logical replication is still not performant enough to keep up with our
scale. Even if we could use logical replication we still don't have an
efficient way to filter data related to a single Organization without
joining all the way to the `organizations` table which will slow down
logical replication dramatically.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,74 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Database Sequences'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Database Sequences
GitLab today ensures that every database row create has a unique ID, allowing to access a merge request, CI Job or Project by a known global ID.
Cells will use many distinct and not connected databases, each of them having a separate ID for most entities.
At a minimum, any ID referenced between a Cell and the shared schema will need to be unique across the cluster to avoid ambiguous references.
Further to required global IDs, it might also be desirable to retain globally unique IDs for all database rows to allow migrating resources between Cells in the future.
## 1. Definition
## 2. Data flow
## 3. Proposal
These are some preliminary ideas how we can retain unique IDs across the system.
### 3.1. UUID
Instead of using incremental sequences, use UUID (128 bit) that is stored in the database.
- This might break existing IDs and requires adding a UUID column for all existing tables.
- This makes all indexes larger as it requires storing 128 bit instead of 32/64 bit in index.
### 3.2. Use Cell index encoded in ID
Because a significant number of tables already use 64 bit ID numbers we could use MSB to encode the Cell ID:
- This might limit the amount of Cells that can be enabled in a system, as we might decide to only allocate 1024 possible Cell numbers.
- This would make it possible to migrate IDs between Cells, because even if an entity from Cell 1 is migrated to Cell 100 this ID would still be unique.
- If resources are migrated the ID itself will not be enough to decode the Cell number and we would need a lookup table.
- This requires updating all IDs to 32 bits.
### 3.3. Allocate sequence ranges from central place
Each Cell might receive its own range of sequences as they are consumed from a centrally managed place.
Once a Cell consumes all IDs assigned for a given table it would be replenished and a next range would be allocated.
Ranges would be tracked to provide a faster lookup table if a random access pattern is required.
- This might make IDs migratable between Cells, because even if an entity from Cell 1 is migrated to Cell 100 this ID would still be unique.
- If resources are migrated the ID itself will not be enough to decode the Cell number and we would need a much more robust lookup table as we could be breaking previously assigned sequence ranges.
- This does not require updating all IDs to 64 bits.
- This adds some performance penalty to all `INSERT` statements in Postgres or at least from Rails as we need to check for the sequence number and potentially wait for our range to be refreshed from the ID server.
- The available range will need to be stored and incremented in a centralized place so that concurrent transactions cannot possibly get the same value.
### 3.4. Define only some tables to require unique IDs
Maybe it is acceptable only for some tables to have a globally unique IDs. It could be Projects, Groups and other top-level entities.
All other tables like `merge_requests` would only offer a Cell-local ID, but when referenced outside it would rather use an IID (an ID that is monotonic in context of a given resource, like a Project).
- This makes the ID 10000 for `merge_requests` be present on all Cells, which might be sometimes confusing regarding the uniqueness of the resource.
- This might make random access by ID (if ever needed) impossible without using a composite key, like: `project_id+merge_request_id`.
- This would require us to implement a transformation/generation of new ID if we need to migrate records to another Cell. This can lead to very difficult migration processes when these IDs are also used as foreign keys for other records being migrated.
- If IDs need to change when moving between Cells this means that any links to records by ID would no longer work even if those links included the `project_id`.
- If we plan to allow these IDs to not be unique and change the unique constraint to be based on a composite key then we'd need to update all foreign key references to be based on the composite key.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,71 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Explore'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Explore
Explore may not play a critical role in GitLab as it functions today, but GitLab today is not isolated. It is the isolation that makes Explore or some viable replacement necessary.
The existing Group and Project Explore will initially be scoped to an Organization. However, there is a need for a global Explore that spans across Organizations to support the discoverability of public Groups and Projects, in particular in the context of discovering open source Projects. See user feedback [here](https://gitlab.com/gitlab-org/gitlab/-/issues/21582#note_1458298192) and [here](https://gitlab.com/gitlab-org/gitlab/-/issues/418228#note_1470045468).
## 1. Definition
The Explore functionality helps users in discovering Groups and Projects. Unauthenticated Users are only able to explore public Groups and Projects, authenticated Users can see all the Groups and Projects that they have access to, including private and internal Groups and Projects.
## 2. Data flow
## 3. Proposal
The Explore feature problem falls under the broader umbrella of solving inter-Cell communication. [This topic warrants deeper research](../index.md#can-different-cells-communicate-with-each-other).
Below are possible directions for further investigation.
### 3.1. Read only table mirror
- Create a `shared_projects` table in the shared cluster-wide database.
- The model for this table is read-only. No inserts/updates/deletes are allowed.
- The table is filled with data (or a subset of data) from the Projects Cell-local table.
- The write model Project (which is Cell-local) writes to the local database. We will primarily use this model for anything Cell-local.
- This data is synchronized with `shared_projects` via a background job any time something changes.
- The data in `shared_projects` is stored normalized, so that all the information necessary to display the Project Explore is there.
- The Project Explore (as of today) is part of an instance-wide functionality, since it's not namespaced to any organizations/groups.
- This section will read data using the read model for `shared_projects`.
- Once the user clicks on a Project, they are redirected to the Cell containing the Organization.
Downsides:
- Need to have an explicit pattern to access instance-wide data. This however may be useful for admin functionalities too.
- The Project Explore may not be as rich in features as it is today (various filtering options, role you have on that Project, etc.).
- Extra complexity in managing CQRS.
### 3.2 Explore scoped to an Organization
The Project Explore and Group Explore are scoped to an Organization.
Downsides:
- No global discoverability of Groups and Projects.
## 4. Evaluation
The existing Group and Project Explore will initially be scoped to an Organization. Considering the [current usage of the Explore feature](https://gitlab.com/gitlab-data/product-analytics/-/issues/1302#note_1491215521), we deem this acceptable. Since all existing Users, Groups and Projects will initially be part of the default Organization, Groups and Projects will remain explorable and accessible as they are today. Only once existing Groups and Projects are moved out of the default Organization into different Organizations will this become a noticeable problem. Solutions to mitigate this are discussed in [issue #418228](https://gitlab.com/gitlab-org/gitlab/-/issues/418228). Ultimately, Explore could be replaced with a better search experience altogether.
## 4.1. Pros
- Initially the lack of discoverability will not be a problem.
- Only around [1.5% of all exisiting Users are using the Explore functionality on a monthly basis](https://gitlab.com/gitlab-data/product-analytics/-/issues/1302#note_1491215521).
## 4.2. Cons
- The GitLab owned top-level Groups would be some of the first to be moved into their own Organization and thus be detached from the explorability of the default Organization.

View File

@ -0,0 +1,156 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Git Access'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Git Access
This document describes impact of Cells architecture on all Git access (over HTTPS and SSH) patterns providing explanation of how potentially those features should be changed to work well with Cells.
## 1. Definition
Git access is done throughout the application.
It can be an operation performed by the system (read Git repository) or by a user (create a new file via Web IDE, `git clone` or `git push` via command line).
The Cells architecture defines that all Git repositories will be local to the Cell, so no repository could be shared with another Cell.
The Cells architecture will require that any Git operation can only be handled by a Cell holding the data.
It means that any operation either via Web interface, API, or GraphQL needs to be routed to the correct Cell.
It means that any `git clone` or `git push` operation can only be performed in the context of a Cell.
## 2. Data flow
The are various operations performed today by GitLab on a Git repository.
This describes the data flow how they behave today to better represent the impact.
It appears that Git access does require changes only to a few endpoints that are scoped to a Project.
There appear to be different types of repositories:
- Project: assigned to Group
- Wiki: additional repository assigned to Project
- Design: similar to Wiki, additional repository assigned to Project
- Snippet: creates a virtual Project to hold repository, likely tied to the User
### 2.1. Git clone over HTTPS
Execution of: `git clone` over HTTPS
```mermaid
sequenceDiagram
User ->> Workhorse: GET /gitlab-org/gitlab.git/info/refs?service=git-upload-pack
Workhorse ->> Rails: GET /gitlab-org/gitlab.git/info/refs?service=git-upload-pack
Rails ->> Workhorse: 200 OK
Workhorse ->> Gitaly: RPC InfoRefsUploadPack
Gitaly ->> User: Response
User ->> Workhorse: POST /gitlab-org/gitlab.git/git-upload-pack
Workhorse ->> Gitaly: RPC PostUploadPackWithSidechannel
Gitaly ->> User: Response
```
### 2.2. Git clone over SSH
Execution of: `git clone` over SSH
```mermaid
sequenceDiagram
User ->> Git SSHD: ssh git@gitlab.com
Git SSHD ->> Rails: GET /api/v4/internal/authorized_keys
Rails ->> Git SSHD: 200 OK (list of accepted SSH keys)
Git SSHD ->> User: Accept SSH
User ->> Git SSHD: git clone over SSH
Git SSHD ->> Rails: POST /api/v4/internal/allowed?project=/gitlab-org/gitlab.git&service=git-upload-pack
Rails ->> Git SSHD: 200 OK
Git SSHD ->> Gitaly: RPC SSHUploadPackWithSidechannel
Gitaly ->> User: Response
```
### 2.3. Git push over HTTPS
Execution of: `git push` over HTTPS
```mermaid
sequenceDiagram
User ->> Workhorse: GET /gitlab-org/gitlab.git/info/refs?service=git-receive-pack
Workhorse ->> Rails: GET /gitlab-org/gitlab.git/info/refs?service=git-receive-pack
Rails ->> Workhorse: 200 OK
Workhorse ->> Gitaly: RPC PostReceivePack
Gitaly ->> Rails: POST /api/v4/internal/allowed?gl_repository=project-111&service=git-receive-pack
Gitaly ->> Rails: POST /api/v4/internal/pre_receive?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/post_receive?gl_repository=project-111
Gitaly ->> User: Response
```
### 2.4. Git push over SSHD
Execution of: `git clone` over SSH
```mermaid
sequenceDiagram
User ->> Git SSHD: ssh git@gitlab.com
Git SSHD ->> Rails: GET /api/v4/internal/authorized_keys
Rails ->> Git SSHD: 200 OK (list of accepted SSH keys)
Git SSHD ->> User: Accept SSH
User ->> Git SSHD: git clone over SSH
Git SSHD ->> Rails: POST /api/v4/internal/allowed?project=/gitlab-org/gitlab.git&service=git-receive-pack
Rails ->> Git SSHD: 200 OK
Git SSHD ->> Gitaly: RPC ReceivePack
Gitaly ->> Rails: POST /api/v4/internal/allowed?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/pre_receive?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/post_receive?gl_repository=project-111
Gitaly ->> User: Response
```
### 2.5. Create commit via Web
Execution of `Add CHANGELOG` to repository:
```mermaid
sequenceDiagram
Web ->> Puma: POST /gitlab-org/gitlab/-/create/main
Puma ->> Gitaly: RPC TreeEntry
Gitaly ->> Rails: POST /api/v4/internal/allowed?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/pre_receive?gl_repository=project-111
Gitaly ->> Rails: POST /api/v4/internal/post_receive?gl_repository=project-111
Gitaly ->> Puma: Response
Puma ->> Web: See CHANGELOG
```
## 3. Proposal
The Cells stateless router proposal requires that any ambiguous path (that is not routable) will be made routable.
It means that at least the following paths will have to be updated to introduce a routable entity (Project, Group, or Organization).
Change:
- `/api/v4/internal/allowed` => `/api/v4/internal/projects/<gl_repository>/allowed`
- `/api/v4/internal/pre_receive` => `/api/v4/internal/projects/<gl_repository>/pre_receive`
- `/api/v4/internal/post_receive` => `/api/v4/internal/projects/<gl_repository>/post_receive`
- `/api/v4/internal/lfs_authenticate` => `/api/v4/internal/projects/<gl_repository>/lfs_authenticate`
Where:
- `gl_repository` can be `project-1111` (`Gitlab::GlRepository`)
- `gl_repository` in some cases might be a full path to repository as executed by GitLab Shell (`/gitlab-org/gitlab.git`)
## 4. Evaluation
Supporting Git repositories if a Cell can access only its own repositories does not appear to be complex.
The one major complication is supporting snippets, but this likely falls in the same category as for the approach to support a user's Personal Namespace.
## 4.1. Pros
1. The API used for supporting HTTPS/SSH and Hooks are well defined and can easily be made routable.
## 4.2. Cons
1. The sharing of repositories objects is limited to the given Cell and Gitaly node.
1. Cross-Cells forks are likely impossible to be supported (discover: How this works today across different Gitaly node).

View File

@ -0,0 +1,30 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: GitLab Pages'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: GitLab Pages
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,35 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Global search'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Global search
When we introduce multiple Cells we intend to isolate all services related to those Cells.
This will include Elasticsearch which means our current global search functionality will not work.
It may be possible to implement aggregated search across all Cells, but it is unlikely to be performant to do fan-out searches across all Cells especially once you start to do pagination which requires setting the correct offset and page number for each search.
## 1. Definition
## 2. Data flow
## 3. Proposal
Likely the first versions of Cells will not support global searches.
Later, we may consider if building global searches to support popular use cases is worthwhile.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,81 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: GraphQL'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: GraphQL
GitLab extensively uses GraphQL to perform efficient data query operations.
GraphQL due to it's nature is not directly routable.
The way GitLab uses it calls the `/api/graphql` endpoint, and only the query or mutation of the body request might define where the data can be accessed.
## 1. Definition
## 2. Data flow
## 3. Proposal
There are at least two main ways to implement GraphQL in a Cells architecture.
### 3.1. GraphQL routable by endpoint
Change `/api/graphql` to `/api/organization/<organization>/graphql`.
- This breaks all existing usages of `/api/graphql` endpoint because the API URI is changed.
### 3.2. GraphQL routable by body
As part of router parse GraphQL body to find a routable entity, like `project`.
- This still makes the GraphQL query be executed only in context of a given Cell and not allowing the data to be merged.
```json
# Good example
{
project(fullPath:"gitlab-org/gitlab") {
id
description
}
}
# Bad example, since Merge Request is not routable
{
mergeRequest(id: 1111) {
iid
description
}
}
```
### 3.3. Merging GraphQL Proxy
Implement as part of router GraphQL Proxy which can parse body and merge results from many Cells.
- This might make pagination hard to achieve, or we might assume that we execute many queries of which results are merged across all Cells.
```json
{
project(fullPath:"gitlab-org/gitlab"){
id, description
}
group(fullPath:"gitlab-com") {
id, description
}
}
```
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,36 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Organizations'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Organizations
One of the major designs of a Cells architecture is strong isolation between Groups.
Organizations as described by the [Organization blueprint](../../organization/index.md) provides a way to have plausible UX for joining together many Groups that are isolated from the rest of the system.
## 1. Definition
Cells do require that all Groups and Projects of a single Organization can only be stored on a single Cell because a Cell can only access data that it holds locally and has very limited capabilities to read information from other Cells.
Cells with Organizations do require strong isolation between Organizations.
It will have significant implications on various user-facing features, like Todos, dropdowns allowing to select Projects, references to other issues or Projects, or any other social functions present at GitLab.
Today those functions were able to reference anything in the whole system.
With the introduction of Organizations this will be forbidden.
This problem definition aims to answer effort and implications required to add strong isolation between Organizations to the system, including features affected and their data processing flow.
The purpose is to ensure that our solution when implemented consistently avoids data leakage between Organizations residing on a single Cell.
## 2. Proposal
See the [Organization blueprint](../../organization/index.md).

View File

@ -0,0 +1,31 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Personal Access Tokens'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Personal Access Tokens
## 1. Definition
Personal Access Tokens associated with a User are a way for Users to interact with the API of GitLab to perform operations.
Personal Access Tokens today are scoped to the User, and can access all Groups that a User has access to.
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,30 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Personal Namespaces'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Personal Namespaces
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,34 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Router Endpoints Classification'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Router Endpoints Classification
Classification of all endpoints is essential to properly route requests hitting the load balancer of a GitLab installation to a Cell that can serve it.
Each Cell should be able to decode each request and classify which Cell it belongs to.
GitLab currently implements hundreds of endpoints.
This document tries to describe various techniques that can be implemented to allow the Rails to provide this information efficiently.
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,38 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Schema changes'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Schema changes
When we introduce multiple Cells that own their own databases this will complicate the process of making schema changes to Postgres and Elasticsearch.
Today we already need to be careful to make changes comply with our zero downtime deployments.
For example, [when removing a column we need to make changes over 3 separate deployments](../../../../development/database/avoiding_downtime_in_migrations.md#dropping-columns).
We have tooling like `post_migrate` that helps with these kinds of changes to reduce the number of merge requests needed, but these will be complicated when we are dealing with deploying multiple Rails applications that will be at different versions at any one time.
This problem will be particularly tricky to solve for shared databases like our plan to share the `users` related tables among all Cells.
A key benefit of Cells may be that it allows us to run different customers on different versions of GitLab.
We may choose to update our own Cell before all our customers giving us even more flexibility than our current canary architecture.
But doing this means that schema changes need to have even more versions of backward compatibility support which could slow down development as we need extra steps to make schema changes.
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,43 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Secrets'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Secrets
Where possible, each Cell should have its own distinct set of secrets.
However, there will be some secrets that will be required to be the same for all Cells in the cluster.
## 1. Definition
GitLab has a lot of [secrets](https://docs.gitlab.com/charts/installation/secrets.html) that need to be configured.
Some secrets are for inter-component communication, for example, `GitLab Shell secret`, and used only within a Cell.
Some secrets are used for features, for example, `ci_jwt_signing_key`.
## 2. Data flow
## 3. Proposal
1. Secrets used for features will need to be consistent across all Cells, so that the UX is consistent.
1. This is especially true for the `db_key_base` secret which is used for
encrypting data at rest in the database - so that Projects that are
transferred to another Cell will continue to work. We do not want to have
to re-encrypt such rows when we move Projects/Groups between Cells.
1. Secrets which are used for intra-Cell communication only should be uniquely generated
per Cell.
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,56 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Snippets'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Snippets
Snippets will be scoped to an Organization. Initially it will not be possible to aggregate snippet collections across Organizations. See also [issue #416954](https://gitlab.com/gitlab-org/gitlab/-/issues/416954).
## 1. Definition
Two different types of snippets exist:
- [Project snippets](../../../../api/project_snippets.md). These snippets have URLs
like `/<group>/<project>/-/snippets/123`
- [Personal snippets](../../../../user/snippets.md). These snippets have URLs like
`/-/snippets/123`
Snippets are backed by a Git repository.
## 2. Data flow
## 3. Proposal
### 3.1. Scoped to an organization
Both project and personal snippets will be scoped to an Organization.
- Project snippets URLs will remain unchanged, as the URLs are routable.
- Personal snippets URLs will need to change to be `/-/organizations/<organization>/snippets/123`,
so that the URL is routeable
Creation of snippets will also be scoped to a User's current Organization. Because of that, we recommend renaming `personal snippets` to `organization snippets` once the Organization is rolled out. A User can create many independent snippet collections across multiple Organizations.
## 4. Evaluation
Snippets are scoped to an Organization because Gitaly is confined to a Cell.
## 4.1. Pros
- No need to have clusterwide Gitaly.
## 4.2. Cons
- We will break [snippet discovery](/ee/user/snippets.md#discover-snippets).
- Snippet access may become subordinate to the visibility of the Organization.

View File

@ -0,0 +1,30 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Problem A'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: A
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,30 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Uploads'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Uploads
> TL;DR
## 1. Definition
## 2. Data flow
## 3. Proposal
## 4. Evaluation
## 4.1. Pros
## 4.2. Cons

View File

@ -0,0 +1,52 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: User Profile'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: User Profile
The existing User Profiles will initially be scoped to an Organization. Long-term, we should consider aggregating parts of the User activity across Organizations to enable Users a global view of their contributions.
## 1. Definition
Each GitLab account has a [User Profile](../../../../user/profile/index.md), which contains information about the User and their GitLab activity.
## 2. Data flow
## 3. Proposal
User Profiles will be scoped to an Organization.
- Users can set a Home Organization as their main Organization.
- Users who do not exist in the database at all display a 404 not found error when trying to access their User Profile.
- User who haven't contributed to an Organization display their User Profile with an empty state.
- When displaying a User Profile empty state, if the profile has a Home Organization set to another Organization, we display a call-to-action allowing navigation to the main Organization.
- User Profile URLs will not reference the Organization and remain as: `/<username>`. We follow the same pattern as is used for `Your Work`, meaning that profiles are always seen in the context of an Organization.
- Breadcrumbs on the User Profile will present as `[Organization Name] / [Username]`.
See [issue #411931](https://gitlab.com/gitlab-org/gitlab/-/issues/411931) for design proposals.
## 4. Evaluation
We expect the [majority of Users to perform most of their activity in one single Organization](../../organization/index.md#data-exploration).
This is why we deem it acceptable to scope the User Profile to an Organization at first.
More discovery is necessary to understand which aspects of the current User Profile are relevant to showcase contributions in a global context.
## 4.1. Pros
- Viewing a User Profile scoped to an Organization allows you to focus on contributions that are most relevant to your Organization, filtering out the User's other activities.
- Existing User Profile URLs do not break.
## 4.2. Cons
- Users will lose the ability to display their entire activity, which may lessen the effectiveness of using their User Profile as a resume of achievements when working across multiple Organizations.

View File

@ -0,0 +1,58 @@
---
stage: enablement
group: Tenant Scale
description: 'Cells: Your Work'
---
<!-- vale gitlab.FutureTense = NO -->
This document is a work-in-progress and represents a very early state of the
Cells design. Significant aspects are not documented, though we expect to add
them in the future. This is one possible architecture for Cells, and we intend to
contrast this with alternatives before deciding which approach to implement.
This documentation will be kept even if we decide not to implement this so that
we can document the reasons for not choosing this approach.
# Cells: Your Work
Your Work will be scoped to an Organization.
Counts presented in the individual dashboards will relate to the selected Organization.
## 1. Definition
When accessing `gitlab.com/dashboard/`, users can find a [focused view of items that they have access to](../../../../tutorials/left_sidebar/index.md#use-a-more-focused-view).
This overview contains dashboards relating to:
- Projects
- Groups
- Issues
- Merge requests
- To-Do list
- Milestones
- Snippets
- Activity
- Workspaces
- Environments
- Operations
- Security
## 2. Data flow
## 3. Proposal
Your Work will be scoped to an Organization, giving the user an overview of all the items they can access in the Organization they are currently viewing.
- Issue, Merge request and To-Do list counts will refer to the selected Organization.
## 4. Evaluation
Scoping Your Work to an Organization makes sense in the context of the [proposed Organization navigation](https://gitlab.com/gitlab-org/gitlab/-/issues/417778).
Considering that [we expect most users to work in a single Organization](../../organization/index.md#data-exploration), we deem this impact acceptable.
## 4.1. Pros
- Viewing Your Work scoped to an Organization allows Users to focus on content that is most relevant to their currently selected Organization.
## 4.2. Cons
- Users working across multiple Organizations will have to navigate to each Organization to access all of their work items.

View File

@ -328,34 +328,34 @@ In the Cells architecture there will be more Postgres instances because of which
The Cells architecture will impact many features requiring some of them to be rewritten, or changed significantly.
Below is a list of known affected features with preliminary proposed solutions.
- [Cells: Admin Area](cells-feature-admin-area.md)
- [Cells: Backups](cells-feature-backups.md)
- [Cells: CI Runners](cells-feature-ci-runners.md)
- [Cells: Container Registry](cells-feature-container-registry.md)
- [Cells: Contributions: Forks](cells-feature-contributions-forks.md)
- [Cells: Database Sequences](cells-feature-database-sequences.md)
- [Cells: Data Migration](cells-feature-data-migration.md)
- [Cells: Explore](cells-feature-explore.md)
- [Cells: Git Access](cells-feature-git-access.md)
- [Cells: Global Search](cells-feature-global-search.md)
- [Cells: GraphQL](cells-feature-graphql.md)
- [Cells: Organizations](cells-feature-organizations.md)
- [Cells: Secrets](cells-feature-secrets.md)
- [Cells: Snippets](cells-feature-snippets.md)
- [Cells: User Profile](cells-feature-user-profile.md)
- [Cells: Your Work](cells-feature-your-work.md)
- [Cells: Admin Area](impacted_features/admin-area.md)
- [Cells: Backups](impacted_features/backups.md)
- [Cells: CI Runners](impacted_features/ci-runners.md)
- [Cells: Container Registry](impacted_features/container-registry.md)
- [Cells: Contributions: Forks](impacted_features/contributions-forks.md)
- [Cells: Database Sequences](impacted_features/database-sequences.md)
- [Cells: Data Migration](impacted_features/data-migration.md)
- [Cells: Explore](impacted_features/explore.md)
- [Cells: Git Access](impacted_features/git-access.md)
- [Cells: Global Search](impacted_features/global-search.md)
- [Cells: GraphQL](impacted_features/graphql.md)
- [Cells: Organizations](impacted_features/organizations.md)
- [Cells: Secrets](impacted_features/secrets.md)
- [Cells: Snippets](impacted_features/snippets.md)
- [Cells: User Profile](impacted_features/user-profile.md)
- [Cells: Your Work](impacted_features/your-work.md)
### Impacted features: Placeholders
The following list of impacted features only represents placeholders that still require work to estimate the impact of Cells and develop solution proposals.
- [Cells: Agent for Kubernetes](cells-feature-agent-for-kubernetes.md)
- [Cells: GitLab Pages](cells-feature-gitlab-pages.md)
- [Cells: Personal Access Tokens](cells-feature-personal-access-tokens.md)
- [Cells: Personal Namespaces](cells-feature-personal-namespaces.md)
- [Cells: Router Endpoints Classification](cells-feature-router-endpoints-classification.md)
- [Cells: Schema changes (Postgres and Elasticsearch migrations)](cells-feature-schema-changes.md)
- [Cells: Uploads](cells-feature-uploads.md)
- [Cells: Agent for Kubernetes](impacted_features/agent-for-kubernetes.md)
- [Cells: GitLab Pages](impacted_features/gitlab-pages.md)
- [Cells: Personal Access Tokens](impacted_features/personal-access-tokens.md)
- [Cells: Personal Namespaces](impacted_features/personal-namespaces.md)
- [Cells: Router Endpoints Classification](impacted_features/router-endpoints-classification.md)
- [Cells: Schema changes (Postgres and Elasticsearch migrations)](impacted_features/schema-changes.md)
- [Cells: Uploads](impacted_features/uploads.md)
- ...
## Frequently Asked Questions

View File

@ -129,7 +129,8 @@ The following GitLab application features are not available:
The following features will not be supported:
- Mattermost
- Server-side Git hooks. Use [push rules](../../user/project/repository/push_rules.md) instead.
- [Server-side Git hooks](../../administration/server_hooks.md).
GitLab Dedicated is a SaaS service, and access to the underlying infrastructure is only available to GitLab Inc. team members. Due to the nature of server side configuration, there is a possible security concern of running arbitrary code on Dedicated services, as well as the possible impact that can have on the service SLA. Use the alternative [push rules](../../user/project/repository/push_rules.md) or [webhooks](../../user/project/integrations/webhooks.md) instead.
### GitLab Dedicated service features

View File

@ -34,8 +34,7 @@ Provide feedback on this experimental feature in [issue 416537](https://gitlab.c
- Title of the merge request
- Contents of the description
- Source branch name
- Target branch name
- Diff of changes between the source branch's head and the target branch
## Summarize merge request changes **(ULTIMATE SAAS)**

View File

@ -31,9 +31,8 @@ module Gitlab
"#{Gitlab::Observability.observability_url}/query/#{project.group.id}/#{project.id}/v1/traces"
end
def provisioning_url(_project)
# TODO Change to correct endpoint when API is ready
Gitlab::Observability.observability_url.to_s
def provisioning_url(project)
"#{Gitlab::Observability.observability_url}/v3/tenant/#{project.id}"
end
# Returns true if the GitLab Observability UI (GOUI) feature flag is enabled

View File

@ -15,17 +15,17 @@ module Gitlab
# All available Themes
def available_themes
[
Theme.new(1, s_('NavigationTheme|Indigo'), 'ui-indigo', 'theme_indigo', '#292961'),
Theme.new(6, s_('NavigationTheme|Light Indigo'), 'ui-light-indigo', 'theme_light_indigo', '#4b4ba3'),
Theme.new(4, s_('NavigationTheme|Blue'), 'ui-blue', 'theme_blue', '#1a3652'),
Theme.new(7, s_('NavigationTheme|Light Blue'), 'ui-light-blue', 'theme_light_blue', '#2261a1'),
Theme.new(5, s_('NavigationTheme|Green'), 'ui-green', 'theme_green', '#0d4524'),
Theme.new(8, s_('NavigationTheme|Light Green'), 'ui-light-green', 'theme_light_green', '#156b39'),
Theme.new(9, s_('NavigationTheme|Red'), 'ui-red', 'theme_red', '#691a16'),
Theme.new(10, s_('NavigationTheme|Light Red'), 'ui-light-red', 'theme_light_red', '#a62e21'),
Theme.new(2, s_('NavigationTheme|Gray'), 'ui-gray', 'theme_gray', '#303030'),
Theme.new(3, s_('NavigationTheme|Light Gray'), 'ui-light-gray', 'theme_light_gray', '#666'),
Theme.new(11, s_('NavigationTheme|Dark Mode (alpha)'), 'gl-dark', nil, '#303030')
Theme.new(1, s_('NavigationTheme|Indigo'), 'ui-indigo', 'theme_indigo', '#222261'),
Theme.new(6, s_('NavigationTheme|Light Indigo'), 'ui-light-indigo', 'theme_light_indigo', '#41419f'),
Theme.new(4, s_('NavigationTheme|Blue'), 'ui-blue', 'theme_blue', '#0b2640'),
Theme.new(7, s_('NavigationTheme|Light Blue'), 'ui-light-blue', 'theme_light_blue', '#145aa1'),
Theme.new(5, s_('NavigationTheme|Green'), 'ui-green', 'theme_green', '#0e4328'),
Theme.new(8, s_('NavigationTheme|Light Green'), 'ui-light-green', 'theme_light_green', '#1b653f'),
Theme.new(9, s_('NavigationTheme|Red'), 'ui-red', 'theme_red', '#580d02'),
Theme.new(10, s_('NavigationTheme|Light Red'), 'ui-light-red', 'theme_light_red', '#a02e1c'),
Theme.new(2, s_('NavigationTheme|Gray'), 'ui-gray', 'theme_gray', '#333238'),
Theme.new(3, s_('NavigationTheme|Light Gray'), 'ui-light-gray', 'theme_light_gray', '#ececef'),
Theme.new(11, s_('NavigationTheme|Dark Mode (alpha)'), 'gl-dark', nil, '#1f1e24')
]
end

View File

@ -25163,6 +25163,16 @@ msgstr ""
msgid "Inline math"
msgstr ""
msgid "InlineFindings|1 Code Quality finding detected"
msgid_plural "InlineFindings|%d Code Quality findings detected"
msgstr[0] ""
msgstr[1] ""
msgid "InlineFindings|1 SAST finding detected"
msgid_plural "InlineFindings|%d SAST findings detected"
msgstr[0] ""
msgstr[1] ""
msgid "Input host keys manually"
msgstr ""
@ -32957,12 +32967,21 @@ msgstr ""
msgid "Organization|An error occurred loading the projects. Please refresh the page to try again."
msgstr ""
msgid "Organization|Copy organization ID"
msgstr ""
msgid "Organization|Org ID"
msgstr ""
msgid "Organization|Organization navigation"
msgstr ""
msgid "Organization|Organization overview"
msgstr ""
msgid "Organization|Public - The organization can be accessed without any authentication."
msgstr ""
msgid "Organization|Search or filter list"
msgstr ""
@ -33679,6 +33698,9 @@ msgstr ""
msgid "Pages Domain"
msgstr ""
msgid "PagesDomain|Certificate Key is too long. (Max %d bytes)"
msgstr ""
msgid "Pagination|First"
msgstr ""

View File

@ -349,6 +349,472 @@ x6zG6WoibsbsJMj70nwseUnPTBQNDP+j61RJjC/r
end
end
trait :extra_long_key do
certificate do
<<~CERT
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
CERT
end
key do
<<~KEY
-----BEGIN PRIVATE KEY-----
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
NtjUPwBEHYJ9bcJ/fgqxWtoc23l/1OfP
-----END PRIVATE KEY-----
KEY
end
end
trait :key_length_8192 do
certificate do
<<~CERT
-----BEGIN CERTIFICATE-----
MIIJLzCCBRegAwIBAgIUB6932CbRBXCmSbrDR1L1AeVQsq0wDQYJKoZIhvcNAQEL
BQAwJzELMAkGA1UEBhMCZGUxGDAWBgNVBAMMD2xvY2FsaG9zdC5sb2NhbDAeFw0y
MzA4MTcxOTA0MDlaFw0yMzA5MTYxOTA0MDlaMCcxCzAJBgNVBAYTAmRlMRgwFgYD
VQQDDA9sb2NhbGhvc3QubG9jYWwwggQiMA0GCSqGSIb3DQEBAQUAA4IEDwAwggQK
AoIEAQC5qEZ5QBj+nPYQjpbOBwRWIsNJVPgB54ADkjYSfL6kbPyeQsgAP2vU8zWA
Z1lIyiV3M5qa7KZ4oK4uin7O3rwho2XoWaaf1Z6ifpNeVl8Favyn/IeVJp+jchtl
VyDAEaX0GozOfo5X+xOAQE7twUNSlB8+6YIqxTQf28zB7ks/2w2qrJvXO3cSgQyB
74tTUn/25hwATNlEzSBRdLzoNeK7ZInayLAvxwR5krIm+yjH0EGsI7XZQMLjZOwL
95FrhfBLYODwPam5lAzPB6paeY+oGGkNwGhzECqmHLnXMSlY6lpeUs1vOCjVcE13
v5jb+nIwLaBWvpF4fnskgwI5+hiBcfsRuByKH+x5pW7ofCnqAOgkq8m1ErqRIvN8
SG54ySlA/+RoPI+G/1/x/AdQtdQxL5X80ZeUjpLGTGy3kPVEQ8feP8Ny3rSklsPt
nIMk6fRWgiw2ba2jt500F+FuntCj3PWDZfQ+LBBswuLpQCoB124RobXwFCCfqSd1
8+S8PfGJQ4gDwc4K17lm/WJq/X3mUDmxJLhEIvpZUxi2App7b3KqdCMthOUxUFoa
IZiWtbK3h0A10c/Qr29BI3soKxD30dw/DylR42dTGZUX9fdEkPOulVaTXmApnGkv
HRomQ2VSJQq8wccceXvte/ZV2duehDk7032LhlNCeosfaJs4L5BXdB+xGfxDzQ65
p2xo/nA3k4eJhWhIejzOmJ1zUcsAl9rDUK312hajRh1UFDIr53yYnpAjcxyDYZqv
EcuFrjNZKi7zPsZC/17f48hOEHXC5PcFJrm7dvJa2z3+Vswy7IVljVhxu5CQQwk/
aIWDAqQXjXYL9L9y8kGspczucVhthOuDKad1Hopy3E5HpaNhXWOLqglWQihjRviG
Llt1DU5RnMRJJUp2JW8Ic+SAgtBpS39xFyJRj+gBrcWopdDWr/leTWY6NEKijTRg
SzxmvFr9uRlrAExO+FNafeTslYoStTQPysBwNC0HWa3SV04GU/LBRVko6f/ee5WQ
aPjGeRiSqBDZBL5HkJ90UMAXrj+2lv+yW16T9upD0H8gv/GpZZIPoZ9fcanSWMz4
1BloOglzUC+AWe/8j6egcpwDbVMbOq51XxP5BkXp/wc2vzxMF8OEMLm6pu6arAup
8JFtFwrS7Cn85iWIppWxNiuSptpBnYdCd+e51uKjigvA+KGyarnPyJOOHynLVwpg
hofK8hw0EzDgkQ2ysh9rwqvKtMJdXr9Vi4LGlacnTvO3modvk2B6zv4AsSsR88gP
HMTM+Y6J8pJVs8OkY8S/utlOXKrTv3Y1+PRueVWtyVggfvOaXy2UKkAp8e6zBZ6V
5D0BheSOW99ImH63c0AXQ8JV6j1TAgMBAAGjUzBRMB0GA1UdDgQWBBQlK1yjMe+/
2592Ei6EiNHBMnzMMjAfBgNVHSMEGDAWgBQlK1yjMe+/2592Ei6EiNHBMnzMMjAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IEAQBYie/7NeBW6jzXhL73
qTfubvtpYkNSCSYj5OvunG5sCzHEpWv31yEWbhRxEoLlh4rZxGtsn7hqsa6D4u6U
/OXydn8NNPIJH+bOLkn2QLz9Rn8N3YLTAnbjtYIIPDSy2VXMnJAP99eBH/+p+V0V
ZIxgSVBNljs34DJsLzOaiDz5js8z/ZPuJbiNjmUOpPxFj2TS7zuVWvpmrei8yGLC
PPxe4+LzIRTSgOJAdfyVzgBBD4vjFyPyNi6z2e5t0PrZ2jMJIXtjJQ98CMDEmn+m
EYrB3RoSTIGAv1Y3MZfkcOY+SzkqEvb8ojqODX+6axso3Vw1ylVQnGhmOesSVV7Z
Fnf2AtaRdjOJD3rhsZw80fjv/PW8NKoS1CdDiylJ0t1GQycSTOs7aCZx0aVc9iDY
OAKZU6rBWbWBQx9Nddsll/oeTkRhd3tW3lZH6r8tXweDERQffrHpAv6GiAnqmQKQ
s5t8cp8iB7mkCH8YmONdaoR+e5jGwEAVgax1BDedt1Ryz85gG6udAN2xHy9NAxPH
z0tRSpcvBztkw5jTSUxT1cVyYt+fDQpB8LX085Vg07bniCZ3oZmq0QO0M3mGrrva
FX9VnsakfTJ7lMlaEE7qMfE58S5sLkbXfWG649MFs6lK0e3vkU81iy+DiAA8wA7D
t/pswj0DLAQHdrNNvpRTMrl4edHAZawQFMDuTO4EFMYygcYu7s/Q4RMZaoknYbWk
57JMXg5iC4/GcwpMZ33zA/zIuf6TMXAS2p9yjCQOaPhiwWVCa4awtCZl4iVI5Q78
yxWN6mLmqH/zHiIBf4rtcwYmSzLyg8rz1FRjBR50BpC3Vt/F80rIe46QgBkyxwT3
FhtOJkGAyw6yko0JinA/14KmeJnpf3g0YZig+jhG1omhigms6U18KYdeVy6N/5wf
35ut0cGvXE+Ii4XKRZuVx0UxszaS3aBxLvsQBOOKEBRJljCHimWGPqGYECgvb7BK
20UYkYmxX/l/hYjlmE9/wI7Wozd3ho2PcPfI5kSBHvo8qVg/VouLAq1cxvHPWjz8
FOOgukZKnLRnVHz1wqH0HxoQKrW4iGZINHDBVh6R1zgEBWAm1X3xNQ6Sww+xIoUz
lYoDi58waKHs6ph82qoPfRtielxdZGLz7JhijQqWG+QYB45d55GG8FC15byH0PTx
JOlCjbDbHKShYdcB/UK7nr7fNTWgjsaF7gaLy0GOZS4zDE6/TtDNLC6zrwipcYOm
dqd6UrgdkMHccm7qCJpBdD09cTH8sJIgGSEC3YuHvqJ1/bUMaXAF5em2cQBcH+Ng
q98r/+7CUPADtb/H+OjIWhW2vLGISMbr7xgWqkETsrh+XZI3QOuz2/n38PZBcwhm
O5R7
-----END CERTIFICATE-----
CERT
end
key do
<<~KEY
-----BEGIN PRIVATE KEY-----
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
c5RerXbZTDEafdHsyGlkhu+nOjYvyGM=
-----END PRIVATE KEY-----
KEY
end
end
trait :instance_serverless do
wildcard { true }
scope { :instance }

View File

@ -1,6 +1,13 @@
import * as testingLibrary from '@testing-library/dom';
import { createWrapper, WrapperArray, ErrorWrapper, mount, shallowMount } from '@vue/test-utils';
import { isArray, upperFirst } from 'lodash';
import {
createWrapper,
Wrapper, // eslint-disable-line no-unused-vars
ErrorWrapper,
mount,
shallowMount,
WrapperArray,
} from '@vue/test-utils';
import { compose } from 'lodash/fp';
const vNodeContainsText = (vnode, text) =>
(vnode.text && vnode.text.includes(text)) ||
@ -14,7 +21,7 @@ const vNodeContainsText = (vnode, text) =>
*
* @param {HTMLElement} element
* @param {Object} options
* @returns VTU wrapper
* @returns {Wrapper} VTU wrapper
*/
const createWrapperFromElement = (element, options) =>
// eslint-disable-next-line no-underscore-dangle
@ -52,19 +59,84 @@ export const waitForMutation = (store, expectedMutationType) =>
});
});
/**
* Query function type
* @callback FindFunction
* @param text
* @returns {Wrapper}
*/
/**
* Query all function type
* @callback FindAllFunction
* @param text
* @returns {WrapperArray}
*/
/**
* Query find with options functions type
* @callback FindWithOptionsFunction
* @param text
* @param options
* @returns {Wrapper}
*/
/**
* Query find all with options functions type
* @callback FindAllWithOptionsFunction
* @param text
* @param options
* @returns {WrapperArray}
*/
/**
* Extended Wrapper queries
* @typedef { {
* findByTestId: FindFunction,
* findAllByTestId: FindAllFunction,
* findComponentByTestId: FindFunction,
* findAllComponentsByTestId: FindAllFunction,
* findByRole: FindWithOptionsFunction,
* findAllByRole: FindAllWithOptionsFunction,
* findByLabelText: FindWithOptionsFunction,
* findAllByLabelText: FindAllWithOptionsFunction,
* findByPlaceholderText: FindWithOptionsFunction,
* findAllByPlaceholderText: FindAllWithOptionsFunction,
* findByText: FindWithOptionsFunction,
* findAllByText: FindAllWithOptionsFunction,
* findByDisplayValue: FindWithOptionsFunction,
* findAllByDisplayValue: FindAllWithOptionsFunction,
* findByAltText: FindWithOptionsFunction,
* findAllByAltText: FindAllWithOptionsFunction,
* findByTitle: FindWithOptionsFunction,
* findAllByTitle: FindAllWithOptionsFunction
* } } ExtendedQueries
*/
/**
* Extended Wrapper
* @typedef {(Wrapper & ExtendedQueries)} ExtendedWrapper
*/
/**
* Creates a Wrapper {@link https://v1.test-utils.vuejs.org/api/wrapper/} with
* Additional Queries {@link https://testing-library.com/docs/queries/about}.
* @param { Wrapper } wrapper
* @returns { ExtendedWrapper }
*/
export const extendedWrapper = (wrapper) => {
// https://testing-library.com/docs/queries/about
const AVAILABLE_QUERIES = [
'byRole',
'byLabelText',
'byPlaceholderText',
'byText',
'byDisplayValue',
'byAltText',
'byTitle',
'ByRole',
'ByLabelText',
'ByPlaceholderText',
'ByText',
'ByDisplayValue',
'ByAltText',
'ByTitle',
];
if (isArray(wrapper) || !wrapper?.find) {
if (Array.isArray(wrapper) || !wrapper?.find) {
// eslint-disable-next-line no-console
console.warn(
'[vue-test-utils-helper]: you are trying to extend an object that is not a VueWrapper.',
@ -74,11 +146,13 @@ export const extendedWrapper = (wrapper) => {
return Object.defineProperties(wrapper, {
findByTestId: {
/** @this { Wrapper } */
value(id) {
return this.find(`[data-testid="${id}"]`);
},
},
findAllByTestId: {
/** @this { Wrapper } */
value(id) {
return this.findAll(`[data-testid="${id}"]`);
},
@ -88,6 +162,7 @@ export const extendedWrapper = (wrapper) => {
* with CSS selectors: https://v1.test-utils.vuejs.org/api/wrapper/#findcomponent
*/
findComponentByTestId: {
/** @this { Wrapper } */
value(id) {
return this.findComponent(`[data-testid="${id}"]`);
},
@ -97,6 +172,7 @@ export const extendedWrapper = (wrapper) => {
* with CSS selectors: https://v1.test-utils.vuejs.org/api/wrapper/#findallcomponents
*/
findAllComponentsByTestId: {
/** @this { Wrapper } */
value(id) {
return this.findAllComponents(`[data-testid="${id}"]`);
},
@ -105,13 +181,10 @@ export const extendedWrapper = (wrapper) => {
...AVAILABLE_QUERIES.reduce((accumulator, query) => {
return {
...accumulator,
[`find${upperFirst(query)}`]: {
[`find${query}`]: {
/** @this { Wrapper } */
value(text, options = {}) {
const elements = testingLibrary[`queryAll${upperFirst(query)}`](
wrapper.element,
text,
options,
);
const elements = testingLibrary[`queryAll${query}`](this.element, text, options);
// Element not found, return an `ErrorWrapper`
if (!elements.length) {
@ -126,13 +199,10 @@ export const extendedWrapper = (wrapper) => {
...AVAILABLE_QUERIES.reduce((accumulator, query) => {
return {
...accumulator,
[`findAll${upperFirst(query)}`]: {
[`findAll${query}`]: {
/** @this { Wrapper } */
value(text, options = {}) {
const elements = testingLibrary[`queryAll${upperFirst(query)}`](
wrapper.element,
text,
options,
);
const elements = testingLibrary[`queryAll${query}`](this.element, text, options);
const wrappers = elements.map((element) => {
const elementWrapper = createWrapperFromElement(element, this.options);
@ -152,6 +222,5 @@ export const extendedWrapper = (wrapper) => {
});
};
export const shallowMountExtended = (...args) => extendedWrapper(shallowMount(...args));
export const mountExtended = (...args) => extendedWrapper(mount(...args));
export const shallowMountExtended = compose(extendedWrapper, shallowMount);
export const mountExtended = compose(extendedWrapper, mount);

View File

@ -374,34 +374,34 @@ describe('Vue test utils helpers', () => {
});
});
describe.each`
mountExtendedFunction | expectedMountFunction
${shallowMountExtended} | ${'shallowMount'}
${mountExtended} | ${'mount'}
`('$mountExtendedFunction', ({ mountExtendedFunction, expectedMountFunction }) => {
const FakeComponent = jest.fn();
const options = {
propsData: {
foo: 'bar',
describe('mount extended functions', () => {
// eslint-disable-next-line vue/one-component-per-file
const FakeChildComponent = Vue.component('FakeChildComponent', {
template: '<div>Bar <div data-testid="fake-id"/></div>',
});
// eslint-disable-next-line vue/one-component-per-file
const FakeComponent = Vue.component('FakeComponent', {
components: {
FakeChildComponent,
},
};
beforeEach(() => {
const mockWrapper = { find: jest.fn() };
jest.spyOn(vtu, expectedMountFunction).mockImplementation(() => mockWrapper);
template: '<div>Foo <fake-child-component data-testid="fake-id" /></div>',
});
it(`calls \`${expectedMountFunction}\` with passed arguments`, () => {
mountExtendedFunction(FakeComponent, options);
expect(vtu[expectedMountFunction]).toHaveBeenCalledWith(FakeComponent, options);
describe('mountExtended', () => {
it('mounts component and provides extended queries', () => {
const wrapper = mountExtended(FakeComponent);
expect(wrapper.text()).toBe('Foo Bar');
expect(wrapper.findAllByTestId('fake-id').length).toBe(2);
});
});
it('returns extended wrapper', () => {
const result = mountExtendedFunction(FakeComponent, options);
expect(result).toHaveProperty('find');
expect(result).toHaveProperty('findByTestId');
describe('shallowMountExtended', () => {
it('shallow mounts component and provides extended queries', () => {
const wrapper = shallowMountExtended(FakeComponent);
expect(wrapper.text()).toBe('Foo');
expect(wrapper.findAllByTestId('fake-id').length).toBe(1);
});
});
});
});

View File

@ -2,7 +2,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import DiffInlineFindings from '~/diffs/components/diff_inline_findings.vue';
import DiffInlineFindingsItem from '~/diffs/components/diff_inline_findings_item.vue';
import { NEW_CODE_QUALITY_FINDINGS } from '~/diffs/i18n';
import { multipleCodeQualityNoSast } from '../mock_data/inline_findings';
import { multipleFindingsArrCodeQualityScale } from '../mock_data/inline_findings';
let wrapper;
const heading = () => wrapper.findByTestId('diff-inline-findings-heading');
@ -13,7 +13,7 @@ describe('DiffInlineFindings', () => {
return shallowMountExtended(DiffInlineFindings, {
propsData: {
title: NEW_CODE_QUALITY_FINDINGS,
findings: multipleCodeQualityNoSast.codeQuality,
findings: multipleFindingsArrCodeQualityScale,
},
});
};
@ -25,7 +25,7 @@ describe('DiffInlineFindings', () => {
it('renders the correct number of DiffInlineFindingsItem components with correct props', () => {
wrapper = createWrapper();
expect(diffInlineFindingsItems()).toHaveLength(multipleCodeQualityNoSast.codeQuality.length);
expect(diffInlineFindingsItems()).toHaveLength(multipleFindingsArrCodeQualityScale.length);
expect(diffInlineFindingsItems().wrappers[0].props('finding')).toEqual(
wrapper.props('findings')[0],
);

View File

@ -2,7 +2,7 @@ import { mountExtended } from 'helpers/vue_test_utils_helper';
import InlineFindings from '~/diffs/components/inline_findings.vue';
import DiffInlineFindings from '~/diffs/components/diff_inline_findings.vue';
import { NEW_CODE_QUALITY_FINDINGS } from '~/diffs/i18n';
import { threeCodeQualityFindingsRaw } from '../mock_data/inline_findings';
import { threeCodeQualityFindings } from '../mock_data/inline_findings';
let wrapper;
@ -12,7 +12,7 @@ describe('InlineFindings', () => {
const createWrapper = () => {
return mountExtended(InlineFindings, {
propsData: {
codeQuality: threeCodeQualityFindingsRaw,
codeQuality: threeCodeQualityFindings,
},
});
};
@ -28,6 +28,6 @@ describe('InlineFindings', () => {
it('renders diff inline findings component with correct props for codequality array', () => {
wrapper = createWrapper();
expect(diffInlineFindings().props('title')).toBe(NEW_CODE_QUALITY_FINDINGS);
expect(diffInlineFindings().props('findings')).toBe(threeCodeQualityFindingsRaw);
expect(diffInlineFindings().props('findings')).toBe(threeCodeQualityFindings);
});
});

View File

@ -4,6 +4,8 @@ export const multipleFindingsArrCodeQualityScale = [
description: 'mocked minor Issue',
line: 2,
scale: 'codeQuality',
href: '#',
text: 'mocked minor Issue',
},
{
severity: 'major',
@ -43,6 +45,8 @@ export const multipleFindingsArrSastScale = [
description: 'mocked low Issue',
line: 2,
scale: 'sast',
href: '#',
text: 'mocked low Issue',
},
{
severity: 'medium',
@ -76,48 +80,6 @@ export const multipleFindingsArrSastScale = [
},
];
export const multipleCodeQualityNoSast = {
codeQuality: multipleFindingsArrCodeQualityScale,
sast: [],
};
export const multipleSastNoCodeQuality = {
codeQuality: [],
sast: multipleFindingsArrSastScale,
};
export const fiveCodeQualityFindings = {
filePath: 'index.js',
codequality: multipleFindingsArrCodeQualityScale.slice(0, 5),
};
export const threeCodeQualityFindings = {
filePath: 'index.js',
codequality: multipleFindingsArrCodeQualityScale.slice(0, 3),
};
export const threeCodeQualityFindingsRaw = [multipleFindingsArrCodeQualityScale.slice(0, 3)];
export const singularCodeQualityFinding = {
filePath: 'index.js',
codequality: [multipleFindingsArrCodeQualityScale[0]],
};
export const singularFindingSast = {
filePath: 'index.js',
sast: [multipleFindingsArrSastScale[0]],
};
export const threeSastFindings = {
filePath: 'index.js',
sast: multipleFindingsArrSastScale.slice(0, 3),
};
export const oneCodeQualityTwoSastFindings = {
filePath: 'index.js',
sast: multipleFindingsArrSastScale.slice(0, 2),
codequality: [multipleFindingsArrCodeQualityScale[0]],
};
export const diffCodeQuality = {
diffFile: { file_hash: '123' },
diffLines: [
@ -151,3 +113,19 @@ export const diffCodeQuality = {
},
],
};
export const singularCodeQualityFinding = [multipleFindingsArrCodeQualityScale[0]];
export const singularSastFinding = [multipleFindingsArrSastScale[0]];
export const twoSastFindings = multipleFindingsArrSastScale.slice(0, 2);
export const fiveCodeQualityFindings = multipleFindingsArrCodeQualityScale.slice(0, 5);
export const threeCodeQualityFindings = multipleFindingsArrCodeQualityScale.slice(0, 3);
export const filePath = 'testPath';
export const scale = 'exampleScale';
export const dropdownIcon = {
id: 'noise.rb-2',
key: 'mockedkey',
name: 'severity-medium',
class: 'gl-text-orange-400',
};

View File

@ -1,15 +1,19 @@
import MockAdapter from 'axios-mock-adapter';
import * as Sentry from '@sentry/browser';
import { buildClient } from '~/observability/client';
import axios from '~/lib/utils/axios_utils';
jest.mock('~/lib/utils/axios_utils');
jest.mock('@sentry/browser');
describe('buildClient', () => {
let client;
let axiosMock;
const tracingUrl = 'https://example.com/tracing';
const EXPECTED_ERROR_MESSAGE = 'traces are missing/invalid in the response';
const provisioningUrl = 'https://example.com/provisioning';
const FETCHING_TRACES_ERROR = 'traces are missing/invalid in the response';
beforeEach(() => {
axiosMock = new MockAdapter(axios);
@ -17,7 +21,7 @@ describe('buildClient', () => {
client = buildClient({
tracingUrl,
provisioningUrl: 'https://example.com/provisioning',
provisioningUrl,
});
});
@ -25,6 +29,77 @@ describe('buildClient', () => {
axiosMock.restore();
});
describe('isTracingEnabled', () => {
it('returns true if requests succeedes', async () => {
axiosMock.onGet(provisioningUrl).reply(200, {
status: 'ready',
});
const enabled = await client.isTracingEnabled();
expect(enabled).toBe(true);
});
it('returns false if response is 404', async () => {
axiosMock.onGet(provisioningUrl).reply(404);
const enabled = await client.isTracingEnabled();
expect(enabled).toBe(false);
});
// we currently ignore the 'status' payload and just check if the request was successful
// We might improve this as part of https://gitlab.com/gitlab-org/opstrace/opstrace/-/issues/2315
it('returns true for any status', async () => {
axiosMock.onGet(provisioningUrl).reply(200, {
status: 'not ready',
});
const enabled = await client.isTracingEnabled();
expect(enabled).toBe(true);
});
it('throws in case of any non-404 error', async () => {
axiosMock.onGet(provisioningUrl).reply(500);
const e = 'Request failed with status code 500';
await expect(client.isTracingEnabled()).rejects.toThrow(e);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(e));
});
it('throws in case of unexpected response', async () => {
axiosMock.onGet(provisioningUrl).reply(200, {});
const e = 'Failed to check provisioning';
await expect(client.isTracingEnabled()).rejects.toThrow(e);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(e));
});
});
describe('enableTraces', () => {
it('makes a PUT request to the provisioning URL', async () => {
let putConfig;
axiosMock.onPut(provisioningUrl).reply((config) => {
putConfig = config;
return [200];
});
await client.enableTraces();
expect(putConfig.withCredentials).toBe(true);
});
it('reports an error if the req fails', async () => {
axiosMock.onPut(provisioningUrl).reply(401);
const e = 'Request failed with status code 401';
await expect(client.enableTraces()).rejects.toThrow(e);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(e));
});
});
describe('fetchTrace', () => {
it('fetches the trace from the tracing URL', async () => {
const mockTraces = [
@ -53,16 +128,18 @@ describe('buildClient', () => {
return expect(client.fetchTrace()).rejects.toThrow('traceId is required.');
});
it('rejects if traces are empty', () => {
it('rejects if traces are empty', async () => {
axiosMock.onGet(tracingUrl).reply(200, { traces: [] });
return expect(client.fetchTrace('trace-1')).rejects.toThrow(EXPECTED_ERROR_MESSAGE);
await expect(client.fetchTrace('trace-1')).rejects.toThrow(FETCHING_TRACES_ERROR);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(FETCHING_TRACES_ERROR));
});
it('rejects if traces are invalid', () => {
it('rejects if traces are invalid', async () => {
axiosMock.onGet(tracingUrl).reply(200, { traces: 'invalid' });
return expect(client.fetchTraces()).rejects.toThrow(EXPECTED_ERROR_MESSAGE);
await expect(client.fetchTraces()).rejects.toThrow(FETCHING_TRACES_ERROR);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(FETCHING_TRACES_ERROR));
});
});
@ -91,16 +168,18 @@ describe('buildClient', () => {
expect(result).toEqual(mockTraces);
});
it('rejects if traces are missing', () => {
it('rejects if traces are missing', async () => {
axiosMock.onGet(tracingUrl).reply(200, {});
return expect(client.fetchTraces()).rejects.toThrow(EXPECTED_ERROR_MESSAGE);
await expect(client.fetchTraces()).rejects.toThrow(FETCHING_TRACES_ERROR);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(FETCHING_TRACES_ERROR));
});
it('rejects if traces are invalid', () => {
it('rejects if traces are invalid', async () => {
axiosMock.onGet(tracingUrl).reply(200, { traces: 'invalid' });
return expect(client.fetchTraces()).rejects.toThrow(EXPECTED_ERROR_MESSAGE);
await expect(client.fetchTraces()).rejects.toThrow(FETCHING_TRACES_ERROR);
expect(Sentry.captureException).toHaveBeenCalledWith(new Error(FETCHING_TRACES_ERROR));
});
describe('query filter', () => {

View File

@ -0,0 +1,28 @@
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import App from '~/organizations/show/components/app.vue';
import OrganizationAvatar from '~/organizations/show/components/organization_avatar.vue';
describe('OrganizationShowApp', () => {
let wrapper;
const defaultPropsData = {
organization: {
id: 1,
name: 'GitLab',
},
};
const createComponent = () => {
wrapper = shallowMountExtended(App, { propsData: defaultPropsData });
};
beforeEach(() => {
createComponent();
});
it('renders organization avatar and passes organization prop', () => {
expect(wrapper.findComponent(OrganizationAvatar).props('organization')).toEqual(
defaultPropsData.organization,
);
});
});

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