<cd-table
[data]="prometheusAlertService.alerts"
[columns]="columns"
+ [extraFilterableColumns]="filters"
identifier="fingerprint"
[forceIdentifier]="true"
[customCss]="customCss"
import { CdTableColumn } from '~/app/shared/models/cd-table-column';
import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
import { Permission } from '~/app/shared/models/permissions';
+import { AlertState } from '~/app/shared/models/prometheus-alerts';
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
import { URLBuilderService } from '~/app/shared/services/url-builder.service';
icons = Icons;
expandedInnerRow: any;
+ filters: CdTableColumn[] = [
+ {
+ name: $localize`State`,
+ prop: 'status.state',
+ filterOptions: [$localize`All`, $localize`Active`, $localize`Suppressed`],
+ filterInitValue: $localize`Active`,
+ filterPredicate: (row, value) => {
+ if (value === 'Active') return row.status?.state === AlertState.ACTIVE;
+ else if (value === 'Suppressed') return row.status?.state === AlertState.SUPPRESSED;
+ if (value === 'All') return true;
+ return false;
+ }
+ }
+ ];
+
constructor(
// NotificationsComponent will refresh all alerts every 5s (No need to do it here as well)
private authStorageService: AuthStorageService,
prometheus = new PrometheusHelper();
prometheusService = TestBed.inject(PrometheusService);
- spyOn(prometheusService, 'getGroupedAlerts').and.callFake(() => {
+ spyOn(prometheusService, 'getAlerts').and.callFake(() => {
const name = _.split(router.url, '/').pop();
return of([prometheus.createAlert(name)]);
});
params = { id: 'alert0' };
expectMode('alertAdd', false, false, 'Create');
expect(prometheusService.getSilences).not.toHaveBeenCalled();
- expect(prometheusService.getGroupedAlerts).toHaveBeenCalled();
+ expect(prometheusService.getAlerts).toHaveBeenCalled();
expect(component.matchers).toEqual([createMatcher('alertname', 'alert0', false)]);
expect(component.matcherMatch).toEqual({
cssClass: 'has-success',
AlertmanagerSilenceMatcherMatch
} from '~/app/shared/models/alertmanager-silence';
import { Permission } from '~/app/shared/models/permissions';
-import { GroupAlertmanagerAlert, PrometheusRule } from '~/app/shared/models/prometheus-alerts';
+import { AlertmanagerAlert, PrometheusRule } from '~/app/shared/models/prometheus-alerts';
import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
import { ModalService } from '~/app/shared/services/modal.service';
import { NotificationService } from '~/app/shared/services/notification.service';
}
});
} else {
- this.prometheusService.getGroupedAlerts().subscribe((alerts) => {
+ this.prometheusService.getAlerts().subscribe((alerts) => {
const alert = _.find(alerts, ['fingerprint', params.id]);
if (!_.isUndefined(alert)) {
this.fillFormByAlert(alert);
this.form.updateValueAndValidity();
}
- private fillFormByAlert(alert: GroupAlertmanagerAlert) {
+ private fillFormByAlert(alert: AlertmanagerAlert) {
const labels = alert.labels;
this.setMatcher({
name: 'alertname',
<ng-container *ngFor="let alert of prometheusAlertService.alerts; let i = index; trackBy: trackByFn">
<div [ngClass]="['border-'+alertClass[alert.labels.severity]]"
*ngIf="alert.status.state === 'active' &&
- alert.labels.severity === alertType ||
- !alertType">
+ (alert.labels.severity === alertType ||
+ !alertType)">
<div class="card tc_alerts border-0 pt-3">
<div class="row no-gutters ps-2">
<div class="col-sm-1 text-center">
[title]="alert.startsAt | cdDate"
i18n>Active since: {{ alert.startsAt | relativeDate }}</small>
<small class="alert_count"
+ *ngIf="alert.alert_count > 1"
[title]="alert.alert_count"
i18n>Total occurrences: {{ alert.alert_count }}</small>
</p>
+type AlertState = 'active' | 'suppressed' | 'resolved' | 'unprocessed';
+
export class PrometheusAlertLabels {
alertname: string;
instance: string;
export class AlertmanagerAlert extends CommonAlertmanagerAlert {
status: {
- state: 'unprocessed' | 'active' | 'suppressed';
+ state: AlertState;
silencedBy: null | string[];
inhibitedBy: null | string[];
};
}
export class PrometheusCustomAlert {
- status: 'resolved' | 'unprocessed' | 'active' | 'suppressed';
+ status: AlertState;
name: string;
url: string;
description: string;
fingerprint?: string | boolean;
severity?: string;
}
+
+export const AlertState = {
+ ACTIVE: 'active' as AlertState,
+ SUPPRESSED: 'suppressed' as AlertState,
+ RESOLVED: 'resolved' as AlertState,
+ UNPROCCESSED: 'unprocessed' as AlertState
+};
AlertmanagerAlert,
PrometheusCustomAlert,
PrometheusRule,
- GroupAlertmanagerAlert
+ GroupAlertmanagerAlert,
+ AlertState
} from '../models/prometheus-alerts';
import { PrometheusAlertFormatter } from './prometheus-alert-formatter';
import { BehaviorSubject } from 'rxjs';
private handleAlerts(alertGroups: GroupAlertmanagerAlert[]) {
const alerts: AlertmanagerAlert[] = alertGroups
- .map((g) => {
- if (!g.alerts.length) return null;
- if (g.alerts.length === 1) return { ...g.alerts[0], alert_count: 1 };
- return { ...g.alerts[0], alert_count: g.alerts.length, subalerts: g.alerts };
+ .map((group) => {
+ if (!group.alerts.length) return null;
+ if (group.alerts.length === 1) return { ...group.alerts[0], alert_count: 1 };
+ const hasActive = group.alerts.some(
+ (alert: AlertmanagerAlert) => alert.status.state === AlertState.ACTIVE
+ );
+ const parent = { ...group.alerts[0] };
+ if (hasActive) parent.status.state = AlertState.ACTIVE;
+ return { ...parent, alert_count: group.alerts.length, subalerts: group.alerts };
})
.filter(Boolean) as AlertmanagerAlert[];
}
this.activeAlerts = _.reduce<AlertmanagerAlert, number>(
alerts,
- (result, alert) => (alert.status.state === 'active' ? ++result : result),
+ (result, alert) => (alert.status.state === AlertState.ACTIVE ? ++result : result),
0
);
this.activeCriticalAlerts = _.reduce<AlertmanagerAlert, number>(
alerts,
(result, alert) =>
- alert.status.state === 'active' && alert.labels.severity === 'critical' ? ++result : result,
+ alert.status.state === AlertState.ACTIVE && alert.labels.severity === 'critical'
+ ? ++result
+ : result,
0
);
this.activeWarningAlerts = _.reduce<AlertmanagerAlert, number>(
alerts,
(result, alert) =>
- alert.status.state === 'active' && alert.labels.severity === 'warning' ? ++result : result,
+ alert.status.state === AlertState.ACTIVE && alert.labels.severity === 'warning'
+ ? ++result
+ : result,
0
);
this.alerts = alerts
this.alertFormatter.convertToCustomAlerts(oldAlerts)
);
const suppressedFiltered = _.filter(changedAlerts, (alert) => {
- return alert.status !== 'suppressed';
+ return alert.status !== AlertState.SUPPRESSED;
});
const notifications = suppressedFiltered.map((alert) =>
this.alertFormatter.convertAlertToNotification(alert)
private getVanishedAlerts(alerts: PrometheusCustomAlert[], oldAlerts: PrometheusCustomAlert[]) {
return _.differenceWith(oldAlerts, alerts, (a, b) => a.fingerprint === b.fingerprint).map(
(alert) => {
- alert.status = 'resolved';
+ alert.status = AlertState.RESOLVED;
return alert;
}
);