Commit d50f7c44 authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent b322483cd738
......@@ -409,7 +409,6 @@ linters:
- 'ee/app/views/projects/push_rules/_index.html.haml'
- 'ee/app/views/projects/services/gitlab_slack_application/_help.html.haml'
- 'ee/app/views/projects/services/gitlab_slack_application/_slack_integration_form.html.haml'
- 'ee/app/views/projects/services/prometheus/_metrics.html.haml'
- 'ee/app/views/projects/settings/slacks/edit.html.haml'
- 'ee/app/views/shared/_additional_email_text.html.haml'
- 'ee/app/views/shared/_mirror_update_button.html.haml'
......
<script>
import getJiraProjects from '../queries/getJiraProjects.query.graphql';
import JiraImportForm from './jira_import_form.vue';
import JiraImportSetup from './jira_import_setup.vue';
export default {
name: 'JiraImportApp',
components: {
JiraImportForm,
JiraImportSetup,
},
props: {
......@@ -41,6 +43,6 @@ export default {
<template>
<div>
<jira-import-setup v-if="!isJiraConfigured" :illustration="setupIllustration" />
<div v-else></div>
<jira-import-form v-else />
</div>
</template>
<script>
import { GlAvatar, GlNewButton, GlFormGroup, GlFormSelect, GlLabel } from '@gitlab/ui';
export default {
name: 'JiraImportForm',
components: {
GlAvatar,
GlNewButton,
GlFormGroup,
GlFormSelect,
GlLabel,
},
currentUserAvatarUrl: gon.current_user_avatar_url,
currentUsername: gon.current_username,
};
</script>
<template>
<div>
<h3 class="page-title">{{ __('New Jira import') }}</h3>
<hr />
<form>
<gl-form-group
class="row align-items-center"
:label="__('Import from')"
label-cols-sm="2"
label-for="jira-project-select"
>
<gl-form-select id="jira-project-select" class="mb-2" />
</gl-form-group>
<gl-form-group
class="row align-items-center"
:label="__('Issue label')"
label-cols-sm="2"
label-for="jira-project-label"
>
<gl-label
id="jira-project-label"
class="mb-2"
background-color="#428BCA"
title="jira-import::KEY-1"
scoped
/>
</gl-form-group>
<hr />
<p class="offset-md-1">
{{
__(
"For each Jira issue successfully imported, we'll create a new GitLab issue with the following data:",
)
}}
</p>
<gl-form-group
class="row align-items-center mb-1"
:label="__('Title')"
label-cols-sm="2"
label-for="jira-project-title"
>
<p id="jira-project-title" class="mb-2">{{ __('jira.issue.summary') }}</p>
</gl-form-group>
<gl-form-group
class="row align-items-center mb-1"
:label="__('Reporter')"
label-cols-sm="2"
label-for="jira-project-reporter"
>
<gl-avatar
id="jira-project-reporter"
class="mb-2"
:src="$options.currentUserAvatarUrl"
:size="24"
:aria-label="$options.currentUsername"
/>
</gl-form-group>
<gl-form-group
class="row align-items-center mb-1"
:label="__('Description')"
label-cols-sm="2"
label-for="jira-project-description"
>
<p id="jira-project-description" class="mb-2">{{ __('jira.issue.description.content') }}</p>
</gl-form-group>
<div class="footer-block row-content-block d-flex justify-content-between">
<gl-new-button category="primary" variant="success">{{ __('Next') }}</gl-new-button>
<gl-new-button>{{ __('Cancel') }}</gl-new-button>
</div>
</form>
</div>
</template>
import IntegrationSettingsForm from '~/integrations/integration_settings_form';
import PrometheusMetrics from 'ee_else_ce/prometheus_metrics/prometheus_metrics';
import PrometheusMetrics from '~/prometheus_metrics/custom_metrics';
import PrometheusAlerts from '~/prometheus_alerts';
import initAlertsSettings from '~/alerts_service_settings';
......
......@@ -2,4 +2,5 @@ export default {
EMPTY: 'empty',
LOADING: 'loading',
LIST: 'list',
NO_INTEGRATION: 'no-integration',
};
import $ from 'jquery';
import { escape, sortBy } from 'lodash';
import PrometheusMetrics from './prometheus_metrics';
import PANEL_STATE from './constants';
import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
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>
`;
}
}
......@@ -41,6 +41,23 @@ def render_cluster_info_tab_content(tab, expanded)
end
end
def cluster_type_label(cluster_type)
case cluster_type
when 'project_type'
s_('ClusterIntegration|Project cluster')
when 'group_type'
s_('ClusterIntegration|Group cluster')
when 'instance_type'
s_('ClusterIntegration|Instance cluster')
else
Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
ArgumentError.new('Cluster Type Missing'),
cluster_error: { error: 'Cluster Type Missing', cluster_type: cluster_type }
)
_('Cluster')
end
end
def has_rbac_enabled?(cluster)
return cluster.platform_kubernetes_rbac? if cluster.platform_kubernetes
......
......@@ -4,7 +4,7 @@ module PerformanceMonitoring
class PrometheusPanel
include ActiveModel::Model
attr_accessor :type, :title, :y_label, :weight, :metrics
attr_accessor :type, :title, :y_label, :weight, :metrics, :y_axis
validates :title, presence: true
validates :metrics, presence: true
......@@ -20,5 +20,9 @@ def self.from_json(json_content)
panel.tap(&:validate!)
end
def id(group_title)
Digest::SHA2.hexdigest([group_title, type, title].join)
end
end
end
......@@ -11,6 +11,7 @@ class BaseService < ::BaseService
SEQUENCE = [
STAGES::CommonMetricsInserter,
STAGES::EndpointInserter,
STAGES::PanelIdsInserter,
STAGES::Sorter
].freeze
......
......@@ -10,7 +10,10 @@ module Dashboard
class GitlabAlertEmbedService < ::Metrics::Dashboard::BaseEmbedService
include Gitlab::Utils::StrongMemoize
SEQUENCE = [STAGES::EndpointInserter].freeze
SEQUENCE = [
STAGES::EndpointInserter,
STAGES::PanelIdsInserter
].freeze
class << self
# Determines whether the provided params are sufficient
......
......@@ -10,7 +10,8 @@ class GrafanaMetricEmbedService < ::Metrics::Dashboard::BaseEmbedService
include ReactiveCaching
SEQUENCE = [
::Gitlab::Metrics::Dashboard::Stages::GrafanaFormatter
::Gitlab::Metrics::Dashboard::Stages::GrafanaFormatter,
::Gitlab::Metrics::Dashboard::Stages::PanelIdsInserter
].freeze
self.reactive_cache_key = ->(service) { service.cache_key }
......
......@@ -11,6 +11,7 @@ class PredefinedDashboardService < ::Metrics::Dashboard::BaseService
SEQUENCE = [
STAGES::EndpointInserter,
STAGES::PanelIdsInserter,
STAGES::Sorter
].freeze
......
......@@ -11,6 +11,7 @@ class SelfMonitoringDashboardService < ::Metrics::Dashboard::PredefinedDashboard
SEQUENCE = [
STAGES::CustomMetricsInserter,
STAGES::EndpointInserter,
STAGES::PanelIdsInserter,
STAGES::Sorter
].freeze
......
......@@ -13,6 +13,7 @@ class SystemDashboardService < ::Metrics::Dashboard::PredefinedDashboardService
STAGES::CustomMetricsInserter,
STAGES::CustomMetricsDetailsInserter,
STAGES::EndpointInserter,
STAGES::PanelIdsInserter,
STAGES::Sorter
].freeze
......
......@@ -4,7 +4,7 @@ module Tags
class CreateService < BaseService
def execute(tag_name, target, message)
valid_tag = Gitlab::GitRefValidator.validate(tag_name)
return error('Tag name invalid') unless valid_tag
return error('Tag name invalid', 400) unless valid_tag
repository = project.repository
message = message&.strip
......@@ -14,7 +14,7 @@ def execute(tag_name, target, message)
begin
new_tag = repository.add_tag(current_user, tag_name, target, message)
rescue Gitlab::Git::Repository::TagExistsError
return error("Tag #{tag_name} already exists")
return error("Tag #{tag_name} already exists", 409)
rescue Gitlab::Git::PreReceiveError => ex
return error(ex.message)
end
......@@ -24,7 +24,7 @@ def execute(tag_name, target, message)
success.merge(tag: new_tag)
else
error("Target #{target} is invalid")
error("Target #{target} is invalid", 400)
end
end
end
......
......@@ -41,7 +41,12 @@
.js-serverless-survey-banner{ data: { user_name: current_user.name, user_email: current_user.email } }
%h4= @cluster.name
.d-flex.my-3
%p.badge.badge-light.p-2.mr-2
= cluster_type_label(@cluster.cluster_type)
%h4.m-0
= @cluster.name
= render 'banner'
- if cluster_created?(@cluster)
......@@ -56,7 +61,3 @@
.tab-content.py-3
.tab-pane.active{ role: 'tabpanel' }
= render_cluster_info_tab_content(params[:tab], expanded_by_default?)
- project = local_assigns.fetch(:project)
.col-lg-3
%p
= s_('PrometheusService|Custom metrics require Prometheus installed on a cluster with environment scope "*" OR a manually configured Prometheus to be available.')
= link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus', anchor: 'adding-custom-metrics'), target: '_blank', rel: "noopener noreferrer"
.col-lg-9
.card.custom-monitored-metrics.js-panel-custom-monitored-metrics{ data: { active_custom_metrics: project_prometheus_metrics_path(project), environments_data: environments_list_data, service_active: "#{@service.active}" } }
.card-header
%strong
= s_('PrometheusService|Custom metrics')
-# haml-lint:disable NoPlainNodes
%span.badge.badge-pill.js-custom-monitored-count 0
-# haml-lint:enable NoPlainNodes
= link_to s_('PrometheusService|New metric'), new_project_prometheus_metric_path(project), class: 'btn btn-success js-new-metric-button hidden'
.card-body
.flash-container.hidden
.flash-warning
.flash-text
.loading-metrics.js-loading-custom-metrics
%p.m-3
= icon('spinner spin', class: 'metrics-load-spinner')
= s_('PrometheusService|Finding custom metrics...')
.empty-metrics.hidden.js-empty-custom-metrics
%p.text-tertiary.m-3.js-no-active-integration-text.hidden
= s_('PrometheusService|Enable Prometheus to define custom metrics, using either option above')
%p.text-tertiary.m-3.js-new-metric-text.hidden
= s_('PrometheusService|No custom metrics have been created. Create one using the button above')
%ul.list-unstyled.metrics-list.hidden.js-custom-metrics-list
- project = local_assigns.fetch(:project)
= render 'projects/services/prometheus/custom_metrics', project: project
.col-lg-3
%p
= s_('PrometheusService|Common metrics are automatically monitored based on a library of metrics from popular exporters.')
......
---
title: Fix 500 error on create release API when providing an invalid tag_name
merge_request: 28969
author: Sashi Kumar
type: fixed
---
title: Automatically assign id to each panel within dashboard to support panel scoped annotations
merge_request: 28341
author:
type: added
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment