Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2024-07-26 12:11:10 +00:00
parent cd85f3f68f
commit a0429b995e
67 changed files with 632 additions and 846 deletions

View File

@ -451,7 +451,7 @@
.os1-services:
services:
- !reference [.zoekt-services, services]
- name: opensearchproject/opensearch:1.3.5
- name: opensearchproject/opensearch:1.3.18
alias: elasticsearch
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true", "-E", "cluster.routing.allocation.disk.threshold_enabled=false"]
@ -490,7 +490,7 @@
.os2-services:
services:
- !reference [.zoekt-services, services]
- name: opensearchproject/opensearch:2.2.1
- name: opensearchproject/opensearch:2.15.0
alias: elasticsearch
command: ["bin/opensearch", "-E", "discovery.type=single-node", "-E", "plugins.security.disabled=true", "-E", "cluster.routing.allocation.disk.threshold_enabled=false"]

View File

@ -387,6 +387,17 @@ rspec-ee unit clickhouse:
- .rspec-base-pg14-clickhouse23
- .rails:rules:ee-only-clickhouse-changes
rspec ci-config-validation:
extends:
- .rspec-base-pg14
- .rails:rules:rspec-ci-config-validation
stage: test
script:
- !reference [.base-script, script]
- rspec_section rspec_simple_job "--tag ci_config_validation -- spec/dot_gitlab_ci/"
after_script:
- echo 'OK' # override after_script from .rspec-base
gitlab:clickhouse:rollback:main:
extends:
- .rspec-base

View File

@ -135,6 +135,9 @@
.if-merge-request-labels-run-without-gitaly-transactions: &if-merge-request-labels-run-without-gitaly-transactions
if: '($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_EVENT_TYPE != "merge_train") && $CI_MERGE_REQUEST_LABELS =~ /run-without-gitaly-transactions/'
.if-merge-request-labels-skip-ci-validation: &if-merge-request-labels-skip-ci-validation
if: '($CI_MERGE_REQUEST_EVENT_TYPE == "merged_result" || $CI_MERGE_REQUEST_EVENT_TYPE == "detached") && $CI_MERGE_REQUEST_LABELS =~ /pipeline:skip-ci-validation/'
.if-security-merge-request: &if-security-merge-request
if: '($CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_EVENT_TYPE != "merge_train") && $CI_PROJECT_NAMESPACE == "gitlab-org/security"'
@ -2370,6 +2373,15 @@
- !reference ['.rails:rules:ee-and-foss-integration', rules]
- !reference ['.rails:rules:ee-and-foss-migration', rules]
.rails:rules:rspec-ci-config-validation:
rules:
- <<: *if-merge-request-labels-skip-ci-validation
when: never
- <<: *if-merge-request
changes:
- "{,jh/}.gitlab-ci.yml"
- "{,jh/}.gitlab/ci/**/*"
.rails:rules:detect-tests:
rules:
- if: '$ENABLE_DETECT_TESTS == "true"'

View File

@ -43,6 +43,10 @@ include:
inputs:
gem_name: "diff_match_patch"
gem_path_prefix: "vendor/gems/"
- local: .gitlab/ci/templates/gem.gitlab-ci.yml
inputs:
gem_name: "gitlab-topology-service-client"
gem_path_prefix: "vendor/gems/"
- local: .gitlab/ci/templates/gem.gitlab-ci.yml
inputs:
gem_name: "sidekiq-7.1.6"

View File

@ -1 +1 @@
67840bedbf1fda223cbb9ebae021db71c31b69aa
a46a38047c60514d83aaa4713ba3e9f2ee626a87

View File

@ -152,6 +152,11 @@ gem 'graphiql-rails', '~> 1.10', feature_category: :api
gem 'apollo_upload_server', '~> 2.1.6', feature_category: :api
gem 'graphlient', '~> 0.8.0', feature_category: :importers # Used by BulkImport feature (group::import)
# Cells
gem 'gitlab-topology-service-client', '~> 0.1',
path: 'vendor/gems/gitlab-topology-service-client',
feature_category: :cell
# Generate Fake data
gem 'ffaker', '~> 2.23' # rubocop:todo Gemfile/MissingFeatureCategory

View File

@ -143,6 +143,12 @@ PATH
specs:
diff_match_patch (0.1.0)
PATH
remote: vendor/gems/gitlab-topology-service-client
specs:
gitlab-topology-service-client (0.1)
grpc
PATH
remote: vendor/gems/mail-smtp_pool
specs:
@ -2038,6 +2044,7 @@ DEPENDENCIES
gitlab-secret_detection!
gitlab-sidekiq-fetcher!
gitlab-styles (~> 12.0.1)
gitlab-topology-service-client (~> 0.1)!
gitlab-utils!
gitlab_chronic_duration (~> 0.12)
gitlab_omniauth-ldap (~> 2.2.0)

View File

@ -161,6 +161,8 @@ export default {
this.infiniteScrollingTriggered = false;
this.filterSearchTriggered = true;
this.resetRequestData();
filters.forEach((filter) => {
if (!filter.type) {
if (this.glFeatures.populateAndUseBuildNamesTable) {
@ -178,12 +180,6 @@ export default {
}
});
// all filters have been cleared reset query params
// and refetch jobs/count with defaults
if (!filters.length) {
this.resetRequestData();
}
this.$apollo.queries.jobs.refetch(this.requestData);
this.updateHistoryAndFetchCount();
},

View File

@ -1,11 +1,3 @@
import initIntegrationSettingsForm from '~/integrations/edit';
import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
initIntegrationSettingsForm();
const prometheusSettingsSelector = '.js-prometheus-metrics-monitoring';
const prometheusSettingsWrapper = document.querySelector(prometheusSettingsSelector);
if (prometheusSettingsWrapper) {
const prometheusMetrics = new PrometheusMetrics(prometheusSettingsSelector);
prometheusMetrics.loadActiveMetrics();
}

View File

@ -1,11 +1,3 @@
import initIntegrationSettingsForm from '~/integrations/edit';
import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
initIntegrationSettingsForm();
const prometheusSettingsSelector = '.js-prometheus-metrics-monitoring';
const prometheusSettingsWrapper = document.querySelector(prometheusSettingsSelector);
if (prometheusSettingsWrapper) {
const prometheusMetrics = new PrometheusMetrics(prometheusSettingsSelector);
prometheusMetrics.loadActiveMetrics();
}

View File

@ -1,11 +1,3 @@
import initIntegrationSettingsForm from '~/integrations/edit';
import CustomMetrics from '~/prometheus_metrics/custom_metrics';
initIntegrationSettingsForm();
const prometheusSettingsSelector = '.js-prometheus-metrics-monitoring';
const prometheusSettingsWrapper = document.querySelector(prometheusSettingsSelector);
if (prometheusSettingsWrapper) {
const customMetrics = new CustomMetrics(prometheusSettingsSelector);
customMetrics.init();
}

View File

@ -1,6 +0,0 @@
export default {
EMPTY: 'empty',
LOADING: 'loading',
LIST: 'list',
NO_INTEGRATION: 'no-integration',
};

View File

@ -1,161 +0,0 @@
import $ from 'jquery';
import { escape, sortBy } from 'lodash';
import axios from '~/lib/utils/axios_utils';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { s__ } from '~/locale';
import PANEL_STATE from './constants';
import PrometheusMetrics from './prometheus_metrics';
export default class CustomMetrics extends PrometheusMetrics {
constructor(wrapperSelector) {
super(wrapperSelector);
this.customMetrics = [];
this.environmentsData = [];
this.$els = [];
this.$wrapperCustomMetrics = $(wrapperSelector);
this.$monitoredCustomMetricsPanel = this.$wrapperCustomMetrics.find(
'.js-panel-custom-monitored-metrics',
);
this.$monitoredCustomMetricsCount = this.$monitoredCustomMetricsPanel.find(
'.js-custom-monitored-count',
);
this.$monitoredCustomMetricsLoading = this.$monitoredCustomMetricsPanel.find(
'.js-loading-custom-metrics',
);
this.$monitoredCustomMetricsEmpty = this.$monitoredCustomMetricsPanel.find(
'.js-empty-custom-metrics',
);
this.$monitoredCustomMetricsList =
this.$monitoredCustomMetricsPanel.find('.js-custom-metrics-list');
this.$monitoredCustomMetricsNoIntegrationText = this.$monitoredCustomMetricsPanel.find(
'.js-no-active-integration-text',
);
this.$newCustomMetricButton = this.$monitoredCustomMetricsPanel.find('.js-new-metric-button');
this.$newCustomMetricText = this.$monitoredCustomMetricsPanel.find('.js-new-metric-text');
this.$flashCustomMetricsContainer = this.$wrapperCustomMetrics.find('.flash-container');
this.$els = [
this.$monitoredCustomMetricsLoading,
this.$monitoredCustomMetricsList,
this.$newCustomMetricButton,
this.$newCustomMetricText,
this.$monitoredCustomMetricsNoIntegrationText,
this.$monitoredCustomMetricsEmpty,
];
this.activeCustomMetricsEndpoint =
this.$monitoredCustomMetricsPanel.data('active-custom-metrics');
this.environmentsDataEndpoint = this.$monitoredCustomMetricsPanel.data(
'environments-data-endpoint',
);
this.isServiceActive = this.$monitoredCustomMetricsPanel.data('service-active');
}
init() {
if (this.isServiceActive) {
this.loadActiveCustomMetrics();
} else {
this.setNoIntegrationActiveState();
}
}
// eslint-disable-next-line class-methods-use-this
setHidden(els) {
els.forEach((el) => el.addClass('hidden'));
}
setVisible(...els) {
this.setHidden(this.$els.filter((el) => !els.includes(el)));
els.forEach((el) => el.removeClass('hidden'));
}
showMonitoringCustomMetricsPanelState(stateName) {
switch (stateName) {
case PANEL_STATE.LOADING:
this.setVisible(this.$monitoredCustomMetricsLoading);
break;
case PANEL_STATE.LIST:
this.setVisible(this.$newCustomMetricButton, this.$monitoredCustomMetricsList);
break;
case PANEL_STATE.NO_INTEGRATION:
this.setVisible(
this.$monitoredCustomMetricsNoIntegrationText,
this.$monitoredCustomMetricsEmpty,
);
break;
default:
this.setVisible(
this.$monitoredCustomMetricsEmpty,
this.$newCustomMetricButton,
this.$newCustomMetricText,
);
break;
}
}
populateCustomMetrics() {
const capitalizeGroup = (metric) => ({
...metric,
group: capitalizeFirstCharacter(metric.group),
});
const sortedMetrics = sortBy(this.customMetrics.map(capitalizeGroup), ['group', 'title']);
sortedMetrics.forEach((metric) => {
this.$monitoredCustomMetricsList.append(CustomMetrics.customMetricTemplate(metric));
});
this.$monitoredCustomMetricsCount.text(this.customMetrics.length);
this.showMonitoringCustomMetricsPanelState(PANEL_STATE.LIST);
if (!this.environmentsData) {
this.showFlashMessage(
s__(
'PrometheusService|These metrics will only be monitored after your first deployment to an environment',
),
);
}
}
showFlashMessage(message) {
this.$flashCustomMetricsContainer.removeClass('hidden');
this.$flashCustomMetricsContainer.find('.flash-text').text(message);
}
setNoIntegrationActiveState() {
this.showMonitoringCustomMetricsPanelState(PANEL_STATE.NO_INTEGRATION);
this.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY);
}
loadActiveCustomMetrics() {
super.loadActiveMetrics();
Promise.all([
axios.get(this.activeCustomMetricsEndpoint),
axios.get(this.environmentsDataEndpoint),
])
.then(([customMetrics, environmentsData]) => {
this.environmentsData = environmentsData.data.environments;
if (!customMetrics.data || !customMetrics.data.metrics) {
this.showMonitoringCustomMetricsPanelState(PANEL_STATE.EMPTY);
} else {
this.customMetrics = customMetrics.data.metrics;
this.populateCustomMetrics(customMetrics.data.metrics);
}
})
.catch((customMetricError) => {
this.showFlashMessage(customMetricError);
this.showMonitoringCustomMetricsPanelState(PANEL_STATE.EMPTY);
});
}
static customMetricTemplate(metric) {
return `
<li class="custom-metric">
<a href="${escape(metric.edit_path)}" class="custom-metric-link-bold">
${escape(metric.group)} / ${escape(metric.title)} (${escape(metric.unit)})
</a>
</li>
`;
}
}

View File

@ -1,151 +0,0 @@
import $ from 'jquery';
import { escape } from 'lodash';
import { s__, n__, sprintf } from '~/locale';
import axios from '../lib/utils/axios_utils';
import { backOff } from '../lib/utils/common_utils';
import PANEL_STATE from './constants';
export default class PrometheusMetrics {
constructor(wrapperSelector) {
this.backOffRequestCounter = 0;
this.$wrapper = $(wrapperSelector);
this.$monitoredMetricsPanel = this.$wrapper.find('.js-panel-monitored-metrics');
this.$monitoredMetricsCount = this.$monitoredMetricsPanel.find('.js-monitored-count');
this.$monitoredMetricsLoading = this.$monitoredMetricsPanel.find('.js-loading-metrics');
this.$monitoredMetricsEmpty = this.$monitoredMetricsPanel.find('.js-empty-metrics');
this.$monitoredMetricsList = this.$monitoredMetricsPanel.find('.js-metrics-list');
this.$missingEnvVarPanel = this.$wrapper.find('.js-panel-missing-env-vars');
this.$panelToggleRight = this.$missingEnvVarPanel.find('.js-panel-toggle-right');
this.$panelToggleDown = this.$missingEnvVarPanel.find('.js-panel-toggle-down');
this.$missingEnvVarMetricCount = this.$missingEnvVarPanel.find('.js-env-var-count');
this.$missingEnvVarMetricsList = this.$missingEnvVarPanel.find('.js-missing-var-metrics-list');
this.activeMetricsEndpoint = this.$monitoredMetricsPanel.data('activeMetrics');
this.helpMetricsPath = this.$monitoredMetricsPanel.data('metrics-help-path');
this.$panelToggleRight.on('click', (e) => this.handlePanelToggle(e));
this.$panelToggleDown.on('click', (e) => this.handlePanelToggle(e));
}
init() {
this.loadActiveMetrics();
}
handlePanelToggle(e) {
const $toggleBtn = $(e.currentTarget);
const $currentPanelBody = $toggleBtn.closest('.card').find('.card-body');
$currentPanelBody.toggleClass('hidden');
if ($toggleBtn.hasClass('js-panel-toggle-right')) {
$toggleBtn.addClass('hidden');
this.$panelToggleDown.removeClass('hidden');
} else if ($toggleBtn.hasClass('js-panel-toggle-down')) {
$toggleBtn.addClass('hidden');
this.$panelToggleRight.removeClass('hidden');
}
}
showMonitoringMetricsPanelState(stateName) {
switch (stateName) {
case PANEL_STATE.LOADING:
this.$monitoredMetricsLoading.removeClass('hidden');
this.$monitoredMetricsEmpty.addClass('hidden');
this.$monitoredMetricsList.addClass('hidden');
break;
case PANEL_STATE.LIST:
this.$monitoredMetricsLoading.addClass('hidden');
this.$monitoredMetricsEmpty.addClass('hidden');
this.$monitoredMetricsList.removeClass('hidden');
break;
default:
this.$monitoredMetricsLoading.addClass('hidden');
this.$monitoredMetricsEmpty.removeClass('hidden');
this.$monitoredMetricsList.addClass('hidden');
break;
}
}
populateActiveMetrics(metrics) {
let totalMonitoredMetrics = 0;
let totalMissingEnvVarMetrics = 0;
let totalExporters = 0;
metrics.forEach((metric) => {
if (metric.active_metrics > 0) {
totalExporters += 1;
this.$monitoredMetricsList.append(
`<li>${escape(metric.group)}<span class="badge">${escape(
metric.active_metrics,
)}</span></li>`,
);
totalMonitoredMetrics += metric.active_metrics;
if (metric.metrics_missing_requirements > 0) {
this.$missingEnvVarMetricsList.append(`<li>${escape(metric.group)}</li>`);
totalMissingEnvVarMetrics += 1;
}
}
});
if (totalMonitoredMetrics === 0) {
const emptyCommonMetricsText = sprintf(
s__('PrometheusService|No %{docsUrlStart}common metrics%{docsUrlEnd} were found'),
{
docsUrlStart: `<a href="${this.helpMetricsPath}">`,
docsUrlEnd: '</a>',
},
false,
);
this.$monitoredMetricsEmpty.empty();
this.$monitoredMetricsEmpty.append(`<p class="text-tertiary">${emptyCommonMetricsText}</p>`);
this.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY);
} else {
const metricsCountText = sprintf(
s__('PrometheusService|%{exporters} with %{metrics} were found'),
{
exporters: n__('%d exporter', '%d exporters', totalExporters),
metrics: n__('%d metric', '%d metrics', totalMonitoredMetrics),
},
);
this.$monitoredMetricsCount.text(metricsCountText);
this.showMonitoringMetricsPanelState(PANEL_STATE.LIST);
if (totalMissingEnvVarMetrics > 0) {
this.$missingEnvVarPanel.removeClass('hidden');
this.$missingEnvVarMetricCount.text(totalMissingEnvVarMetrics);
}
}
}
loadActiveMetrics() {
this.showMonitoringMetricsPanelState(PANEL_STATE.LOADING);
backOff((next, stop) => {
axios
.get(this.activeMetricsEndpoint)
.then(({ data }) => {
if (data && data.success) {
stop(data);
} else {
this.backOffRequestCounter += 1;
if (this.backOffRequestCounter < 3) {
next();
} else {
stop(data);
}
}
})
.catch(stop);
})
.then((res) => {
if (res && res.data && res.data.length) {
this.populateActiveMetrics(res.data);
} else {
this.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY);
}
})
.catch(() => {
this.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY);
});
}
}

View File

@ -61,7 +61,7 @@
.header-content {
a {
color: var(--gl-text-color-heading);
color: var(--gl-text-color-default);
&:hover {
color: var(--blue-600, $blue-600);

View File

@ -37,7 +37,7 @@
&:hover {
a {
color: var(--black, $black);
color: var(--gl-text-color-default);
}
}
@ -48,13 +48,6 @@
}
}
.wiki-list-expand-button,
.wiki-list-collapse-button {
color: $gray-400;
position: absolute;
right: $gl-spacing-scale-2;
}
ul.wiki-pages,
ul.wiki-pages li {
list-style: none;
@ -68,10 +61,6 @@
}
ul.wiki-pages-list.content-list {
a {
color: var(--blue-600, $blue-600);
}
ul {
list-style: none;
margin-left: 0;

View File

@ -31,7 +31,7 @@ module Groups
private
def namespace_work_items_enabled?
group&.namespace_work_items_enabled?
group&.namespace_work_items_enabled?(current_user)
end
def show_params

View File

@ -42,7 +42,7 @@ class GroupsController < Groups::ApplicationController
push_force_frontend_feature_flag(:work_items_alpha, group.work_items_alpha_feature_flag_enabled?)
push_frontend_feature_flag(:issues_grid_view)
push_frontend_feature_flag(:group_multi_select_tokens, group)
push_force_frontend_feature_flag(:namespace_level_work_items, group.namespace_work_items_enabled?)
push_force_frontend_feature_flag(:namespace_level_work_items, group.namespace_work_items_enabled?(current_user))
end
before_action only: :merge_requests do

View File

@ -130,7 +130,7 @@ module WorkItems
end
def include_namespace_level_work_items?
params.group? && params.group.namespace_work_items_enabled?
params.group? && params.group.namespace_work_items_enabled?(current_user)
end
def include_descendants?

View File

@ -8,7 +8,7 @@ module Resolvers
argument :iid, GraphQL::Types::String, required: true, description: 'IID of the work item.'
def ready?(**args)
return false if resource_parent.is_a?(Group) && !resource_parent.namespace_work_items_enabled?
return false if resource_parent.is_a?(Group) && !resource_parent.namespace_work_items_enabled?(current_user)
super
end

View File

@ -15,7 +15,7 @@ module Resolvers
description: 'Include work items from descendant groups and projects.'
def ready?(**args)
super && resource_parent.namespace_work_items_enabled?
super && resource_parent.namespace_work_items_enabled?(current_user)
end
private

View File

@ -952,7 +952,7 @@ class Group < Namespace
end
# Note: this method is overridden in EE to check the work_item_epics feature flag which also enables this feature
def namespace_work_items_enabled?
def namespace_work_items_enabled?(_user = nil)
::Feature.enabled?(:namespace_level_work_items, self, type: :development)
end

View File

@ -3,8 +3,6 @@
module Packages
module Protection
class Rule < ApplicationRecord
include IgnorableColumns
enum package_type: Packages::Package.package_types.slice(:npm)
enum minimum_access_level_for_push:
Gitlab::Access.sym_options_with_admin.slice(:maintainer, :owner, :admin),

View File

@ -11,7 +11,7 @@ module Groups
finder_params[:issue_types] = issue_types if issue_types.present?
finder_class =
if group.namespace_work_items_enabled?
if group.namespace_work_items_enabled?(current_user)
finder_params[:include_descendants] = true
WorkItems::WorkItemsFinder
else

View File

@ -48,6 +48,8 @@
= f.label field_name, "#{type.upcase} SSH keys", class: 'label-bold'
= f.select field_name, key_restriction_options_for_select(type), {}, class: 'form-control'
= render_if_exists 'admin/application_settings/disable_personal_access_tokens', form: f
.form-group
%label.label-bold= s_('AdminSettings|Feed token')
= f.gitlab_ui_checkbox_component :disable_feed_token, s_('AdminSettings|Disable feed token')

View File

@ -18,8 +18,8 @@
.gl-flex.gl-place-content-between.gl-items-center.gl-pb-3.gl-pr-1{ class: (@sidebar_page ? 'js-wiki-expand-pages-list wiki-list collapsed gl-pl-0' : 'gl-pl-3') }
.gl-flex.gl-items-center
- if @sidebar_page
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, icon: 'chevron-right', button_options: { class: "js-wiki-list-expand-button wiki-list-expand-button !gl-static gl-mr-2", data: { testid: 'expand-pages-list' } })
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, icon: 'chevron-down', button_options: { class: "js-wiki-list-collapse-button wiki-list-collapse-button !gl-static gl-mr-2", data: { testid: 'expand-pages-list' } })
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, icon: 'chevron-right', button_options: { class: "js-wiki-list-expand-button wiki-list-expand-button gl-mr-2", data: { testid: 'expand-pages-list' } })
= render Pajamas::ButtonComponent.new(category: :tertiary, size: :small, icon: 'chevron-down', button_options: { class: "js-wiki-list-collapse-button wiki-list-collapse-button gl-mr-2", data: { testid: 'expand-pages-list' } })
%h2.gl-text-lg.gl-my-0.gl-mr-3= s_('Wiki|Pages')
= gl_badge_tag @wiki_pages_count

View File

@ -181,6 +181,7 @@ The following metrics are available:
| `gitlab_connection_pool_size` | Gauge | 16.7 | Size of connection pool | |
| `gitlab_connection_pool_available_count` | Gauge | 16.7 | Number of available connections in the pool | |
| `gitlab_security_policies_scan_result_process_duration_seconds` | Histogram | 16.7 | The amount of time to process merge request approval policies | |
| `gitlab_security_policies_scan_execution_configuration_rendering_seconds` | Histogram | 17.3 | The amount of time to render scan execution policy CI configurations | |
| `gitlab_highlight_usage` | Counter | 16.8 | The number of times `Gitlab::Highlight` is used | `used_on` |
| `dependency_linker_usage` | Counter | 16.8 | The number of times dependency linker is used | `used_on` |
| `gitlab_keeparound_refs_requested_total` | Counter | 16.10 | Counts the number of keep-around refs requested to be created | `source` |

View File

@ -1,14 +0,0 @@
---
stage: Verify
group: Pipeline Execution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
redirect_to: 'api_resources.md'
remove_date: '2024-07-24'
---
This document was moved to [another location](api_resources.md).
<!-- This redirect file can be deleted after <2024-07-24>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,17 +0,0 @@
---
redirect_to: '../administration/operations/moving_repositories.md'
stage: Systems
group: Distribution
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
remove_date: '2024-07-16'
---
# List repository directories Rake task (removed)
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** Self-managed
This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137592) in GitLab 16.7
and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/384361) in 17.0.
See how to [move repositories](../administration/operations/moving_repositories.md) instead.

View File

@ -79,7 +79,11 @@ which must be paid at your next [reconciliation](../quarterly_reconciliation.md)
A top-level group can be [changed](../../user/group/manage.md#change-a-groups-path) like any other group.
Every user is included in seat usage, with the following exceptions:
### Billable users
Billable users count toward the number of subscription seats purchased in your subscription.
A user is not counted as a billable user if:
- Users who are pending approval.
- Members with the [Guest role on an Ultimate subscription](#free-guest-users).

View File

@ -186,4 +186,4 @@ flawfinder-sast:
## Semgrep slowness, unexpected results, or other errors
If Semgrep is slow, reports too many false positives or false negatives, crashes, fails, or is otherwise broken, see the Semgrep docs for [troubleshooting GitLab SAST](https://semgrep.dev/docs/troubleshooting/semgrep-ci#troubleshooting-gitlab-sast).
If Semgrep is slow, reports too many false positives or false negatives, crashes, fails, or is otherwise broken, see the Semgrep docs for [troubleshooting GitLab SAST](https://semgrep.dev/docs/troubleshooting/semgrep-app#troubleshooting-gitlab-sast).

View File

@ -1,11 +0,0 @@
---
redirect_to: '../gitops.md'
remove_date: '2024-07-25'
---
This document was moved to [another location](../gitops.md).
<!-- This redirect file can be deleted after <2024-07-25>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,11 +0,0 @@
---
redirect_to: '../gitops.md'
remove_date: '2024-07-25'
---
This document was moved to [another location](../gitops.md).
<!-- This redirect file can be deleted after <2024-07-25>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -1,11 +0,0 @@
---
redirect_to: '../../../clusters/agent/gitops.md'
remove_date: '2024-07-25'
---
This document was moved to [another location](../../../clusters/agent/gitops.md).
<!-- This redirect file can be deleted after <2024-07-25>. -->
<!-- Redirects that point to other docs in the same project expire in three months. -->
<!-- Redirects that point to docs in a different project or site (for example, link is not relative and starts with `https:`) expire in one year. -->
<!-- Before deletion, see: https://docs.gitlab.com/ee/development/documentation/redirects.html -->

View File

@ -97,6 +97,14 @@ Prerequisites:
In GitLab 15.7 and later, you can [use the application settings API to disable personal access tokens](../../api/settings.md#list-of-settings-that-can-be-accessed-via-api-calls).
In GitLab 17.3 and later, you can disable personal access tokens in the Admin UI:
1. On the left sidebar, at the bottom, select **Admin**.
1. Select **Settings > General**.
1. Expand **Visibility and access controls**.
1. Select the **Disable personal access tokens** checkbox.
1. Select **Save changes**.
### Disable personal access tokens for enterprise users
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/369504) in GitLab 16.11 [with a flag](../../administration/feature_flags.md) named `enterprise_disable_personal_access_tokens`. Disabled by default.

View File

@ -129,11 +129,6 @@ pre-push:
tags: secrets
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
run: 'if command -v gitleaks > /dev/null 2>&1; then gitleaks detect --log-opts="$(git merge-base origin/master HEAD)..HEAD" --no-banner --redact --verbose; else echo "WARNING: gitleaks is not installed. Please install it. See https://github.com/zricethezav/gitleaks#installing."; fi'
reminder-to-validate-ci-config:
tags: ci config
files: git diff --name-only --diff-filter=d $(git merge-base origin/master HEAD)..HEAD
glob: '{.gitlab-ci.yml,.gitlab/**/*.yml}'
run: "echo '🚨WARNING: You are modifying ci pipeline configurations. Please test your changes by running `bundle exec rspec spec/dot_gitlab_ci/job_dependency_spec.rb`. If you think we are missing test coverage for your changes, please add them.'"
scripts:
'merge_conflicts':
@ -161,11 +156,6 @@ pre-commit:
tags: secrets
files: git diff --name-only --diff-filter=d --staged
run: 'if command -v gitleaks > /dev/null 2>&1; then gitleaks protect --no-banner --staged --redact --verbose; else echo "WARNING: gitleaks is not installed. Please install it. See https://github.com/zricethezav/gitleaks#installing."; fi'
reminder-to-validate-ci-config:
tags: ci config
files: git diff --name-only --diff-filter=d --staged
glob: '{.gitlab-ci.yml,.gitlab/**/*.yml}'
run: "echo '🚨WARNING: You are modifying ci pipeline configurations. Please test your changes by running `bundle exec rspec spec/dot_gitlab_ci/job_dependency_spec.rb`. If you think we are missing test coverage for your changes, please add them.'"
auto-fix:
parallel: true

View File

@ -260,11 +260,6 @@ msgid_plural "%d epics"
msgstr[0] ""
msgstr[1] ""
msgid "%d exporter"
msgid_plural "%d exporters"
msgstr[0] ""
msgstr[1] ""
msgid "%d failed security job"
msgid_plural "%d failed security jobs"
msgstr[0] ""
@ -350,11 +345,6 @@ msgid_plural "%d merge requests"
msgstr[0] ""
msgstr[1] ""
msgid "%d metric"
msgid_plural "%d metrics"
msgstr[0] ""
msgstr[1] ""
msgid "%d milestone"
msgid_plural "%d milestones"
msgstr[0] ""
@ -19064,6 +19054,9 @@ msgstr ""
msgid "Disable group runners"
msgstr ""
msgid "Disable personal access tokens"
msgstr ""
msgid "Disable two-factor authentication"
msgstr ""
@ -42449,9 +42442,6 @@ msgstr ""
msgid "PrometheusAlerts|is less than"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr ""
msgid "PrometheusService|Active"
msgstr ""
@ -42503,9 +42493,6 @@ msgstr ""
msgid "PrometheusService|New metric"
msgstr ""
msgid "PrometheusService|No %{docsUrlStart}common metrics%{docsUrlEnd} were found"
msgstr ""
msgid "PrometheusService|No custom metrics have been created. Create one using the button above"
msgstr ""
@ -42524,9 +42511,6 @@ msgstr ""
msgid "PrometheusService|The contents of the credentials.json file of your service account."
msgstr ""
msgid "PrometheusService|These metrics will only be monitored after your first deployment to an environment"
msgstr ""
msgid "PrometheusService|To set up automatic monitoring, add the environment variable %{variable} to exporter's queries."
msgstr ""

View File

@ -64,7 +64,7 @@
"@floating-ui/dom": "^1.2.9",
"@gitlab/application-sdk-browser": "^0.3.2",
"@gitlab/at.js": "1.5.7",
"@gitlab/cluster-client": "^2.2.0",
"@gitlab/cluster-client": "^2.2.1",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.3.0",
"@gitlab/svgs": "3.109.0",

View File

@ -72,7 +72,7 @@ RSpec.describe Groups::GroupMembersController, feature_category: :groups_and_pro
context 'when there are import source users available' do
it 'returns import source users count' do
create(:import_source_user, :pending_assignment, namespace: group)
create(:import_source_user, :pending_reassignment, namespace: group)
create(:import_source_user, :awaiting_approval, namespace: group)
create(:import_source_user, :completed, namespace: group)
@ -105,7 +105,7 @@ RSpec.describe Groups::GroupMembersController, feature_category: :groups_and_pro
it 'returns 0 counts' do
stub_feature_flags(importer_user_mapping: false)
create(:import_source_user, :pending_assignment, namespace: group)
create(:import_source_user, :pending_reassignment, namespace: group)
get :index, params: { group_id: group }

View File

@ -15,8 +15,7 @@ require 'spec_helper'
# See `with gitlab.com gitlab-org gitlab-foss project` context below for details.
# If you think we are missing important test cases for a pipeline type, please add them following this exmaple.
# ***********************************************************************************************************
RSpec.describe 'ci jobs dependency', feature_category: :tooling,
quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/34040#note_1991033499' do
RSpec.describe 'ci jobs dependency', feature_category: :tooling do
include ProjectForksHelper
def sync_local_files_to_project(project, user, branch_name, files:)
@ -105,7 +104,7 @@ RSpec.describe 'ci jobs dependency', feature_category: :tooling,
end
before do
# delete once we have a migration to permenantly increase limit
# delete once we have a migration to permanently increase limit
stub_application_setting(max_yaml_size_bytes: 2.megabytes)
end

View File

@ -18,7 +18,7 @@ FactoryBot.define do
reassigned_by_user factory: :user
end
trait :pending_assignment do
trait :pending_reassignment do
status { 0 }
end

View File

@ -10,6 +10,8 @@ FactoryBot.define do
status { ::Members::MemberApproval.statuses[:pending] }
member { association(:project_member, user: user) }
member_namespace { association(:namespace) }
member_role_id { nil }
metadata { { access_level: new_access_level, member_role_id: member_role_id }.compact }
trait :for_new_member do
member { nil }

View File

@ -298,14 +298,9 @@ describe('Job table app', () => {
createComponent();
expect(successHandler).toHaveBeenCalledTimes(1);
expect(countSuccessHandler).toHaveBeenCalledTimes(1);
await findFilteredSearch().vm.$emit('filterJobsBySearch', ['raw text']);
expect(createAlert).toHaveBeenCalledWith(expectedWarning);
expect(successHandler).toHaveBeenCalledTimes(1);
expect(countSuccessHandler).toHaveBeenCalledTimes(1);
});
it('updates URL query string when filtering jobs by status', async () => {
@ -369,10 +364,45 @@ describe('Job table app', () => {
first: 30,
fullPath: 'gitlab-org/gitlab',
name: mockJobName,
statuses: null,
});
expect(countSuccessHandler).toHaveBeenCalledWith({
fullPath: 'gitlab-org/gitlab',
name: mockJobName,
statuses: null,
});
});
it('filters only by name after removing status filter', async () => {
await findFilteredSearch().vm.$emit('filterJobsBySearch', [
mockFailedSearchToken,
mockJobName,
]);
expect(successHandler).toHaveBeenCalledWith({
first: 30,
fullPath: 'gitlab-org/gitlab',
name: mockJobName,
statuses: 'FAILED',
});
expect(countSuccessHandler).toHaveBeenCalledWith({
fullPath: 'gitlab-org/gitlab',
name: mockJobName,
statuses: 'FAILED',
});
await findFilteredSearch().vm.$emit('filterJobsBySearch', [mockJobName]);
expect(successHandler).toHaveBeenCalledWith({
first: 30,
fullPath: 'gitlab-org/gitlab',
name: mockJobName,
statuses: null,
});
expect(countSuccessHandler).toHaveBeenCalledWith({
fullPath: 'gitlab-org/gitlab',
name: mockJobName,
statuses: null,
});
});

View File

@ -1,116 +0,0 @@
import MockAdapter from 'axios-mock-adapter';
import prometheusIntegration from 'test_fixtures/integrations/prometheus/prometheus_integration.html';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import PANEL_STATE from '~/prometheus_metrics/constants';
import CustomMetrics from '~/prometheus_metrics/custom_metrics';
import { metrics1 as metrics } from './mock_data';
describe('PrometheusMetrics', () => {
const customMetricsEndpoint =
'http://test.host/frontend-fixtures/integrations-project/prometheus/metrics';
let mock;
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(customMetricsEndpoint).reply(HTTP_STATUS_OK, {
metrics,
});
setHTMLFixture(prometheusIntegration);
});
afterEach(() => {
mock.restore();
resetHTMLFixture();
});
describe('Custom Metrics', () => {
let customMetrics;
beforeEach(() => {
customMetrics = new CustomMetrics('.js-prometheus-metrics-monitoring');
});
it('should initialize wrapper element refs on the class object', () => {
expect(customMetrics.$wrapperCustomMetrics).not.toBeNull();
expect(customMetrics.$monitoredCustomMetricsPanel).not.toBeNull();
expect(customMetrics.$monitoredCustomMetricsCount).not.toBeNull();
expect(customMetrics.$monitoredCustomMetricsLoading).not.toBeNull();
expect(customMetrics.$monitoredCustomMetricsEmpty).not.toBeNull();
expect(customMetrics.$monitoredCustomMetricsList).not.toBeNull();
expect(customMetrics.$newCustomMetricButton).not.toBeNull();
expect(customMetrics.$flashCustomMetricsContainer).not.toBeNull();
});
it('should contain api endpoints', () => {
expect(customMetrics.activeCustomMetricsEndpoint).toEqual(customMetricsEndpoint);
});
it('should show loading state when called with `loading`', () => {
customMetrics.showMonitoringCustomMetricsPanelState(PANEL_STATE.LOADING);
expect(customMetrics.$monitoredCustomMetricsLoading.hasClass('hidden')).toEqual(false);
expect(customMetrics.$monitoredCustomMetricsEmpty.hasClass('hidden')).toBe(true);
expect(customMetrics.$monitoredCustomMetricsList.hasClass('hidden')).toBe(true);
expect(customMetrics.$monitoredCustomMetricsNoIntegrationText.hasClass('hidden')).toBe(true);
expect(customMetrics.$newCustomMetricButton.hasClass('hidden')).toBe(true);
expect(customMetrics.$newCustomMetricText.hasClass('hidden')).toBe(true);
});
it('should show metrics list when called with `list`', () => {
customMetrics.showMonitoringCustomMetricsPanelState(PANEL_STATE.LIST);
expect(customMetrics.$monitoredCustomMetricsLoading.hasClass('hidden')).toBe(true);
expect(customMetrics.$monitoredCustomMetricsEmpty.hasClass('hidden')).toBe(true);
expect(customMetrics.$monitoredCustomMetricsList.hasClass('hidden')).toEqual(false);
expect(customMetrics.$monitoredCustomMetricsNoIntegrationText.hasClass('hidden')).toBe(true);
expect(customMetrics.$newCustomMetricButton.hasClass('hidden')).toEqual(false);
expect(customMetrics.$newCustomMetricText.hasClass('hidden')).toBe(true);
});
it('should show empty state when called with `empty`', () => {
customMetrics.showMonitoringCustomMetricsPanelState(PANEL_STATE.EMPTY);
expect(customMetrics.$monitoredCustomMetricsLoading.hasClass('hidden')).toBe(true);
expect(customMetrics.$monitoredCustomMetricsEmpty.hasClass('hidden')).toEqual(false);
expect(customMetrics.$monitoredCustomMetricsList.hasClass('hidden')).toBe(true);
expect(customMetrics.$monitoredCustomMetricsNoIntegrationText.hasClass('hidden')).toBe(true);
expect(customMetrics.$newCustomMetricButton.hasClass('hidden')).toEqual(false);
expect(customMetrics.$newCustomMetricText.hasClass('hidden')).toEqual(false);
});
it('should show monitored metrics list', () => {
customMetrics.customMetrics = metrics;
customMetrics.populateCustomMetrics();
const $metricsListLi = customMetrics.$monitoredCustomMetricsList.find('li');
expect(customMetrics.$monitoredCustomMetricsLoading.hasClass('hidden')).toBe(true);
expect(customMetrics.$monitoredCustomMetricsList.hasClass('hidden')).toEqual(false);
expect(customMetrics.$monitoredCustomMetricsNoIntegrationText.hasClass('hidden')).toBe(true);
expect(customMetrics.$newCustomMetricButton.hasClass('hidden')).toEqual(false);
expect(customMetrics.$newCustomMetricText.hasClass('hidden')).toBe(true);
expect($metricsListLi.length).toEqual(metrics.length);
});
it('should show the NO-INTEGRATION empty state', () => {
customMetrics.setNoIntegrationActiveState();
expect(customMetrics.$monitoredCustomMetricsEmpty.hasClass('hidden')).toEqual(false);
expect(customMetrics.$monitoredCustomMetricsNoIntegrationText.hasClass('hidden')).toEqual(
false,
);
expect(customMetrics.$monitoredCustomMetricsLoading.hasClass('hidden')).toBe(true);
expect(customMetrics.$monitoredCustomMetricsList.hasClass('hidden')).toBe(true);
expect(customMetrics.$newCustomMetricButton.hasClass('hidden')).toBe(true);
expect(customMetrics.$newCustomMetricText.hasClass('hidden')).toBe(true);
});
});
});

View File

@ -1,62 +0,0 @@
export const metrics1 = [
{
edit_path: '/root/prometheus-test/prometheus/metrics/3/edit',
id: 3,
title: 'Requests',
group: 'Business',
},
{
edit_path: '/root/prometheus-test/prometheus/metrics/2/edit',
id: 2,
title: 'Sales by the hour',
group: 'Business',
},
{
edit_path: '/root/prometheus-test/prometheus/metrics/1/edit',
id: 1,
title: 'Requests',
group: 'Business',
},
];
export const metrics2 = [
{
group: 'Kubernetes',
priority: 1,
active_metrics: 4,
metrics_missing_requirements: 0,
},
{
group: 'HAProxy',
priority: 2,
active_metrics: 3,
metrics_missing_requirements: 0,
},
{
group: 'Apache',
priority: 3,
active_metrics: 5,
metrics_missing_requirements: 0,
},
];
export const missingVarMetrics = [
{
group: 'Kubernetes',
priority: 1,
active_metrics: 4,
metrics_missing_requirements: 0,
},
{
group: 'HAProxy',
priority: 2,
active_metrics: 3,
metrics_missing_requirements: 1,
},
{
group: 'Apache',
priority: 3,
active_metrics: 5,
metrics_missing_requirements: 3,
},
];

View File

@ -1,176 +0,0 @@
import MockAdapter from 'axios-mock-adapter';
import prometheusIntegration from 'test_fixtures/integrations/prometheus/prometheus_integration.html';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import PANEL_STATE from '~/prometheus_metrics/constants';
import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
import { metrics2 as metrics, missingVarMetrics } from './mock_data';
describe('PrometheusMetrics', () => {
beforeEach(() => {
setHTMLFixture(prometheusIntegration);
});
describe('constructor', () => {
let prometheusMetrics;
beforeEach(() => {
prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
});
afterEach(() => {
resetHTMLFixture();
});
it('should initialize wrapper element refs on class object', () => {
expect(prometheusMetrics.$wrapper).toBeDefined();
expect(prometheusMetrics.$monitoredMetricsPanel).toBeDefined();
expect(prometheusMetrics.$monitoredMetricsCount).toBeDefined();
expect(prometheusMetrics.$monitoredMetricsLoading).toBeDefined();
expect(prometheusMetrics.$monitoredMetricsEmpty).toBeDefined();
expect(prometheusMetrics.$monitoredMetricsList).toBeDefined();
expect(prometheusMetrics.$missingEnvVarPanel).toBeDefined();
expect(prometheusMetrics.$panelToggleRight).toBeDefined();
expect(prometheusMetrics.$panelToggleDown).toBeDefined();
expect(prometheusMetrics.$missingEnvVarMetricCount).toBeDefined();
expect(prometheusMetrics.$missingEnvVarMetricsList).toBeDefined();
});
it('should initialize metadata on class object', () => {
expect(prometheusMetrics.backOffRequestCounter).toEqual(0);
expect(prometheusMetrics.activeMetricsEndpoint).toContain('/test');
});
});
describe('showMonitoringMetricsPanelState', () => {
let prometheusMetrics;
beforeEach(() => {
prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
});
it('should show loading state when called with `loading`', () => {
prometheusMetrics.showMonitoringMetricsPanelState(PANEL_STATE.LOADING);
expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBe(false);
expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBe(true);
expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBe(true);
});
it('should show metrics list when called with `list`', () => {
prometheusMetrics.showMonitoringMetricsPanelState(PANEL_STATE.LIST);
expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBe(true);
expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBe(true);
expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBe(false);
});
it('should show empty state when called with `empty`', () => {
prometheusMetrics.showMonitoringMetricsPanelState(PANEL_STATE.EMPTY);
expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBe(true);
expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBe(false);
expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBe(true);
});
});
describe('populateActiveMetrics', () => {
let prometheusMetrics;
beforeEach(() => {
prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
});
it('should show monitored metrics list', () => {
prometheusMetrics.populateActiveMetrics(metrics);
const $metricsListLi = prometheusMetrics.$monitoredMetricsList.find('li');
expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBe(true);
expect(prometheusMetrics.$monitoredMetricsList.hasClass('hidden')).toBe(false);
expect(prometheusMetrics.$monitoredMetricsCount.text()).toEqual(
'3 exporters with 12 metrics were found',
);
expect($metricsListLi.length).toEqual(metrics.length);
expect($metricsListLi.first().find('.badge').text()).toEqual(`${metrics[0].active_metrics}`);
});
it('should show missing environment variables list', () => {
prometheusMetrics.populateActiveMetrics(missingVarMetrics);
expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBe(true);
expect(prometheusMetrics.$missingEnvVarPanel.hasClass('hidden')).toBe(false);
expect(prometheusMetrics.$missingEnvVarMetricCount.text()).toEqual('2');
expect(prometheusMetrics.$missingEnvVarPanel.find('li').length).toEqual(2);
expect(prometheusMetrics.$missingEnvVarPanel.find('.flash-container')).toBeDefined();
});
});
describe('loadActiveMetrics', () => {
let prometheusMetrics;
let mock;
function mockSuccess() {
mock.onGet(prometheusMetrics.activeMetricsEndpoint).reply(HTTP_STATUS_OK, {
data: metrics,
success: true,
});
}
function mockError() {
mock.onGet(prometheusMetrics.activeMetricsEndpoint).networkError();
}
beforeEach(() => {
jest.spyOn(axios, 'get');
prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
mock = new MockAdapter(axios);
});
afterEach(() => {
mock.restore();
});
it('should show loader animation while response is being loaded and hide it when request is complete', async () => {
mockSuccess();
prometheusMetrics.loadActiveMetrics();
expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBe(false);
expect(axios.get).toHaveBeenCalledWith(prometheusMetrics.activeMetricsEndpoint);
await waitForPromises();
expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBe(true);
});
it('should show empty state if response failed to load', async () => {
mockError();
prometheusMetrics.loadActiveMetrics();
await waitForPromises();
expect(prometheusMetrics.$monitoredMetricsLoading.hasClass('hidden')).toBe(true);
expect(prometheusMetrics.$monitoredMetricsEmpty.hasClass('hidden')).toBe(false);
});
it('should populate metrics list once response is loaded', async () => {
jest.spyOn(prometheusMetrics, 'populateActiveMetrics').mockImplementation();
mockSuccess();
prometheusMetrics.loadActiveMetrics();
await waitForPromises();
expect(prometheusMetrics.populateActiveMetrics).toHaveBeenCalledWith(metrics);
});
});
});

View File

@ -38,11 +38,11 @@ RSpec.describe Import::SourceUser, type: :model, feature_category: :importers do
describe '.awaiting_reassignment' do
it 'only returns source users that await reassignment' do
namespace = create(:namespace)
pending_assignment_user = create(:import_source_user, :pending_assignment, namespace: namespace)
pending_reassignment_user = create(:import_source_user, :pending_reassignment, namespace: namespace)
awaiting_approval_user = create(:import_source_user, :awaiting_approval, namespace: namespace)
expect(namespace.import_source_users.awaiting_reassignment)
.to match_array([pending_assignment_user, awaiting_approval_user])
.to match_array([pending_reassignment_user, awaiting_approval_user])
end
end

View File

@ -148,6 +148,10 @@ RSpec.configure do |config|
metadata[:type] = :feature
end
config.define_derived_metadata(file_path: %r{spec/dot_gitlab_ci/}) do |metadata|
metadata[:ci_config_validation] = true
end
config.include LicenseHelpers
config.include ActiveJob::TestHelper
config.include ActiveSupport::Testing::TimeHelpers
@ -395,6 +399,11 @@ RSpec.configure do |config|
example.run if config.inclusion_filter[:quarantine] || !ENV['CI']
end
config.around(:example, :ci_config_validation) do |example|
# Skip tests for ci config validation unless we explicitly focus on them or not in CI
example.run if config.inclusion_filter[:ci_config_validation] || !ENV['CI']
end
config.around(:example, :request_store) do |example|
::Gitlab::SafeRequestStore.ensure_request_store { example.run }
end

View File

@ -37,10 +37,6 @@ mapping:
- source: 'rubocop/(?<rest>.+)\.rb'
test: 'spec/rubocop/%{rest}_spec.rb'
# .gitlab/ci related specs
- source: '.gitlab/ci/(?<rest>.+)\.gitlab-ci\.yml'
test: 'spec/dot_gitlab_ci/%{rest}_spec.rb'
# Map config to respective specs
- source: 'config/(?<rest>.+)\..*'
test: 'spec/config/%{rest}_spec.rb'

View File

@ -0,0 +1,5 @@
include:
- local: gems/gem.gitlab-ci.yml
inputs:
gem_name: "gitlab-topology-service-client"
gem_path_prefix: "vendor/gems/"

View File

@ -0,0 +1 @@
* @gitlab-org/tenant-scale-group/backend-engineers

View File

@ -0,0 +1,8 @@
# Topology Service Client Gem
This Ruby Gem is meant to initialize a client to the Cells Topology Service.
This Gem is automatically generated via GRPC from the
repository https://gitlab.com/gitlab-org/cells/topology-service
For more information about the Topology Service:
https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/cells/topology_service/

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
source "https://rubygems.org"
# Specify your gem's dependencies in ruby.gemspec
gemspec

View File

@ -0,0 +1,137 @@
PATH
remote: .
specs:
gitlab-topology-service-client (0.1)
grpc
GEM
remote: https://rubygems.org/
specs:
activesupport (7.1.3.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.8)
binding_of_caller (1.0.1)
debug_inspector (>= 1.2.0)
concurrent-ruby (1.2.3)
connection_pool (2.4.1)
debug_inspector (1.2.0)
diff-lcs (1.5.1)
drb (2.2.1)
gitlab-styles (10.1.0)
rubocop (~> 1.50.2)
rubocop-graphql (~> 0.18)
rubocop-performance (~> 1.15)
rubocop-rails (~> 2.17)
rubocop-rspec (~> 2.22)
google-protobuf (3.25.3)
googleapis-common-protos-types (1.14.0)
google-protobuf (~> 3.18)
grpc (1.63.0)
google-protobuf (~> 3.25)
googleapis-common-protos-types (~> 1.0)
i18n (1.14.5)
concurrent-ruby (~> 1.0)
json (2.7.1)
minitest (5.22.3)
mutex_m (0.2.0)
parallel (1.24.0)
parser (3.3.0.5)
ast (~> 2.4.1)
racc
proc_to_ast (0.2.0)
parser
rouge
unparser
racc (1.7.3)
rack (3.0.11)
rainbow (3.1.1)
regexp_parser (2.9.0)
rexml (3.2.6)
rouge (4.3.0)
rspec (3.13.0)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.0)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-parameterized (1.0.2)
rspec-parameterized-core (< 2)
rspec-parameterized-table_syntax (< 2)
rspec-parameterized-core (1.0.1)
parser
proc_to_ast (>= 0.2.0)
rspec (>= 2.13, < 4)
unparser
rspec-parameterized-table_syntax (1.0.1)
binding_of_caller
rspec-parameterized-core (< 2)
rspec-support (3.13.0)
rubocop (1.50.2)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.2.0.0)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-capybara (2.20.0)
rubocop (~> 1.41)
rubocop-factory_bot (2.25.1)
rubocop (~> 1.41)
rubocop-graphql (0.19.0)
rubocop (>= 0.87, < 2)
rubocop-performance (1.20.2)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-rails (2.23.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-rspec (2.29.2)
rubocop (~> 1.40)
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
rubocop-rspec_rails (~> 2.28)
rubocop-rspec_rails (2.28.3)
rubocop (~> 1.40)
ruby-progressbar (1.13.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
unparser (0.6.15)
diff-lcs (~> 1.3)
parser (>= 3.3.0)
PLATFORMS
ruby
DEPENDENCIES
gitlab-styles (~> 10.1.0)
gitlab-topology-service-client!
rspec (~> 3.0)
rspec-parameterized (~> 1.0.2)
rubocop (~> 1.21)
BUNDLED WITH
2.4.4

View File

@ -0,0 +1,24 @@
# frozen_string_literal: true
require_relative "lib/gitlab/cells/topology_service/version"
Gem::Specification.new do |spec|
spec.name = "gitlab-topology-service-client"
spec.version = Gitlab::Cells::TopologyService::VERSION
spec.authors = ["group::tenant scale"]
spec.email = ["engineering@gitlab.com"]
spec.summary = "Client library to interact with Topology Service for GitLab Cells architecture"
spec.homepage = "https://gitlab.com/gitlab-org/cells/topology-service"
spec.license = "MIT"
spec.required_ruby_version = ">= 2.6.0"
spec.files = Dir['lib/**/*.rb']
spec.require_paths = ["lib"]
spec.add_dependency "grpc"
spec.add_development_dependency "gitlab-styles", "~> 10.1.0"
spec.add_development_dependency "rspec", "~> 3.0"
spec.add_development_dependency "rspec-parameterized", "~> 1.0.2"
spec.add_development_dependency "rubocop", "~> 1.21"
end

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
require_relative "topology_service/version"
require "proto/claim_service_services_pb"
require "proto/classify_service_services_pb"
require "proto/health_service_services_pb"
require "proto/sequence_service_services_pb"

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
module Gitlab
module Cells
module TopologyService
VERSION = "0.1"
end
end
end

View File

@ -0,0 +1,30 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: proto/cell_info.proto
require 'google/protobuf'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_file("proto/cell_info.proto", :syntax => :proto3) do
add_message "gitlab.cells.topology_service.CellInfo" do
optional :id, :int64, 1
optional :name, :string, 2
optional :address, :string, 3
optional :session_prefix, :string, 4
end
add_message "gitlab.cells.topology_service.GetCellsRequest" do
end
add_message "gitlab.cells.topology_service.GetCellsResponse" do
repeated :cells, :message, 1, "gitlab.cells.topology_service.CellInfo"
end
end
end
module Gitlab
module Cells
module TopologyService
CellInfo = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.CellInfo").msgclass
GetCellsRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.GetCellsRequest").msgclass
GetCellsResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.GetCellsResponse").msgclass
end
end
end

View File

@ -0,0 +1,69 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: proto/claim_service.proto
require 'google/protobuf'
require 'proto/cell_info_pb'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_file("proto/claim_service.proto", :syntax => :proto3) do
add_message "gitlab.cells.topology_service.ClaimRecord" do
optional :bucket, :enum, 1, "gitlab.cells.topology_service.ClaimRecord.Bucket"
optional :value, :string, 2
end
add_enum "gitlab.cells.topology_service.ClaimRecord.Bucket" do
value :Unknown, 0
value :Routes, 1
end
add_message "gitlab.cells.topology_service.ParentRecord" do
optional :model, :enum, 1, "gitlab.cells.topology_service.ParentRecord.ApplicationModel"
optional :id, :int64, 2
end
add_enum "gitlab.cells.topology_service.ParentRecord.ApplicationModel" do
value :Unknown, 0
value :Group, 1
value :Project, 2
value :UserNamespace, 3
end
add_message "gitlab.cells.topology_service.OwnerRecord" do
optional :table, :enum, 1, "gitlab.cells.topology_service.OwnerRecord.Table"
optional :id, :int64, 2
end
add_enum "gitlab.cells.topology_service.OwnerRecord.Table" do
value :Unknown, 0
value :routes, 1
end
add_message "gitlab.cells.topology_service.ClaimDetails" do
optional :claim, :message, 1, "gitlab.cells.topology_service.ClaimRecord"
optional :parent, :message, 2, "gitlab.cells.topology_service.ParentRecord"
optional :owner, :message, 3, "gitlab.cells.topology_service.OwnerRecord"
end
add_message "gitlab.cells.topology_service.ClaimInfo" do
optional :id, :int64, 1
optional :details, :message, 2, "gitlab.cells.topology_service.ClaimDetails"
proto3_optional :cell_info, :message, 3, "gitlab.cells.topology_service.CellInfo"
end
add_message "gitlab.cells.topology_service.CreateClaimRequest" do
optional :details, :message, 1, "gitlab.cells.topology_service.ClaimDetails"
end
add_message "gitlab.cells.topology_service.CreateClaimResponse" do
optional :claim, :message, 1, "gitlab.cells.topology_service.ClaimInfo"
end
end
end
module Gitlab
module Cells
module TopologyService
ClaimRecord = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ClaimRecord").msgclass
ClaimRecord::Bucket = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ClaimRecord.Bucket").enummodule
ParentRecord = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ParentRecord").msgclass
ParentRecord::ApplicationModel = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ParentRecord.ApplicationModel").enummodule
OwnerRecord = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.OwnerRecord").msgclass
OwnerRecord::Table = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.OwnerRecord.Table").enummodule
ClaimDetails = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ClaimDetails").msgclass
ClaimInfo = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ClaimInfo").msgclass
CreateClaimRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.CreateClaimRequest").msgclass
CreateClaimResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.CreateClaimResponse").msgclass
end
end
end

View File

@ -0,0 +1,28 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# Source: proto/claim_service.proto for package 'Gitlab.Cells.TopologyService'
require 'grpc'
require 'proto/claim_service_pb'
module Gitlab
module Cells
module TopologyService
module ClaimService
# Restricted read-write service to claim global uniqueness on resources
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'gitlab.cells.topology_service.ClaimService'
rpc :GetCells, Gitlab::Cells::TopologyService::GetCellsRequest, Gitlab::Cells::TopologyService::GetCellsResponse
rpc :CreateClaim, Gitlab::Cells::TopologyService::CreateClaimRequest, Gitlab::Cells::TopologyService::CreateClaimResponse
end
Stub = Service.rpc_stub_class
end
end
end
end

View File

@ -0,0 +1,43 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: proto/classify_service.proto
require 'google/protobuf'
require 'proto/cell_info_pb'
require 'google/api/annotations_pb'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_file("proto/classify_service.proto", :syntax => :proto3) do
add_message "gitlab.cells.topology_service.ClassifyRequest" do
optional :type, :enum, 2, "gitlab.cells.topology_service.ClassifyType"
optional :value, :string, 3
end
add_message "gitlab.cells.topology_service.ProxyInfo" do
optional :address, :string, 1
end
add_message "gitlab.cells.topology_service.ClassifyResponse" do
optional :action, :enum, 1, "gitlab.cells.topology_service.ClassifyAction"
optional :proxy, :message, 2, "gitlab.cells.topology_service.ProxyInfo"
end
add_enum "gitlab.cells.topology_service.ClassifyType" do
value :UnknownType, 0
value :FirstCell, 1
value :SessionPrefix, 2
end
add_enum "gitlab.cells.topology_service.ClassifyAction" do
value :UnknownAction, 0
value :Proxy, 1
end
end
end
module Gitlab
module Cells
module TopologyService
ClassifyRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ClassifyRequest").msgclass
ProxyInfo = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ProxyInfo").msgclass
ClassifyResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ClassifyResponse").msgclass
ClassifyType = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ClassifyType").enummodule
ClassifyAction = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ClassifyAction").enummodule
end
end
end

View File

@ -0,0 +1,28 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# Source: proto/classify_service.proto for package 'Gitlab.Cells.TopologyService'
require 'grpc'
require 'proto/classify_service_pb'
module Gitlab
module Cells
module TopologyService
module ClassifyService
# Public read-only service used by various Routing Services
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'gitlab.cells.topology_service.ClassifyService'
rpc :GetCells, Gitlab::Cells::TopologyService::GetCellsRequest, Gitlab::Cells::TopologyService::GetCellsResponse
rpc :Classify, Gitlab::Cells::TopologyService::ClassifyRequest, Gitlab::Cells::TopologyService::ClassifyResponse
end
Stub = Service.rpc_stub_class
end
end
end
end

View File

@ -0,0 +1,29 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: proto/health_service.proto
require 'google/protobuf'
require 'google/api/annotations_pb'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_file("proto/health_service.proto", :syntax => :proto3) do
add_message "gitlab.cells.topology_service.ReadinessProbeRequest" do
end
add_message "gitlab.cells.topology_service.ReadinessProbeResponse" do
end
add_message "gitlab.cells.topology_service.LivenessProbeRequest" do
end
add_message "gitlab.cells.topology_service.LivenessProbeResponse" do
end
end
end
module Gitlab
module Cells
module TopologyService
ReadinessProbeRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ReadinessProbeRequest").msgclass
ReadinessProbeResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.ReadinessProbeResponse").msgclass
LivenessProbeRequest = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.LivenessProbeRequest").msgclass
LivenessProbeResponse = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("gitlab.cells.topology_service.LivenessProbeResponse").msgclass
end
end
end

View File

@ -0,0 +1,30 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# Source: proto/health_service.proto for package 'Gitlab.Cells.TopologyService'
require 'grpc'
require 'proto/health_service_pb'
module Gitlab
module Cells
module TopologyService
module HealthService
# Public read-only service
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'gitlab.cells.topology_service.HealthService'
# (Lightweight) Used for checking if application is ready to accept requests
rpc :ReadinessProbe, Gitlab::Cells::TopologyService::ReadinessProbeRequest, Gitlab::Cells::TopologyService::ReadinessProbeResponse
# (Lightweight) Used for checking if application is alive, or whether it should be restarted
rpc :LivenessProbe, Gitlab::Cells::TopologyService::LivenessProbeRequest, Gitlab::Cells::TopologyService::LivenessProbeResponse
end
Stub = Service.rpc_stub_class
end
end
end
end

View File

@ -0,0 +1,17 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: proto/sequence_service.proto
require 'google/protobuf'
require 'proto/cell_info_pb'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_file("proto/sequence_service.proto", :syntax => :proto3) do
end
end
module Gitlab
module Cells
module TopologyService
end
end
end

View File

@ -0,0 +1,27 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# Source: proto/sequence_service.proto for package 'Gitlab.Cells.TopologyService'
require 'grpc'
require 'proto/sequence_service_pb'
module Gitlab
module Cells
module TopologyService
module SequenceService
# Restricted read-write service to provide cluster-wide primary key uniqueness
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = 'gitlab.cells.topology_service.SequenceService'
rpc :GetCells, Gitlab::Cells::TopologyService::GetCellsRequest, Gitlab::Cells::TopologyService::GetCellsResponse
end
Stub = Service.rpc_stub_class
end
end
end
end

View File

@ -1310,10 +1310,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/at.js/-/at.js-1.5.7.tgz#1ee6f838cc4410a1d797770934df91d90df8179e"
integrity sha512-c6ySRK/Ma7lxwpIVbSAF3P+xiTLrNTGTLRx4/pHK111AdFxwgUwrYF6aVZFXvmG65jHOJHoa0eQQ21RW6rm0Rg==
"@gitlab/cluster-client@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@gitlab/cluster-client/-/cluster-client-2.2.0.tgz#418a1783d8a6b38624d3f446f55b7a5c737cae73"
integrity sha512-57k5U1dDXSvIvbveKkRccNDl3qliDp+dfaJAPktL+V6GUMleoCbsx9pXselW4RK9YoHh+Zx9afIkczlR0TUv2Q==
"@gitlab/cluster-client@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@gitlab/cluster-client/-/cluster-client-2.2.1.tgz#9d14336d653c1df92d2d764a7eff74b5a3a43119"
integrity sha512-YtpSfFxro0q/YRhBuZWiSfHQ44JyTfuwQgE8F77+0v5xOuvoeXR81mmJzF6ByejKVAee9auvjdd4eHSUQ5MqsQ==
dependencies:
axios "^0.24.0"
core-js "^3.29.1"