From 282150d44b2f60bbb1dd7d7c9ae09fad6fb00c4d Mon Sep 17 00:00:00 2001 From: Volker Theile Date: Mon, 9 May 2022 15:31:15 +0200 Subject: [PATCH] mgr/dashboard: Creating and editing Prometheus AlertManager silences is buggy When creating a new monitoring silence the form is pre-filled with the wrong alert data. It is always used the alert data from the very first object in the list of the API response but not the specified alert identified by the 'fingerprint' property. The same problem applies to editing silences. The selected silence is not edited, it's always the first one in the list returned API response but not that with the specified 'id' property. The main problem of the origin implementation is that the Prometheus Alertmanager API endpoints /api/v1/[alerts/silences] do not support querying. To fix that, filtering is done in the frontend. Fixes: https://tracker.ceph.com/issues/55578 Signed-off-by: Volker Theile (cherry picked from commit 658486b566f0f9cac2fc0225c4cd78702f943d40) --- .../silence-form.component.spec.ts | 22 ++++++++++--------- .../silence-form/silence-form.component.ts | 14 ++++++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.spec.ts index 136375cbbbe38..41898315019c9 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.spec.ts @@ -85,7 +85,7 @@ describe('SilenceFormComponent', () => { const changeAction = (action: string) => { const modes = { add: '/monitoring/silences/add', - alertAdd: '/monitoring/silences/add/someAlert', + alertAdd: '/monitoring/silences/add/alert0', recreate: '/monitoring/silences/recreate/someExpiredId', edit: '/monitoring/silences/edit/someNotExpiredId' }; @@ -99,9 +99,10 @@ describe('SilenceFormComponent', () => { prometheus = new PrometheusHelper(); prometheusService = TestBed.inject(PrometheusService); - spyOn(prometheusService, 'getAlerts').and.callFake(() => - of([prometheus.createAlert('alert0')]) - ); + spyOn(prometheusService, 'getAlerts').and.callFake(() => { + const name = _.split(router.url, '/').pop(); + return of([prometheus.createAlert(name)]); + }); ifPrometheusSpy = spyOn(prometheusService, 'ifPrometheusConfigured').and.callFake((fn) => fn()); rulesSpy = spyOn(prometheusService, 'getRules').and.callFake(() => of({ @@ -231,9 +232,10 @@ describe('SilenceFormComponent', () => { }; beforeEach(() => { - spyOn(prometheusService, 'getSilences').and.callFake((p) => - of([prometheus.createSilence(p.id)]) - ); + spyOn(prometheusService, 'getSilences').and.callFake(() => { + const id = _.split(router.url, '/').pop(); + return of([prometheus.createSilence(id)]); + }); }); it('should have no special action activate by default', () => { @@ -251,7 +253,7 @@ describe('SilenceFormComponent', () => { it('should be in edit action if route includes edit', () => { params = { id: 'someNotExpiredId' }; expectMode('edit', true, false, 'Edit'); - expect(prometheusService.getSilences).toHaveBeenCalledWith(params); + expect(prometheusService.getSilences).toHaveBeenCalled(); expect(component.form.value).toEqual({ comment: `A comment for ${params.id}`, createdBy: `Creator of ${params.id}`, @@ -265,7 +267,7 @@ describe('SilenceFormComponent', () => { it('should be in recreation action if route includes recreate', () => { params = { id: 'someExpiredId' }; expectMode('recreate', false, true, 'Recreate'); - expect(prometheusService.getSilences).toHaveBeenCalledWith(params); + expect(prometheusService.getSilences).toHaveBeenCalled(); expect(component.form.value).toEqual({ comment: `A comment for ${params.id}`, createdBy: `Creator of ${params.id}`, @@ -277,7 +279,7 @@ describe('SilenceFormComponent', () => { }); it('adds matchers based on the label object of the alert with the given id', () => { - params = { id: 'someAlert' }; + params = { id: 'alert0' }; expectMode('alertAdd', false, false, 'Create'); expect(prometheusService.getSilences).not.toHaveBeenCalled(); expect(prometheusService.getAlerts).toHaveBeenCalled(); diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.ts index b3bc1401082a6..b698e4958edda 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/cluster/prometheus/silence-form/silence-form.component.ts @@ -217,12 +217,18 @@ export class SilenceFormComponent { return; } if (this.edit || this.recreate) { - this.prometheusService.getSilences(params).subscribe((silences) => { - this.fillFormWithSilence(silences[0]); + this.prometheusService.getSilences().subscribe((silences) => { + const silence = _.find(silences, ['id', params.id]); + if (!_.isUndefined(silence)) { + this.fillFormWithSilence(silence); + } }); } else { - this.prometheusService.getAlerts(params).subscribe((alerts) => { - this.fillFormByAlert(alerts[0]); + this.prometheusService.getAlerts().subscribe((alerts) => { + const alert = _.find(alerts, ['fingerprint', params.id]); + if (!_.isUndefined(alert)) { + this.fillFormByAlert(alert); + } }); } }); -- 2.39.5