@oneuptime/common 10.0.93 → 10.0.94
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/DatabaseModels/DockerResource.ts +497 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/Monitor.ts +83 -0
- package/Server/API/MonitorTemplateAPI.ts +182 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1777933061000-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1777972687018-MigrationName.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/BillingInvoiceService.ts +18 -29
- package/Server/Services/DockerResourceService.ts +310 -0
- package/Server/Services/MonitorTemplateService.ts +359 -0
- package/Types/Dashboard/DashboardComponentType.ts +13 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +16 -0
- package/Types/Dashboard/DashboardComponents/DashboardAlertListComponent.ts +4 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerContainerListComponent.ts +15 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerHostListComponent.ts +14 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerImageListComponent.ts +15 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerNetworkListComponent.ts +14 -0
- package/Types/Dashboard/DashboardComponents/DashboardDockerVolumeListComponent.ts +14 -0
- package/Types/Dashboard/DashboardComponents/DashboardIncidentListComponent.ts +4 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesCronJobListComponent.ts +15 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesDaemonSetListComponent.ts +15 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesDeploymentListComponent.ts +15 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesJobListComponent.ts +15 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesNamespaceListComponent.ts +14 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesNodeListComponent.ts +15 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesPodListComponent.ts +16 -0
- package/Types/Dashboard/DashboardComponents/DashboardKubernetesStatefulSetListComponent.ts +15 -0
- package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +3 -0
- package/Types/Docker/DockerInventoryExtractor.ts +343 -0
- package/Utils/Dashboard/Components/DashboardAlertListComponent.ts +47 -2
- package/Utils/Dashboard/Components/DashboardDockerContainerListComponent.ts +93 -0
- package/Utils/Dashboard/Components/DashboardDockerHostListComponent.ts +83 -0
- package/Utils/Dashboard/Components/DashboardDockerImageListComponent.ts +92 -0
- package/Utils/Dashboard/Components/DashboardDockerNetworkListComponent.ts +82 -0
- package/Utils/Dashboard/Components/DashboardDockerVolumeListComponent.ts +82 -0
- package/Utils/Dashboard/Components/DashboardIncidentListComponent.ts +47 -2
- package/Utils/Dashboard/Components/DashboardKubernetesCronJobListComponent.ts +36 -0
- package/Utils/Dashboard/Components/DashboardKubernetesDaemonSetListComponent.ts +36 -0
- package/Utils/Dashboard/Components/DashboardKubernetesDeploymentListComponent.ts +36 -0
- package/Utils/Dashboard/Components/DashboardKubernetesJobListComponent.ts +34 -0
- package/Utils/Dashboard/Components/DashboardKubernetesNamespaceListComponent.ts +36 -0
- package/Utils/Dashboard/Components/DashboardKubernetesNodeListComponent.ts +57 -0
- package/Utils/Dashboard/Components/DashboardKubernetesPodListComponent.ts +60 -0
- package/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.ts +75 -0
- package/Utils/Dashboard/Components/DashboardKubernetesStatefulSetListComponent.ts +36 -0
- package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +59 -2
- package/Utils/Dashboard/Components/Index.ts +102 -0
- package/build/dist/Models/DatabaseModels/DockerResource.js +525 -0
- package/build/dist/Models/DatabaseModels/DockerResource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +82 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Server/API/MonitorTemplateAPI.js +108 -0
- package/build/dist/Server/API/MonitorTemplateAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1777933061000-MigrationName.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1777933061000-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1777972687018-MigrationName.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1777972687018-MigrationName.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/BillingInvoiceService.js +22 -25
- package/build/dist/Server/Services/BillingInvoiceService.js.map +1 -1
- package/build/dist/Server/Services/DockerResourceService.js +196 -0
- package/build/dist/Server/Services/DockerResourceService.js.map +1 -0
- package/build/dist/Server/Services/MonitorTemplateService.js +290 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +13 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +15 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerContainerListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerContainerListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerHostListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerHostListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerImageListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerImageListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerNetworkListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerNetworkListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerVolumeListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDockerVolumeListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesCronJobListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesCronJobListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesDaemonSetListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesDaemonSetListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesDeploymentListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesDeploymentListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesJobListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesJobListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesNamespaceListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesNamespaceListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesNodeListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesNodeListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesPodListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesPodListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesStatefulSetListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardKubernetesStatefulSetListComponent.js.map +1 -0
- package/build/dist/Types/Docker/DockerInventoryExtractor.js +293 -0
- package/build/dist/Types/Docker/DockerInventoryExtractor.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardAlertListComponent.js +43 -3
- package/build/dist/Utils/Dashboard/Components/DashboardAlertListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDockerContainerListComponent.js +75 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerContainerListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerHostListComponent.js +69 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerHostListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerImageListComponent.js +75 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerImageListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerNetworkListComponent.js +66 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerNetworkListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerVolumeListComponent.js +66 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDockerVolumeListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardIncidentListComponent.js +43 -3
- package/build/dist/Utils/Dashboard/Components/DashboardIncidentListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesCronJobListComponent.js +29 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesCronJobListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesDaemonSetListComponent.js +29 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesDaemonSetListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesDeploymentListComponent.js +29 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesDeploymentListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesJobListComponent.js +29 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesJobListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesNamespaceListComponent.js +29 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesNamespaceListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesNodeListComponent.js +44 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesNodeListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesPodListComponent.js +47 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesPodListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.js +55 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesResourceListShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesStatefulSetListComponent.js +29 -0
- package/build/dist/Utils/Dashboard/Components/DashboardKubernetesStatefulSetListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +46 -3
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/Index.js +53 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import DatabaseService from "./DatabaseService";
|
|
2
|
+
import Model from "../../Models/DatabaseModels/DockerResource";
|
|
3
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
4
|
+
import ObjectID from "../../Types/ObjectID";
|
|
5
|
+
import OneUptimeDate from "../../Types/Date";
|
|
6
|
+
import { JSONObject } from "../../Types/JSON";
|
|
7
|
+
import logger from "../Utils/Logger";
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* ------------------------------------------------------------------
|
|
11
|
+
* DockerResourceService
|
|
12
|
+
*
|
|
13
|
+
* Writes and reads the Docker inventory table populated by the
|
|
14
|
+
* telemetry ingest path. Container rows are upserted from the
|
|
15
|
+
* docker_stats receiver metric stream — every container.* metric
|
|
16
|
+
* carries container.id / container.name / container.image.name
|
|
17
|
+
* resource attributes, which is enough to maintain a live inventory
|
|
18
|
+
* of running containers per host without any agent-side change.
|
|
19
|
+
*
|
|
20
|
+
* Image / Network / Volume kinds are reserved for a follow-up agent
|
|
21
|
+
* change that adds a snapshot poller; the schema is ready for them.
|
|
22
|
+
*
|
|
23
|
+
* Rows are hard-deleted once lastSeenAt falls behind the staleness
|
|
24
|
+
* threshold (default: 15 min) so containers that stopped emitting
|
|
25
|
+
* metrics fall off the list automatically.
|
|
26
|
+
* ------------------------------------------------------------------
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export interface ParsedDockerContainer {
|
|
30
|
+
containerName: string;
|
|
31
|
+
containerId: string | null;
|
|
32
|
+
imageName: string | null;
|
|
33
|
+
state: string;
|
|
34
|
+
cpuPercent: number | null;
|
|
35
|
+
memoryBytes: number | null;
|
|
36
|
+
observedAt: Date;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ParsedDockerResource {
|
|
40
|
+
kind: string;
|
|
41
|
+
name: string;
|
|
42
|
+
containerId: string | null;
|
|
43
|
+
imageName: string | null;
|
|
44
|
+
state: string | null;
|
|
45
|
+
labels: JSONObject | null;
|
|
46
|
+
resourceCreationTimestamp: Date | null;
|
|
47
|
+
lastSeenAt: Date;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DockerHostInventoryCounts {
|
|
51
|
+
containersRunning: number;
|
|
52
|
+
containersStopped: number;
|
|
53
|
+
containersPaused: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const UPSERT_BATCH_SIZE: number = 500;
|
|
57
|
+
const STALE_DELETE_WARN_THRESHOLD: number = 100;
|
|
58
|
+
|
|
59
|
+
export class Service extends DatabaseService<Model> {
|
|
60
|
+
public constructor() {
|
|
61
|
+
super(Model);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Upsert a batch of Container rows for a single (project, host)
|
|
66
|
+
* pair. Uses ON CONFLICT on the UNIQUE (projectId, dockerHostId,
|
|
67
|
+
* kind, name) index with a dominance guard on lastSeenAt so
|
|
68
|
+
* out-of-order ingest never regresses a newer observation.
|
|
69
|
+
*
|
|
70
|
+
* Containers are upserted from metric snapshots (no separate
|
|
71
|
+
* inventory snapshot path), so this also writes
|
|
72
|
+
* latestCpuPercent / latestMemoryBytes / metricsUpdatedAt in the
|
|
73
|
+
* same statement — saves a round trip vs. the K8s pattern of
|
|
74
|
+
* upsert-then-update.
|
|
75
|
+
*/
|
|
76
|
+
@CaptureSpan()
|
|
77
|
+
public async bulkUpsertContainers(data: {
|
|
78
|
+
projectId: ObjectID;
|
|
79
|
+
dockerHostId: ObjectID;
|
|
80
|
+
containers: Array<ParsedDockerContainer>;
|
|
81
|
+
}): Promise<void> {
|
|
82
|
+
if (data.containers.length === 0) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
for (
|
|
87
|
+
let i: number = 0;
|
|
88
|
+
i < data.containers.length;
|
|
89
|
+
i += UPSERT_BATCH_SIZE
|
|
90
|
+
) {
|
|
91
|
+
const chunk: Array<ParsedDockerContainer> = data.containers.slice(
|
|
92
|
+
i,
|
|
93
|
+
i + UPSERT_BATCH_SIZE,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const valueFragments: Array<string> = [];
|
|
97
|
+
const params: Array<unknown> = [];
|
|
98
|
+
let p: number = 1;
|
|
99
|
+
|
|
100
|
+
for (const c of chunk) {
|
|
101
|
+
valueFragments.push(
|
|
102
|
+
`($${p++}, $${p++}, $${p++}, $${p++}, $${p++}, $${p++}, $${p++}, $${p++}::numeric, $${p++}::bigint, $${p++}::timestamptz, $${p++}::timestamptz, $${p++})`,
|
|
103
|
+
);
|
|
104
|
+
params.push(
|
|
105
|
+
data.projectId.toString(),
|
|
106
|
+
data.dockerHostId.toString(),
|
|
107
|
+
"Container",
|
|
108
|
+
c.containerName,
|
|
109
|
+
c.containerId,
|
|
110
|
+
c.imageName,
|
|
111
|
+
c.state,
|
|
112
|
+
c.cpuPercent !== null && c.cpuPercent !== undefined
|
|
113
|
+
? c.cpuPercent
|
|
114
|
+
: null,
|
|
115
|
+
c.memoryBytes !== null && c.memoryBytes !== undefined
|
|
116
|
+
? Math.trunc(c.memoryBytes).toString()
|
|
117
|
+
: null,
|
|
118
|
+
c.observedAt,
|
|
119
|
+
c.observedAt,
|
|
120
|
+
0, // version (BaseModel @VersionColumn)
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const sql: string = `
|
|
125
|
+
INSERT INTO "DockerResource" (
|
|
126
|
+
"projectId", "dockerHostId", "kind", "name",
|
|
127
|
+
"containerId", "imageName", "state",
|
|
128
|
+
"latestCpuPercent", "latestMemoryBytes",
|
|
129
|
+
"metricsUpdatedAt", "lastSeenAt", "version"
|
|
130
|
+
)
|
|
131
|
+
VALUES ${valueFragments.join(", ")}
|
|
132
|
+
ON CONFLICT ("projectId", "dockerHostId", "kind", "name")
|
|
133
|
+
DO UPDATE SET
|
|
134
|
+
"containerId" = COALESCE(EXCLUDED."containerId", "DockerResource"."containerId"),
|
|
135
|
+
"imageName" = COALESCE(EXCLUDED."imageName", "DockerResource"."imageName"),
|
|
136
|
+
"state" = EXCLUDED."state",
|
|
137
|
+
"latestCpuPercent" = COALESCE(EXCLUDED."latestCpuPercent", "DockerResource"."latestCpuPercent"),
|
|
138
|
+
"latestMemoryBytes" = COALESCE(EXCLUDED."latestMemoryBytes", "DockerResource"."latestMemoryBytes"),
|
|
139
|
+
"metricsUpdatedAt" = EXCLUDED."metricsUpdatedAt",
|
|
140
|
+
"lastSeenAt" = EXCLUDED."lastSeenAt",
|
|
141
|
+
"updatedAt" = now()
|
|
142
|
+
WHERE EXCLUDED."lastSeenAt" >= "DockerResource"."lastSeenAt"
|
|
143
|
+
`;
|
|
144
|
+
|
|
145
|
+
await this.getRepository().manager.query(sql, params);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Upsert a batch of resources for any kind. Used by the snapshot
|
|
151
|
+
* ingest path (Container / Image / Network / Volume rows from the
|
|
152
|
+
* Docker agent's inventory poller). Container rows from this path
|
|
153
|
+
* carry full state (running / exited / paused / restarting / dead /
|
|
154
|
+
* created), unlike the metric-derived path which only sees running
|
|
155
|
+
* containers.
|
|
156
|
+
*/
|
|
157
|
+
@CaptureSpan()
|
|
158
|
+
public async bulkUpsert(data: {
|
|
159
|
+
projectId: ObjectID;
|
|
160
|
+
dockerHostId: ObjectID;
|
|
161
|
+
resources: Array<ParsedDockerResource>;
|
|
162
|
+
}): Promise<void> {
|
|
163
|
+
if (data.resources.length === 0) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
for (let i: number = 0; i < data.resources.length; i += UPSERT_BATCH_SIZE) {
|
|
168
|
+
const chunk: Array<ParsedDockerResource> = data.resources.slice(
|
|
169
|
+
i,
|
|
170
|
+
i + UPSERT_BATCH_SIZE,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const valueFragments: Array<string> = [];
|
|
174
|
+
const params: Array<unknown> = [];
|
|
175
|
+
let p: number = 1;
|
|
176
|
+
|
|
177
|
+
for (const r of chunk) {
|
|
178
|
+
valueFragments.push(
|
|
179
|
+
`($${p++}, $${p++}, $${p++}, $${p++}, $${p++}, $${p++}, $${p++}, $${p++}, $${p++}::timestamptz, $${p++}::timestamptz, $${p++})`,
|
|
180
|
+
);
|
|
181
|
+
params.push(
|
|
182
|
+
data.projectId.toString(),
|
|
183
|
+
data.dockerHostId.toString(),
|
|
184
|
+
r.kind,
|
|
185
|
+
r.name,
|
|
186
|
+
r.containerId,
|
|
187
|
+
r.imageName,
|
|
188
|
+
r.state,
|
|
189
|
+
r.labels ? JSON.stringify(r.labels) : null,
|
|
190
|
+
r.resourceCreationTimestamp,
|
|
191
|
+
r.lastSeenAt,
|
|
192
|
+
0, // version
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const sql: string = `
|
|
197
|
+
INSERT INTO "DockerResource" (
|
|
198
|
+
"projectId", "dockerHostId", "kind", "name",
|
|
199
|
+
"containerId", "imageName", "state", "labels",
|
|
200
|
+
"resourceCreationTimestamp", "lastSeenAt", "version"
|
|
201
|
+
)
|
|
202
|
+
VALUES ${valueFragments.join(", ")}
|
|
203
|
+
ON CONFLICT ("projectId", "dockerHostId", "kind", "name")
|
|
204
|
+
DO UPDATE SET
|
|
205
|
+
"containerId" = COALESCE(EXCLUDED."containerId", "DockerResource"."containerId"),
|
|
206
|
+
"imageName" = COALESCE(EXCLUDED."imageName", "DockerResource"."imageName"),
|
|
207
|
+
"state" = COALESCE(EXCLUDED."state", "DockerResource"."state"),
|
|
208
|
+
"labels" = COALESCE(EXCLUDED."labels", "DockerResource"."labels"),
|
|
209
|
+
"resourceCreationTimestamp" = COALESCE(EXCLUDED."resourceCreationTimestamp", "DockerResource"."resourceCreationTimestamp"),
|
|
210
|
+
"lastSeenAt" = EXCLUDED."lastSeenAt",
|
|
211
|
+
"updatedAt" = now()
|
|
212
|
+
WHERE EXCLUDED."lastSeenAt" >= "DockerResource"."lastSeenAt"
|
|
213
|
+
`;
|
|
214
|
+
|
|
215
|
+
await this.getRepository().manager.query(sql, params);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Hard-delete all resources on a host whose last observation is
|
|
221
|
+
* older than olderThan. Returns the number of deleted rows.
|
|
222
|
+
*/
|
|
223
|
+
@CaptureSpan()
|
|
224
|
+
public async deleteStaleForHost(data: {
|
|
225
|
+
dockerHostId: ObjectID;
|
|
226
|
+
olderThan: Date;
|
|
227
|
+
}): Promise<number> {
|
|
228
|
+
const result: Array<{ affected?: number }> | { affected?: number } =
|
|
229
|
+
await this.getRepository().manager.query(
|
|
230
|
+
`DELETE FROM "DockerResource" WHERE "dockerHostId" = $1 AND "lastSeenAt" < $2`,
|
|
231
|
+
[data.dockerHostId.toString(), data.olderThan],
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
let affected: number = 0;
|
|
235
|
+
if (Array.isArray(result) && result.length >= 2) {
|
|
236
|
+
const second: unknown = (result as Array<unknown>)[1];
|
|
237
|
+
if (typeof second === "number") {
|
|
238
|
+
affected = second;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (affected > STALE_DELETE_WARN_THRESHOLD) {
|
|
243
|
+
logger.warn(
|
|
244
|
+
`DockerResource cleanup deleted ${affected} stale rows for host ${data.dockerHostId.toString()} — larger than expected; investigate agent health.`,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return affected;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Compute container state breakdowns for a single host from the
|
|
253
|
+
* inventory table. Used by the cleanup worker to refresh the cached
|
|
254
|
+
* counts on DockerHost so the Hosts page / dashboard widget shows
|
|
255
|
+
* accurate numbers without needing a SQL aggregation per render.
|
|
256
|
+
*/
|
|
257
|
+
@CaptureSpan()
|
|
258
|
+
public async getContainerCountsForHost(data: {
|
|
259
|
+
projectId: ObjectID;
|
|
260
|
+
dockerHostId: ObjectID;
|
|
261
|
+
}): Promise<DockerHostInventoryCounts> {
|
|
262
|
+
const rows: Array<{
|
|
263
|
+
running: string;
|
|
264
|
+
stopped: string;
|
|
265
|
+
paused: string;
|
|
266
|
+
}> = await this.getRepository().manager.query(
|
|
267
|
+
`SELECT
|
|
268
|
+
COUNT(*) FILTER (WHERE LOWER("state") = 'running')::text AS "running",
|
|
269
|
+
COUNT(*) FILTER (WHERE LOWER("state") IN ('exited', 'dead', 'created'))::text AS "stopped",
|
|
270
|
+
COUNT(*) FILTER (WHERE LOWER("state") = 'paused')::text AS "paused"
|
|
271
|
+
FROM "DockerResource"
|
|
272
|
+
WHERE "projectId" = $1
|
|
273
|
+
AND "dockerHostId" = $2
|
|
274
|
+
AND "kind" = 'Container'
|
|
275
|
+
AND "deletedAt" IS NULL`,
|
|
276
|
+
[data.projectId.toString(), data.dockerHostId.toString()],
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
const row:
|
|
280
|
+
| { running: string; stopped: string; paused: string }
|
|
281
|
+
| undefined = rows[0];
|
|
282
|
+
return {
|
|
283
|
+
containersRunning: row ? parseInt(row.running, 10) || 0 : 0,
|
|
284
|
+
containersStopped: row ? parseInt(row.stopped, 10) || 0 : 0,
|
|
285
|
+
containersPaused: row ? parseInt(row.paused, 10) || 0 : 0,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
public getStaleThresholdDate(nowOverride?: Date): Date {
|
|
290
|
+
const minutes: number = this.getStaleThresholdMinutes();
|
|
291
|
+
return OneUptimeDate.addRemoveMinutes(
|
|
292
|
+
nowOverride || OneUptimeDate.getCurrentDate(),
|
|
293
|
+
-minutes,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
public getStaleThresholdMinutes(): number {
|
|
298
|
+
const raw: string | undefined =
|
|
299
|
+
process.env["DOCKER_INVENTORY_STALE_MINUTES"];
|
|
300
|
+
if (raw) {
|
|
301
|
+
const parsed: number = parseInt(raw, 10);
|
|
302
|
+
if (!isNaN(parsed) && parsed >= 5) {
|
|
303
|
+
return parsed;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return 15;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export default new Service();
|
|
@@ -1,9 +1,368 @@
|
|
|
1
1
|
import DatabaseService from "./DatabaseService";
|
|
2
|
+
import MonitorService from "./MonitorService";
|
|
3
|
+
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
4
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
5
|
+
import LIMIT_MAX from "../../Types/Database/LimitMax";
|
|
6
|
+
import ObjectID from "../../Types/ObjectID";
|
|
7
|
+
import PositiveNumber from "../../Types/PositiveNumber";
|
|
2
8
|
import Model from "../../Models/DatabaseModels/MonitorTemplate";
|
|
9
|
+
import Monitor from "../../Models/DatabaseModels/Monitor";
|
|
10
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
11
|
+
|
|
12
|
+
export interface SyncLinkedMonitorsResult {
|
|
13
|
+
totalLinkedMonitors: number;
|
|
14
|
+
syncedMonitors: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Subset of Monitor fields that a template push can overwrite. Anything
|
|
19
|
+
* outside this set (name, description, labels, monitorType, etc.) is
|
|
20
|
+
* intentionally never touched by sync — those are per-monitor concerns.
|
|
21
|
+
*/
|
|
22
|
+
export type SyncableTemplateField =
|
|
23
|
+
| "monitorSteps"
|
|
24
|
+
| "monitoringInterval"
|
|
25
|
+
| "minimumProbeAgreement";
|
|
26
|
+
|
|
27
|
+
const ALL_SYNCABLE_FIELDS: ReadonlyArray<SyncableTemplateField> = [
|
|
28
|
+
"monitorSteps",
|
|
29
|
+
"monitoringInterval",
|
|
30
|
+
"minimumProbeAgreement",
|
|
31
|
+
];
|
|
3
32
|
|
|
4
33
|
export class Service extends DatabaseService<Model> {
|
|
5
34
|
public constructor() {
|
|
6
35
|
super(Model);
|
|
7
36
|
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Count monitors created from this template.
|
|
40
|
+
* Caller must already have read access on the template via the API layer.
|
|
41
|
+
*/
|
|
42
|
+
@CaptureSpan()
|
|
43
|
+
public async countLinkedMonitors(data: {
|
|
44
|
+
monitorTemplateId: ObjectID;
|
|
45
|
+
projectId: ObjectID;
|
|
46
|
+
}): Promise<number> {
|
|
47
|
+
const count: PositiveNumber = await MonitorService.countBy({
|
|
48
|
+
query: {
|
|
49
|
+
monitorTemplateId: data.monitorTemplateId,
|
|
50
|
+
projectId: data.projectId,
|
|
51
|
+
},
|
|
52
|
+
props: {
|
|
53
|
+
isRoot: true,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return count.toNumber();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Validate and narrow a list of field names to the syncable subset.
|
|
62
|
+
* Anything not in the whitelist throws — we never silently drop a field the
|
|
63
|
+
* caller asked for, that would mask UI bugs.
|
|
64
|
+
*/
|
|
65
|
+
private validateSyncableFields(
|
|
66
|
+
fields: Array<string> | undefined,
|
|
67
|
+
): Array<SyncableTemplateField> {
|
|
68
|
+
if (!fields || fields.length === 0) {
|
|
69
|
+
return [...ALL_SYNCABLE_FIELDS];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const allowed: Set<string> = new Set(ALL_SYNCABLE_FIELDS);
|
|
73
|
+
for (const field of fields) {
|
|
74
|
+
if (!allowed.has(field)) {
|
|
75
|
+
throw new BadDataException(
|
|
76
|
+
`Field "${field}" is not syncable from a monitor template`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return fields as Array<SyncableTemplateField>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private buildUpdateData(
|
|
84
|
+
template: Model,
|
|
85
|
+
fields: Array<SyncableTemplateField>,
|
|
86
|
+
): Partial<Monitor> {
|
|
87
|
+
const updateData: Partial<Monitor> = {};
|
|
88
|
+
|
|
89
|
+
for (const field of fields) {
|
|
90
|
+
const value: unknown = (template as unknown as Record<string, unknown>)[
|
|
91
|
+
field
|
|
92
|
+
];
|
|
93
|
+
if (value === undefined) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
(updateData as unknown as Record<string, unknown>)[field] = value;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return updateData;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Push the template's current configuration onto every monitor that was
|
|
104
|
+
* created from it. Sync is intentionally explicit (button-triggered) so a
|
|
105
|
+
* config tweak doesn't silently re-deploy across the whole fleet.
|
|
106
|
+
*
|
|
107
|
+
* Pass `fields` to scope the sync — e.g. `["monitorSteps"]` to push only the
|
|
108
|
+
* criteria. If omitted, every syncable field is pushed.
|
|
109
|
+
*/
|
|
110
|
+
@CaptureSpan()
|
|
111
|
+
public async syncLinkedMonitors(data: {
|
|
112
|
+
monitorTemplateId: ObjectID;
|
|
113
|
+
props: DatabaseCommonInteractionProps;
|
|
114
|
+
fields?: Array<string>;
|
|
115
|
+
}): Promise<SyncLinkedMonitorsResult> {
|
|
116
|
+
const fields: Array<SyncableTemplateField> = this.validateSyncableFields(
|
|
117
|
+
data.fields,
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const template: Model | null = await this.findOneById({
|
|
121
|
+
id: data.monitorTemplateId,
|
|
122
|
+
select: {
|
|
123
|
+
_id: true,
|
|
124
|
+
projectId: true,
|
|
125
|
+
monitorSteps: true,
|
|
126
|
+
monitoringInterval: true,
|
|
127
|
+
minimumProbeAgreement: true,
|
|
128
|
+
},
|
|
129
|
+
props: data.props,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (!template) {
|
|
133
|
+
throw new BadDataException("Monitor template not found");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!template.projectId) {
|
|
137
|
+
throw new BadDataException("Monitor template is missing projectId");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const totalLinkedMonitors: number = await this.countLinkedMonitors({
|
|
141
|
+
monitorTemplateId: template.id!,
|
|
142
|
+
projectId: template.projectId,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
if (totalLinkedMonitors === 0) {
|
|
146
|
+
return {
|
|
147
|
+
totalLinkedMonitors: 0,
|
|
148
|
+
syncedMonitors: 0,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const updateData: Partial<Monitor> = this.buildUpdateData(template, fields);
|
|
153
|
+
|
|
154
|
+
if (Object.keys(updateData).length === 0) {
|
|
155
|
+
return {
|
|
156
|
+
totalLinkedMonitors,
|
|
157
|
+
syncedMonitors: 0,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const syncedMonitors: number = await MonitorService.updateBy({
|
|
162
|
+
query: {
|
|
163
|
+
monitorTemplateId: template.id!,
|
|
164
|
+
projectId: template.projectId,
|
|
165
|
+
},
|
|
166
|
+
data: updateData as any,
|
|
167
|
+
limit: LIMIT_MAX,
|
|
168
|
+
skip: 0,
|
|
169
|
+
props: data.props,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
totalLinkedMonitors,
|
|
174
|
+
syncedMonitors,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Sync the template's current configuration onto a single monitor that was
|
|
180
|
+
* created from it. The monitor must be linked to this template — passing an
|
|
181
|
+
* arbitrary monitor ID is rejected so the endpoint can't be tricked into
|
|
182
|
+
* pushing config to an unrelated monitor.
|
|
183
|
+
*
|
|
184
|
+
* Pass `fields` to scope the sync; if omitted, every syncable field is
|
|
185
|
+
* pushed.
|
|
186
|
+
*/
|
|
187
|
+
@CaptureSpan()
|
|
188
|
+
public async syncToMonitor(data: {
|
|
189
|
+
monitorTemplateId: ObjectID;
|
|
190
|
+
monitorId: ObjectID;
|
|
191
|
+
props: DatabaseCommonInteractionProps;
|
|
192
|
+
fields?: Array<string>;
|
|
193
|
+
}): Promise<void> {
|
|
194
|
+
const fields: Array<SyncableTemplateField> = this.validateSyncableFields(
|
|
195
|
+
data.fields,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const template: Model | null = await this.findOneById({
|
|
199
|
+
id: data.monitorTemplateId,
|
|
200
|
+
select: {
|
|
201
|
+
_id: true,
|
|
202
|
+
projectId: true,
|
|
203
|
+
monitorSteps: true,
|
|
204
|
+
monitoringInterval: true,
|
|
205
|
+
minimumProbeAgreement: true,
|
|
206
|
+
},
|
|
207
|
+
props: data.props,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
if (!template) {
|
|
211
|
+
throw new BadDataException("Monitor template not found");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!template.projectId) {
|
|
215
|
+
throw new BadDataException("Monitor template is missing projectId");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const monitor: Monitor | null = await MonitorService.findOneById({
|
|
219
|
+
id: data.monitorId,
|
|
220
|
+
select: {
|
|
221
|
+
_id: true,
|
|
222
|
+
projectId: true,
|
|
223
|
+
monitorTemplateId: true,
|
|
224
|
+
},
|
|
225
|
+
props: { isRoot: true },
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
if (!monitor) {
|
|
229
|
+
throw new BadDataException("Monitor not found");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (
|
|
233
|
+
!monitor.monitorTemplateId ||
|
|
234
|
+
monitor.monitorTemplateId.toString() !== template.id!.toString()
|
|
235
|
+
) {
|
|
236
|
+
throw new BadDataException("Monitor is not linked to this template");
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (
|
|
240
|
+
!monitor.projectId ||
|
|
241
|
+
monitor.projectId.toString() !== template.projectId.toString()
|
|
242
|
+
) {
|
|
243
|
+
throw new BadDataException(
|
|
244
|
+
"Monitor and template belong to different projects",
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const updateData: Partial<Monitor> = this.buildUpdateData(template, fields);
|
|
249
|
+
|
|
250
|
+
if (Object.keys(updateData).length === 0) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
await MonitorService.updateOneById({
|
|
255
|
+
id: data.monitorId,
|
|
256
|
+
data: updateData as any,
|
|
257
|
+
props: data.props,
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Link an existing monitor to this template. The monitor must be in the same
|
|
263
|
+
* project AND have the same monitorType as the template — anything else is
|
|
264
|
+
* rejected, so a user can't (e.g.) link an API monitor to a Server-monitor
|
|
265
|
+
* template and then sync incompatible criteria onto it.
|
|
266
|
+
*/
|
|
267
|
+
@CaptureSpan()
|
|
268
|
+
public async linkMonitor(data: {
|
|
269
|
+
monitorTemplateId: ObjectID;
|
|
270
|
+
monitorId: ObjectID;
|
|
271
|
+
props: DatabaseCommonInteractionProps;
|
|
272
|
+
}): Promise<void> {
|
|
273
|
+
const template: Model | null = await this.findOneById({
|
|
274
|
+
id: data.monitorTemplateId,
|
|
275
|
+
select: {
|
|
276
|
+
_id: true,
|
|
277
|
+
projectId: true,
|
|
278
|
+
monitorType: true,
|
|
279
|
+
},
|
|
280
|
+
props: data.props,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
if (!template) {
|
|
284
|
+
throw new BadDataException("Monitor template not found");
|
|
285
|
+
}
|
|
286
|
+
if (!template.projectId) {
|
|
287
|
+
throw new BadDataException("Monitor template is missing projectId");
|
|
288
|
+
}
|
|
289
|
+
if (!template.monitorType) {
|
|
290
|
+
throw new BadDataException("Monitor template is missing monitorType");
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const monitor: Monitor | null = await MonitorService.findOneById({
|
|
294
|
+
id: data.monitorId,
|
|
295
|
+
select: {
|
|
296
|
+
_id: true,
|
|
297
|
+
projectId: true,
|
|
298
|
+
monitorType: true,
|
|
299
|
+
},
|
|
300
|
+
props: data.props,
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
if (!monitor) {
|
|
304
|
+
throw new BadDataException("Monitor not found");
|
|
305
|
+
}
|
|
306
|
+
if (
|
|
307
|
+
!monitor.projectId ||
|
|
308
|
+
monitor.projectId.toString() !== template.projectId.toString()
|
|
309
|
+
) {
|
|
310
|
+
throw new BadDataException(
|
|
311
|
+
"Monitor and template belong to different projects",
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
if (monitor.monitorType !== template.monitorType) {
|
|
315
|
+
throw new BadDataException(
|
|
316
|
+
`Monitor type "${monitor.monitorType}" does not match template type "${template.monitorType}"`,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
await MonitorService.updateOneById({
|
|
321
|
+
id: data.monitorId,
|
|
322
|
+
data: {
|
|
323
|
+
monitorTemplateId: template.id!,
|
|
324
|
+
} as any,
|
|
325
|
+
props: data.props,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Detach a monitor from this template. The monitor must currently be linked
|
|
331
|
+
* to *this* template — passing a monitor linked elsewhere (or unlinked) is
|
|
332
|
+
* rejected so a stale UI can't accidentally clear someone else's link.
|
|
333
|
+
*/
|
|
334
|
+
@CaptureSpan()
|
|
335
|
+
public async unlinkMonitor(data: {
|
|
336
|
+
monitorTemplateId: ObjectID;
|
|
337
|
+
monitorId: ObjectID;
|
|
338
|
+
props: DatabaseCommonInteractionProps;
|
|
339
|
+
}): Promise<void> {
|
|
340
|
+
const monitor: Monitor | null = await MonitorService.findOneById({
|
|
341
|
+
id: data.monitorId,
|
|
342
|
+
select: {
|
|
343
|
+
_id: true,
|
|
344
|
+
monitorTemplateId: true,
|
|
345
|
+
},
|
|
346
|
+
props: data.props,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
if (!monitor) {
|
|
350
|
+
throw new BadDataException("Monitor not found");
|
|
351
|
+
}
|
|
352
|
+
if (
|
|
353
|
+
!monitor.monitorTemplateId ||
|
|
354
|
+
monitor.monitorTemplateId.toString() !== data.monitorTemplateId.toString()
|
|
355
|
+
) {
|
|
356
|
+
throw new BadDataException("Monitor is not linked to this template");
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
await MonitorService.updateOneById({
|
|
360
|
+
id: data.monitorId,
|
|
361
|
+
data: {
|
|
362
|
+
monitorTemplateId: null,
|
|
363
|
+
} as any,
|
|
364
|
+
props: data.props,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
8
367
|
}
|
|
9
368
|
export default new Service();
|
|
@@ -9,6 +9,19 @@ enum DashboardComponentType {
|
|
|
9
9
|
IncidentList = `IncidentList`,
|
|
10
10
|
AlertList = `AlertList`,
|
|
11
11
|
MonitorList = `MonitorList`,
|
|
12
|
+
KubernetesPodList = `KubernetesPodList`,
|
|
13
|
+
KubernetesNodeList = `KubernetesNodeList`,
|
|
14
|
+
KubernetesNamespaceList = `KubernetesNamespaceList`,
|
|
15
|
+
KubernetesDeploymentList = `KubernetesDeploymentList`,
|
|
16
|
+
KubernetesStatefulSetList = `KubernetesStatefulSetList`,
|
|
17
|
+
KubernetesDaemonSetList = `KubernetesDaemonSetList`,
|
|
18
|
+
KubernetesJobList = `KubernetesJobList`,
|
|
19
|
+
KubernetesCronJobList = `KubernetesCronJobList`,
|
|
20
|
+
DockerHostList = `DockerHostList`,
|
|
21
|
+
DockerContainerList = `DockerContainerList`,
|
|
22
|
+
DockerImageList = `DockerImageList`,
|
|
23
|
+
DockerNetworkList = `DockerNetworkList`,
|
|
24
|
+
DockerVolumeList = `DockerVolumeList`,
|
|
12
25
|
}
|
|
13
26
|
|
|
14
27
|
export default DashboardComponentType;
|