@oneuptime/common 10.5.31 → 10.5.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +45 -0
- package/Models/AnalyticsModels/AuditLog.ts +3 -0
- package/Models/AnalyticsModels/ExceptionInstance.ts +3 -0
- package/Models/AnalyticsModels/Index.ts +2 -0
- package/Models/AnalyticsModels/Log.ts +3 -0
- package/Models/AnalyticsModels/Metric.ts +3 -0
- package/Models/AnalyticsModels/Span.ts +3 -0
- package/Models/DatabaseModels/Index.ts +4 -0
- package/Models/DatabaseModels/KubernetesResource.ts +37 -0
- package/Models/DatabaseModels/MetricSavedView.ts +334 -0
- package/Models/DatabaseModels/TraceSavedView.ts +334 -0
- package/Server/API/KubernetesResourceAPI.ts +18 -9
- package/Server/Infrastructure/Postgres/SchemaMigrations/1780645560183-AddMetricAndTraceSavedView.ts +81 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1780651429467-AddKubernetesLatestMemoryPercent.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/KubernetesResourceService.ts +37 -11
- package/Server/Services/MetricSavedViewService.ts +109 -0
- package/Server/Services/TraceSavedViewService.ts +109 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +34 -0
- package/Server/Utils/Monitor/MonitorIncident.ts +60 -93
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +229 -0
- package/Server/Utils/Monitor/MonitorResource.ts +18 -0
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +156 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +211 -0
- package/Types/Telemetry/TelemetrySavedViewState.ts +26 -0
- package/UI/Components/Table/TableHeader.tsx +6 -2
- package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +175 -0
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js +32 -0
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/AuditLog.js +2 -0
- package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +2 -0
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Index.js +2 -0
- package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Log.js +2 -0
- package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Metric.js +2 -0
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Span.js +2 -0
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesResource.js +38 -0
- package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MetricSavedView.js +356 -0
- package/build/dist/Models/DatabaseModels/MetricSavedView.js.map +1 -0
- package/build/dist/Models/DatabaseModels/TraceSavedView.js +356 -0
- package/build/dist/Models/DatabaseModels/TraceSavedView.js.map +1 -0
- package/build/dist/Server/API/KubernetesResourceAPI.js +6 -4
- package/build/dist/Server/API/KubernetesResourceAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1780645560183-AddMetricAndTraceSavedView.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1780645560183-AddMetricAndTraceSavedView.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1780651429467-AddKubernetesLatestMemoryPercent.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1780651429467-AddKubernetesLatestMemoryPercent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/KubernetesResourceService.js +13 -5
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
- package/build/dist/Server/Services/MetricSavedViewService.js +82 -0
- package/build/dist/Server/Services/MetricSavedViewService.js.map +1 -0
- package/build/dist/Server/Services/TraceSavedViewService.js +82 -0
- package/build/dist/Server/Services/TraceSavedViewService.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +36 -17
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +60 -107
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +165 -0
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +16 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +106 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -0
- package/build/dist/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.js +142 -0
- package/build/dist/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetrySavedViewState.js +9 -0
- package/build/dist/Types/Telemetry/TelemetrySavedViewState.js.map +1 -0
- package/build/dist/UI/Components/Table/TableHeader.js +2 -2
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +58 -0
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import DatabaseService from "./DatabaseService";
|
|
2
|
+
import Model from "../../Models/DatabaseModels/MetricSavedView";
|
|
3
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
4
|
+
import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
|
|
5
|
+
import UpdateBy from "../Types/Database/UpdateBy";
|
|
6
|
+
import ObjectID from "../../Types/ObjectID";
|
|
7
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
8
|
+
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
|
9
|
+
|
|
10
|
+
export class Service extends DatabaseService<Model> {
|
|
11
|
+
public constructor() {
|
|
12
|
+
super(Model);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
protected override async onBeforeCreate(
|
|
16
|
+
createBy: CreateBy<Model>,
|
|
17
|
+
): Promise<OnCreate<Model>> {
|
|
18
|
+
if (createBy.data.isDefault === undefined && createBy.data.projectId) {
|
|
19
|
+
const existingDefaultView: Model | null = await this.findOneBy({
|
|
20
|
+
query: {
|
|
21
|
+
projectId: createBy.data.projectId,
|
|
22
|
+
isDefault: true,
|
|
23
|
+
},
|
|
24
|
+
select: {
|
|
25
|
+
_id: true,
|
|
26
|
+
},
|
|
27
|
+
props: {
|
|
28
|
+
isRoot: true,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
createBy.data.isDefault = !existingDefaultView;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (createBy.data.projectId) {
|
|
36
|
+
await this.unsetOtherDefaultsIfNeeded({
|
|
37
|
+
projectId: createBy.data.projectId,
|
|
38
|
+
isDefault: createBy.data.isDefault || false,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { createBy, carryForward: null };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected override async onBeforeUpdate(
|
|
46
|
+
updateBy: UpdateBy<Model>,
|
|
47
|
+
): Promise<OnUpdate<Model>> {
|
|
48
|
+
if (updateBy.data.isDefault !== true) {
|
|
49
|
+
return { updateBy, carryForward: null };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const itemsToUpdate: Array<Model> = await this.findBy({
|
|
53
|
+
query: updateBy.query,
|
|
54
|
+
select: {
|
|
55
|
+
_id: true,
|
|
56
|
+
projectId: true,
|
|
57
|
+
},
|
|
58
|
+
props: {
|
|
59
|
+
isRoot: true,
|
|
60
|
+
},
|
|
61
|
+
limit: LIMIT_MAX,
|
|
62
|
+
skip: 0,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
for (const item of itemsToUpdate) {
|
|
66
|
+
if (item.projectId) {
|
|
67
|
+
await this.unsetOtherDefaultsIfNeeded({
|
|
68
|
+
projectId: item.projectId,
|
|
69
|
+
isDefault: true,
|
|
70
|
+
excludeIds: item._id ? [item._id] : [],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { updateBy, carryForward: null };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private async unsetOtherDefaultsIfNeeded(data: {
|
|
79
|
+
projectId?: ObjectID;
|
|
80
|
+
isDefault?: boolean;
|
|
81
|
+
excludeIds?: Array<string>;
|
|
82
|
+
}): Promise<void> {
|
|
83
|
+
if (!data.projectId || !data.isDefault) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await this.updateBy({
|
|
88
|
+
query: {
|
|
89
|
+
projectId: data.projectId,
|
|
90
|
+
isDefault: true,
|
|
91
|
+
...(data.excludeIds && data.excludeIds.length > 0
|
|
92
|
+
? {
|
|
93
|
+
_id: QueryHelper.notInOrNull(data.excludeIds),
|
|
94
|
+
}
|
|
95
|
+
: {}),
|
|
96
|
+
},
|
|
97
|
+
data: {
|
|
98
|
+
isDefault: false,
|
|
99
|
+
},
|
|
100
|
+
props: {
|
|
101
|
+
isRoot: true,
|
|
102
|
+
},
|
|
103
|
+
limit: LIMIT_MAX,
|
|
104
|
+
skip: 0,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default new Service();
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import DatabaseService from "./DatabaseService";
|
|
2
|
+
import Model from "../../Models/DatabaseModels/TraceSavedView";
|
|
3
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
4
|
+
import { OnCreate, OnUpdate } from "../Types/Database/Hooks";
|
|
5
|
+
import UpdateBy from "../Types/Database/UpdateBy";
|
|
6
|
+
import ObjectID from "../../Types/ObjectID";
|
|
7
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
8
|
+
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
|
9
|
+
|
|
10
|
+
export class Service extends DatabaseService<Model> {
|
|
11
|
+
public constructor() {
|
|
12
|
+
super(Model);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
protected override async onBeforeCreate(
|
|
16
|
+
createBy: CreateBy<Model>,
|
|
17
|
+
): Promise<OnCreate<Model>> {
|
|
18
|
+
if (createBy.data.isDefault === undefined && createBy.data.projectId) {
|
|
19
|
+
const existingDefaultView: Model | null = await this.findOneBy({
|
|
20
|
+
query: {
|
|
21
|
+
projectId: createBy.data.projectId,
|
|
22
|
+
isDefault: true,
|
|
23
|
+
},
|
|
24
|
+
select: {
|
|
25
|
+
_id: true,
|
|
26
|
+
},
|
|
27
|
+
props: {
|
|
28
|
+
isRoot: true,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
createBy.data.isDefault = !existingDefaultView;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (createBy.data.projectId) {
|
|
36
|
+
await this.unsetOtherDefaultsIfNeeded({
|
|
37
|
+
projectId: createBy.data.projectId,
|
|
38
|
+
isDefault: createBy.data.isDefault || false,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { createBy, carryForward: null };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected override async onBeforeUpdate(
|
|
46
|
+
updateBy: UpdateBy<Model>,
|
|
47
|
+
): Promise<OnUpdate<Model>> {
|
|
48
|
+
if (updateBy.data.isDefault !== true) {
|
|
49
|
+
return { updateBy, carryForward: null };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const itemsToUpdate: Array<Model> = await this.findBy({
|
|
53
|
+
query: updateBy.query,
|
|
54
|
+
select: {
|
|
55
|
+
_id: true,
|
|
56
|
+
projectId: true,
|
|
57
|
+
},
|
|
58
|
+
props: {
|
|
59
|
+
isRoot: true,
|
|
60
|
+
},
|
|
61
|
+
limit: LIMIT_MAX,
|
|
62
|
+
skip: 0,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
for (const item of itemsToUpdate) {
|
|
66
|
+
if (item.projectId) {
|
|
67
|
+
await this.unsetOtherDefaultsIfNeeded({
|
|
68
|
+
projectId: item.projectId,
|
|
69
|
+
isDefault: true,
|
|
70
|
+
excludeIds: item._id ? [item._id] : [],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { updateBy, carryForward: null };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private async unsetOtherDefaultsIfNeeded(data: {
|
|
79
|
+
projectId?: ObjectID;
|
|
80
|
+
isDefault?: boolean;
|
|
81
|
+
excludeIds?: Array<string>;
|
|
82
|
+
}): Promise<void> {
|
|
83
|
+
if (!data.projectId || !data.isDefault) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await this.updateBy({
|
|
88
|
+
query: {
|
|
89
|
+
projectId: data.projectId,
|
|
90
|
+
isDefault: true,
|
|
91
|
+
...(data.excludeIds && data.excludeIds.length > 0
|
|
92
|
+
? {
|
|
93
|
+
_id: QueryHelper.notInOrNull(data.excludeIds),
|
|
94
|
+
}
|
|
95
|
+
: {}),
|
|
96
|
+
},
|
|
97
|
+
data: {
|
|
98
|
+
isDefault: false,
|
|
99
|
+
},
|
|
100
|
+
props: {
|
|
101
|
+
isRoot: true,
|
|
102
|
+
},
|
|
103
|
+
limit: LIMIT_MAX,
|
|
104
|
+
skip: 0,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export default new Service();
|
|
@@ -115,6 +115,13 @@ export default class MonitorAlert {
|
|
|
115
115
|
telemetryQuery?: TelemetryQuery | undefined;
|
|
116
116
|
};
|
|
117
117
|
matchesPerSeries?: Array<PerSeriesCriteriaMatch> | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Series fingerprints whose underlying resource is inside an
|
|
120
|
+
* ongoing scheduled maintenance window. Alerts for these series are
|
|
121
|
+
* suppressed at creation time even though the monitor keeps
|
|
122
|
+
* evaluating. See MonitorMaintenanceSuppression.
|
|
123
|
+
*/
|
|
124
|
+
suppressedSeriesFingerprints?: Set<string> | undefined;
|
|
118
125
|
}): Promise<void> {
|
|
119
126
|
const alertLogAttributes: LogAttributes = {
|
|
120
127
|
projectId: input.monitor.projectId?.toString(),
|
|
@@ -164,6 +171,33 @@ export default class MonitorAlert {
|
|
|
164
171
|
const seriesRootCause: string =
|
|
165
172
|
seriesMatch?.rootCause || input.rootCause;
|
|
166
173
|
|
|
174
|
+
/*
|
|
175
|
+
* Per-series scheduled-maintenance suppression: skip creating an
|
|
176
|
+
* alert for a series whose resource is inside an ongoing
|
|
177
|
+
* maintenance window. Other series on the same monitor are
|
|
178
|
+
* unaffected. Only *new* creation is suppressed — existing open
|
|
179
|
+
* alerts follow the normal resolve path.
|
|
180
|
+
*/
|
|
181
|
+
if (
|
|
182
|
+
seriesFingerprint &&
|
|
183
|
+
input.suppressedSeriesFingerprints?.has(seriesFingerprint)
|
|
184
|
+
) {
|
|
185
|
+
logger.debug(
|
|
186
|
+
`${input.monitor.id?.toString()} - Skipping alert for series ${seriesFingerprint}: its resource is under an active scheduled maintenance window.`,
|
|
187
|
+
alertLogAttributes,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
input.evaluationSummary?.events.push({
|
|
191
|
+
type: "alert-skipped",
|
|
192
|
+
title: "Alert suppressed by scheduled maintenance",
|
|
193
|
+
message:
|
|
194
|
+
"Skipped creating an alert because the resource for this series is under an active scheduled maintenance window.",
|
|
195
|
+
relatedCriteriaId: input.criteriaInstance.data?.id,
|
|
196
|
+
at: OneUptimeDate.getCurrentDate(),
|
|
197
|
+
});
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
|
|
167
201
|
const alreadyOpenAlert: Alert | undefined = openAlerts.find(
|
|
168
202
|
(alert: Alert) => {
|
|
169
203
|
return (
|
|
@@ -37,6 +37,9 @@ import OneUptimeDate from "../../../Types/Date";
|
|
|
37
37
|
import MonitorEvaluationSummary from "../../../Types/Monitor/MonitorEvaluationSummary";
|
|
38
38
|
import { IncidentMemberRoleAssignment } from "../../../Types/Monitor/CriteriaIncident";
|
|
39
39
|
import { PerSeriesCriteriaMatch } from "../../../Types/Probe/ProbeApiIngestResponse";
|
|
40
|
+
import SeriesResourceLabels, {
|
|
41
|
+
SeriesResourceRefs,
|
|
42
|
+
} from "./SeriesResourceLabels";
|
|
40
43
|
|
|
41
44
|
export default class MonitorIncident {
|
|
42
45
|
@CaptureSpan()
|
|
@@ -143,6 +146,14 @@ export default class MonitorIncident {
|
|
|
143
146
|
* reference `{{host.name}}` etc. via the template engine.
|
|
144
147
|
*/
|
|
145
148
|
matchesPerSeries?: Array<PerSeriesCriteriaMatch> | undefined;
|
|
149
|
+
/**
|
|
150
|
+
* Series fingerprints whose underlying resource (host, docker host,
|
|
151
|
+
* kubernetes cluster, or service) is inside an ongoing scheduled
|
|
152
|
+
* maintenance window. The monitor itself keeps evaluating — it is
|
|
153
|
+
* not attached to the maintenance — but incidents for these series
|
|
154
|
+
* are suppressed at creation time. See MonitorMaintenanceSuppression.
|
|
155
|
+
*/
|
|
156
|
+
suppressedSeriesFingerprints?: Set<string> | undefined;
|
|
146
157
|
}): Promise<void> {
|
|
147
158
|
const incidentLogAttributes: LogAttributes = {
|
|
148
159
|
projectId: input.monitor.projectId?.toString(),
|
|
@@ -202,6 +213,37 @@ export default class MonitorIncident {
|
|
|
202
213
|
const seriesRootCause: string =
|
|
203
214
|
seriesMatch?.rootCause || input.rootCause;
|
|
204
215
|
|
|
216
|
+
/*
|
|
217
|
+
* Per-series scheduled-maintenance suppression: this series'
|
|
218
|
+
* resource is inside an ongoing maintenance window, so skip
|
|
219
|
+
* creating an incident for it. Other series on the same monitor
|
|
220
|
+
* whose resources are not under maintenance still get incidents.
|
|
221
|
+
* Note: we only suppress *new* creation — any incident already
|
|
222
|
+
* open for this series is left to the normal resolve path
|
|
223
|
+
* (checkOpenIncidentsAndCloseIfResolved still sees the full
|
|
224
|
+
* breaching set), so a real incident raised before maintenance
|
|
225
|
+
* is not silently closed.
|
|
226
|
+
*/
|
|
227
|
+
if (
|
|
228
|
+
seriesFingerprint &&
|
|
229
|
+
input.suppressedSeriesFingerprints?.has(seriesFingerprint)
|
|
230
|
+
) {
|
|
231
|
+
logger.debug(
|
|
232
|
+
`${input.monitor.id?.toString()} - Skipping incident for series ${seriesFingerprint}: its resource is under an active scheduled maintenance window.`,
|
|
233
|
+
incidentLogAttributes,
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
input.evaluationSummary?.events.push({
|
|
237
|
+
type: "incident-skipped",
|
|
238
|
+
title: "Incident suppressed by scheduled maintenance",
|
|
239
|
+
message:
|
|
240
|
+
"Skipped creating an incident because the resource for this series is under an active scheduled maintenance window.",
|
|
241
|
+
relatedCriteriaId: input.criteriaInstance.data?.id,
|
|
242
|
+
at: OneUptimeDate.getCurrentDate(),
|
|
243
|
+
});
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
205
247
|
const alreadyOpenIncident: Incident | undefined = openIncidents.find(
|
|
206
248
|
(incident: Incident) => {
|
|
207
249
|
return (
|
|
@@ -485,97 +527,22 @@ export default class MonitorIncident {
|
|
|
485
527
|
}
|
|
486
528
|
|
|
487
529
|
/*
|
|
488
|
-
* Pull every host / docker-host / k8s-cluster
|
|
489
|
-
* series labels and attach the matching project-scoped
|
|
490
|
-
* the incident.
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
* the `resource.` prefix in ClickHouse, so prefixed and unprefixed
|
|
496
|
-
* forms are both accepted — whichever the group-by query surfaced,
|
|
497
|
-
* we'll find it. Multi-value labels are flattened, so a series that
|
|
498
|
-
* groups by a multi-valued attribute attaches every matching
|
|
499
|
-
* record. Lookups are always project-scoped so a stale or hostile
|
|
500
|
-
* stamp can't pull in a record from another tenant.
|
|
501
|
-
*
|
|
502
|
-
* For Docker hosts we deliberately ignore raw `host.name`/
|
|
503
|
-
* `oneuptime.host.name`: those are the Host's territory, and the
|
|
504
|
-
* ingest pipeline stamps `oneuptime.docker.host.*` independently
|
|
505
|
-
* when the source is a docker host.
|
|
530
|
+
* Pull every host / docker-host / k8s-cluster / service identifier
|
|
531
|
+
* out of the series labels and attach the matching project-scoped
|
|
532
|
+
* records to the incident. The label-key → resource-type mapping
|
|
533
|
+
* lives in SeriesResourceLabels (shared with the scheduled-maintenance
|
|
534
|
+
* suppression path so the two never disagree about which labels
|
|
535
|
+
* identify which resource). Lookups are always project-scoped so a
|
|
536
|
+
* stale or hostile stamp can't pull in a record from another tenant.
|
|
506
537
|
*/
|
|
507
538
|
private static async linkResourceContextFromSeries(input: {
|
|
508
539
|
incident: Incident;
|
|
509
540
|
seriesLabels: JSONObject;
|
|
510
541
|
projectId: ObjectID;
|
|
511
542
|
}): Promise<void> {
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
)
|
|
515
|
-
const found: Set<string> = new Set<string>();
|
|
516
|
-
for (const key of keys) {
|
|
517
|
-
const value: unknown = input.seriesLabels[key];
|
|
518
|
-
if (typeof value === "string" && value.length > 0) {
|
|
519
|
-
found.add(value);
|
|
520
|
-
continue;
|
|
521
|
-
}
|
|
522
|
-
if (Array.isArray(value)) {
|
|
523
|
-
for (const item of value) {
|
|
524
|
-
if (typeof item === "string" && item.length > 0) {
|
|
525
|
-
found.add(item);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
return Array.from(found);
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
const hostIds: Array<string> = collect([
|
|
534
|
-
"resource.oneuptime.host.id",
|
|
535
|
-
"oneuptime.host.id",
|
|
536
|
-
]);
|
|
537
|
-
const hostNames: Array<string> = collect([
|
|
538
|
-
"resource.oneuptime.host.name",
|
|
539
|
-
"oneuptime.host.name",
|
|
540
|
-
"resource.host.name",
|
|
541
|
-
"host.name",
|
|
542
|
-
]);
|
|
543
|
-
|
|
544
|
-
const dockerHostIds: Array<string> = collect([
|
|
545
|
-
"resource.oneuptime.docker.host.id",
|
|
546
|
-
"oneuptime.docker.host.id",
|
|
547
|
-
]);
|
|
548
|
-
const dockerHostNames: Array<string> = collect([
|
|
549
|
-
"resource.oneuptime.docker.host.name",
|
|
550
|
-
"oneuptime.docker.host.name",
|
|
551
|
-
]);
|
|
552
|
-
|
|
553
|
-
const clusterIds: Array<string> = collect([
|
|
554
|
-
"resource.oneuptime.kubernetes.cluster.id",
|
|
555
|
-
"oneuptime.kubernetes.cluster.id",
|
|
556
|
-
]);
|
|
557
|
-
const clusterNames: Array<string> = collect([
|
|
558
|
-
"resource.oneuptime.kubernetes.cluster.name",
|
|
559
|
-
"oneuptime.kubernetes.cluster.name",
|
|
560
|
-
"resource.k8s.cluster.name",
|
|
561
|
-
"k8s.cluster.name",
|
|
562
|
-
]);
|
|
563
|
-
|
|
564
|
-
/*
|
|
565
|
-
* Services come from OTel-ingested telemetry. The ingest pipeline
|
|
566
|
-
* auto-creates a Service row keyed by `service.name`, so any series
|
|
567
|
-
* label that carries that attribute (raw or prefixed) tells us the
|
|
568
|
-
* emitting service. We also accept the `oneuptime.service.id`
|
|
569
|
-
* stamp for callers that already resolved the ID upstream.
|
|
570
|
-
*/
|
|
571
|
-
const serviceIds: Array<string> = collect([
|
|
572
|
-
"resource.oneuptime.service.id",
|
|
573
|
-
"oneuptime.service.id",
|
|
574
|
-
]);
|
|
575
|
-
const serviceNames: Array<string> = collect([
|
|
576
|
-
"resource.service.name",
|
|
577
|
-
"service.name",
|
|
578
|
-
]);
|
|
543
|
+
const refs: SeriesResourceRefs = SeriesResourceLabels.extractResourceRefs(
|
|
544
|
+
input.seriesLabels,
|
|
545
|
+
);
|
|
579
546
|
|
|
580
547
|
const [
|
|
581
548
|
resolvedHosts,
|
|
@@ -584,29 +551,29 @@ export default class MonitorIncident {
|
|
|
584
551
|
resolvedServices,
|
|
585
552
|
] = await Promise.all([
|
|
586
553
|
this.resolveResourceIds({
|
|
587
|
-
ids: hostIds,
|
|
588
|
-
names: hostNames,
|
|
554
|
+
ids: refs.hostIds,
|
|
555
|
+
names: refs.hostNames,
|
|
589
556
|
nameColumn: "hostIdentifier",
|
|
590
557
|
projectId: input.projectId,
|
|
591
558
|
findBy: HostService.findBy.bind(HostService),
|
|
592
559
|
}),
|
|
593
560
|
this.resolveResourceIds({
|
|
594
|
-
ids: dockerHostIds,
|
|
595
|
-
names: dockerHostNames,
|
|
561
|
+
ids: refs.dockerHostIds,
|
|
562
|
+
names: refs.dockerHostNames,
|
|
596
563
|
nameColumn: "hostIdentifier",
|
|
597
564
|
projectId: input.projectId,
|
|
598
565
|
findBy: DockerHostService.findBy.bind(DockerHostService),
|
|
599
566
|
}),
|
|
600
567
|
this.resolveResourceIds({
|
|
601
|
-
ids:
|
|
602
|
-
names:
|
|
568
|
+
ids: refs.kubernetesClusterIds,
|
|
569
|
+
names: refs.kubernetesClusterNames,
|
|
603
570
|
nameColumn: "clusterIdentifier",
|
|
604
571
|
projectId: input.projectId,
|
|
605
572
|
findBy: KubernetesClusterService.findBy.bind(KubernetesClusterService),
|
|
606
573
|
}),
|
|
607
574
|
this.resolveResourceIds({
|
|
608
|
-
ids: serviceIds,
|
|
609
|
-
names: serviceNames,
|
|
575
|
+
ids: refs.serviceIds,
|
|
576
|
+
names: refs.serviceNames,
|
|
610
577
|
nameColumn: "name",
|
|
611
578
|
projectId: input.projectId,
|
|
612
579
|
findBy: ServiceService.findBy.bind(ServiceService),
|