diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml index 9d4574c4590200c5212d8104b359272ee6125af8..6aafa85e99a720bb0d137944f1f7f23ac004605a 100644 --- a/app/views/projects/services/prometheus/_show.html.haml +++ b/app/views/projects/services/prometheus/_show.html.haml @@ -8,3 +8,5 @@ .col-lg-9 = render 'projects/services/prometheus/metrics', project: @project + += render_if_exists 'projects/services/prometheus/external_alerts', project: @project diff --git a/ee/app/assets/javascripts/pages/projects/services/edit/index.js b/ee/app/assets/javascripts/pages/projects/services/edit/index.js index 42139b804ffd9cc6b161db5d251941359f81b59d..9e1a16174b417ec7c3fa92659bb2b76c84bda376 100644 --- a/ee/app/assets/javascripts/pages/projects/services/edit/index.js +++ b/ee/app/assets/javascripts/pages/projects/services/edit/index.js @@ -1,5 +1,6 @@ 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(); }); diff --git a/ee/app/assets/javascripts/prometheus_alerts/components/reset_key.vue b/ee/app/assets/javascripts/prometheus_alerts/components/reset_key.vue new file mode 100644 index 0000000000000000000000000000000000000000..72ae4d7585da6af36d0462098a898a118dbbe9ac --- /dev/null +++ b/ee/app/assets/javascripts/prometheus_alerts/components/reset_key.vue @@ -0,0 +1,124 @@ + + + diff --git a/ee/app/assets/javascripts/prometheus_alerts/index.js b/ee/app/assets/javascripts/prometheus_alerts/index.js new file mode 100644 index 0000000000000000000000000000000000000000..10ff65dfddde8cc5af85a18b6aee5403f01eb8db --- /dev/null +++ b/ee/app/assets/javascripts/prometheus_alerts/index.js @@ -0,0 +1,23 @@ +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, + }, + }); + }, + }); +}; diff --git a/ee/app/views/projects/services/prometheus/_external_alerts.html.haml b/ee/app/views/projects/services/prometheus/_external_alerts.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..ab2cc84ddbf2ae93ead5758c9737568be150de03 --- /dev/null +++ b/ee/app/views/projects/services/prometheus/_external_alerts.html.haml @@ -0,0 +1,7 @@ +- 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 } } diff --git a/ee/spec/javascripts/prometheus_alerts/components/reset_key_spec.js b/ee/spec/javascripts/prometheus_alerts/components/reset_key_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..87c006bc4cc232c617c140dd9e0db3a4c4426018 --- /dev/null +++ b/ee/spec/javascripts/prometheus_alerts/components/reset_key_spec.js @@ -0,0 +1,103 @@ +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('
'); + }); + + 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(); + }); + }); + }); +}); diff --git a/locale/gitlab.pot b/locale/gitlab.pot index a666a28fae071b6502a1d75c625a23b6fa5573ab..b63ec541f69079e956f8ebf40d3a954a1c15284c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -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 "" @@ -3797,6 +3803,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 "" @@ -4145,6 +4154,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 "" @@ -7822,12 +7837,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 "" @@ -9777,6 +9804,9 @@ msgstr "" msgid "To preserve performance only %{display_size} of %{real_size} 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 "" @@ -9921,6 +9951,9 @@ msgstr "" msgid "Type" msgstr "" +msgid "URL" +msgstr "" + msgid "Unable to load the diff. %{button_try_again}" msgstr ""