Commit 3ca1afc1 authored by Filipa Lacerda's avatar Filipa Lacerda

Merge branch '9258-support-alerts-from-external-prometheus-servers-frontend' into 'master'

Resolve "Support alerts from external Prometheus servers" ~frontend

Closes #9258

See merge request gitlab-org/gitlab-ee!9239
parents 85520a43 9b40298c
......@@ -8,3 +8,5 @@
.col-lg-9
= render 'projects/services/prometheus/metrics', project: @project
= render_if_exists 'projects/services/prometheus/external_alerts', project: @project
import IntegrationSettingsForm from '~/integrations/integration_settings_form';
import PrometheusMetrics from 'ee/prometheus_metrics/prometheus_metrics';
import PrometheusAlerts from 'ee/prometheus_alerts';
document.addEventListener('DOMContentLoaded', () => {
const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
......@@ -14,4 +15,6 @@ document.addEventListener('DOMContentLoaded', () => {
prometheusMetrics.setNoIntegrationActiveState();
}
}
PrometheusAlerts();
});
<script>
import { GlButton, GlFormGroup, GlFormInput, GlModal, GlModalDirective } from '@gitlab/ui';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale';
import createFlash from '~/flash';
export default {
copyToClipboard: __('Copy to clipboard'),
components: {
GlButton,
GlFormGroup,
GlFormInput,
GlModal,
ClipboardButton,
},
directives: {
'gl-modal': GlModalDirective,
},
props: {
initialAuthorizationKey: {
type: String,
required: false,
default: '',
},
changeKeyUrl: {
type: String,
required: true,
},
notifyUrl: {
type: String,
required: true,
},
learnMoreUrl: {
type: String,
required: true,
},
},
data() {
return {
authorizationKey: this.initialAuthorizationKey,
sectionDescription: sprintf(
__(
'To receive alerts from manually configured Prometheus services, add the following URL and Authorization key to your Prometheus webhook config file. Learn more about %{linkStart}configuring Prometheus%{linkEnd} to send alerts to GitLab.',
),
{
linkStart: `<a href="${this.learnMoreUrl}" target="_blank" rel="noopener noreferrer">`,
linkEnd: '</a>',
},
false,
),
};
},
methods: {
resetKey() {
axios
.post(this.changeKeyUrl)
.then(res => {
this.authorizationKey = res.data.token;
})
.catch(() => {
createFlash(__('Failed to reset key. Please try again.'));
});
},
},
};
</script>
<template>
<div class="row py-4 border-top js-prometheus-alerts">
<div class="col-lg-3">
<h4 class="mt-0">
{{ __('Alerts') }}
</h4>
<p>
{{ __('Receive alerts from manually configured Prometheus servers.') }}
</p>
</div>
<div class="col-lg-9">
<p v-html="sectionDescription"></p>
<gl-form-group :label="__('URL')" label-for="notify-url" label-class="label-bold">
<div class="input-group">
<gl-form-input id="notify-url" :readonly="true" :value="notifyUrl" />
<span class="input-group-append">
<clipboard-button :text="notifyUrl" :title="$options.copyToClipboard" />
</span>
</div>
</gl-form-group>
<gl-form-group
:label="__('Authorization key')"
label-for="authorization-key"
label-class="label-bold"
>
<div class="input-group">
<gl-form-input id="authorization-key" :readonly="true" :value="authorizationKey" />
<span class="input-group-append">
<clipboard-button :text="authorizationKey" :title="$options.copyToClipboard" />
</span>
</div>
</gl-form-group>
<template v-if="authorizationKey.length > 0">
<gl-modal
modal-id="authKeyModal"
:title="__('Reset authorization key?')"
:ok-title="__('Reset authorization key')"
ok-variant="danger"
@ok="resetKey"
>
{{
__(
'Resetting the authorization key will invalidate the previous key. Existing alert configurations will need to be updated with the new key.',
)
}}
</gl-modal>
<gl-button v-gl-modal.authKeyModal class="js-reset-auth-key">{{
__('Reset key')
}}</gl-button>
</template>
<gl-button v-else class="js-reset-auth-key" @click="resetKey">{{
__('Generate key')
}}</gl-button>
</div>
</div>
</template>
import Vue from 'vue';
import ResetKey from './components/reset_key.vue';
export default () => {
const el = document.querySelector('#js-settings-prometheus-alerts');
const { authorizationKey, changeKeyUrl, notifyUrl, learnMoreUrl } = el.dataset;
// eslint-disable-next-line no-new
new Vue({
el,
render(createElement) {
return createElement(ResetKey, {
props: {
initialAuthorizationKey: authorizationKey,
changeKeyUrl,
notifyUrl,
learnMoreUrl,
},
});
},
});
};
- return unless can?(current_user, :read_prometheus_alerts, @project)
- notify_url = notify_project_prometheus_alerts_url(@project, format: :json)
- authorization_key = @project.alerting_setting.try(:token)
- learn_more_url = help_page_path('administration/monitoring/prometheus/index', anchor: 'configuring-prometheus')
#js-settings-prometheus-alerts{ data: { notify_url: notify_url, authorization_key: authorization_key, change_key_url: reset_alerting_token_project_settings_operations_path(@project), learn_more_url: learn_more_url } }
import { shallowMount, createLocalVue } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import ResetKey from 'ee/prometheus_alerts/components/reset_key.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import { GlModal } from '@gitlab/ui';
describe('ResetKey', () => {
let Component;
let mock;
let vm;
const localVue = createLocalVue();
const propsData = {
initialAuthorizationKey: 'abcd1234',
changeKeyUrl: '/updateKeyUrl',
notifyUrl: '/root/autodevops-deploy/prometheus/alerts/notify.json',
learnMoreUrl: '/learnMore',
};
beforeEach(() => {
mock = new MockAdapter(axios);
Component = localVue.extend(ResetKey);
setFixtures('<div class="flash-container"></div><div id="reset-key"></div>');
});
afterEach(() => {
mock.restore();
vm.destroy();
});
describe('authorization key exists', () => {
beforeEach(() => {
propsData.initialAuthorizationKey = 'abcd1234';
vm = shallowMount(Component, {
propsData,
});
});
it('shows fields and buttons', () => {
expect(vm.find('#notify-url').attributes('value')).toEqual(propsData.notifyUrl);
expect(vm.find('#authorization-key').attributes('value')).toEqual(
propsData.initialAuthorizationKey,
);
expect(vm.findAll(ClipboardButton).length).toBe(2);
expect(vm.find('.js-reset-auth-key').text()).toEqual('Reset key');
});
it('reset updates key', done => {
mock.onPost(propsData.changeKeyUrl).replyOnce(200, { token: 'newToken' });
vm.find(GlModal).vm.$emit('ok');
setTimeout(() => {
expect(vm.find('#authorization-key').attributes('value')).toEqual('newToken');
done();
});
});
it('reset key failure shows error', done => {
mock.onPost(propsData.changeKeyUrl).replyOnce(500);
vm.find(GlModal).vm.$emit('ok');
setTimeout(() => {
expect(vm.find('#authorization-key').attributes('value')).toEqual(
propsData.initialAuthorizationKey,
);
expect(document.querySelector('.flash-container').innerText.trim()).toEqual(
'Failed to reset key. Please try again.',
);
done();
});
});
});
describe('authorization key has not been set', () => {
beforeEach(() => {
propsData.initialAuthorizationKey = '';
vm = shallowMount(Component, {
propsData,
});
});
it('shows Generate Key button', () => {
expect(vm.find('.js-reset-auth-key').text()).toEqual('Generate key');
expect(vm.find('#authorization-key').attributes('value')).toEqual('');
});
it('Generate key button triggers key change', done => {
mock.onPost(propsData.changeKeyUrl).replyOnce(200, { token: 'newToken' });
vm.find('.js-reset-auth-key').vm.$emit('click');
setTimeout(() => {
expect(vm.find('#authorization-key').attributes('value')).toEqual('newToken');
done();
});
});
});
});
......@@ -607,6 +607,9 @@ msgid_plural "Alerts"
msgstr[0] ""
msgstr[1] ""
msgid "Alerts"
msgstr ""
msgid "All"
msgstr ""
......@@ -1051,6 +1054,9 @@ msgstr ""
msgid "Authorization code:"
msgstr ""
msgid "Authorization key"
msgstr ""
msgid "Authorization was granted by entering your username and password in the application."
msgstr ""
......@@ -3800,6 +3806,9 @@ msgstr ""
msgid "Failed to remove the pipeline schedule"
msgstr ""
msgid "Failed to reset key. Please try again."
msgstr ""
msgid "Failed to signing using smartcard authentication"
msgstr ""
......@@ -4148,6 +4157,9 @@ msgstr ""
msgid "Generate a default set of labels"
msgstr ""
msgid "Generate key"
msgstr ""
msgid "Geo"
msgstr ""
......@@ -7607,6 +7619,9 @@ msgstr ""
msgid "Real-time features"
msgstr ""
msgid "Receive alerts from manually configured Prometheus servers."
msgstr ""
msgid "Recent searches"
msgstr ""
......@@ -7825,12 +7840,24 @@ msgstr ""
msgid "Resend invite"
msgstr ""
msgid "Reset authorization key"
msgstr ""
msgid "Reset authorization key?"
msgstr ""
msgid "Reset health check access token"
msgstr ""
msgid "Reset key"
msgstr ""
msgid "Reset runners registration token"
msgstr ""
msgid "Resetting the authorization key will invalidate the previous key. Existing alert configurations will need to be updated with the new key."
msgstr ""
msgid "Resolve all discussions in new issue"
msgstr ""
......@@ -9765,6 +9792,9 @@ msgstr ""
msgid "To preserve performance only <strong>%{display_size} of %{real_size}</strong> files are displayed."
msgstr ""
msgid "To receive alerts from manually configured Prometheus services, add the following URL and Authorization key to your Prometheus webhook config file. Learn more about %{linkStart}configuring Prometheus%{linkEnd} to send alerts to GitLab."
msgstr ""
msgid "To set up SAML authentication for your group through an identity provider like Azure, Okta, Onelogin, Ping Identity, or your custom SAML 2.0 provider:"
msgstr ""
......@@ -9909,6 +9939,9 @@ msgstr ""
msgid "Type"
msgstr ""
msgid "URL"
msgstr ""
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
......
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