@oneuptime/common 10.0.65 → 10.0.66

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.
Files changed (210) hide show
  1. package/Models/DatabaseModels/DockerHostOwnerTeam.ts +464 -0
  2. package/Models/DatabaseModels/DockerHostOwnerUser.ts +463 -0
  3. package/Models/DatabaseModels/Index.ts +24 -0
  4. package/Models/DatabaseModels/KubernetesClusterOwnerTeam.ts +464 -0
  5. package/Models/DatabaseModels/KubernetesClusterOwnerUser.ts +463 -0
  6. package/Models/DatabaseModels/KubernetesResource.ts +548 -0
  7. package/Models/DatabaseModels/MetricPipelineRule.ts +804 -0
  8. package/Models/DatabaseModels/MetricRecordingRule.ts +470 -0
  9. package/Models/DatabaseModels/Monitor.ts +2 -0
  10. package/Models/DatabaseModels/Project.ts +53 -0
  11. package/Models/DatabaseModels/Service.ts +79 -0
  12. package/Models/DatabaseModels/TraceDropFilter.ts +508 -0
  13. package/Models/DatabaseModels/TracePipeline.ts +436 -0
  14. package/Models/DatabaseModels/TracePipelineProcessor.ts +454 -0
  15. package/Models/DatabaseModels/TraceRecordingRule.ts +470 -0
  16. package/Models/DatabaseModels/TraceScrubRule.ts +546 -0
  17. package/Server/API/KubernetesResourceAPI.ts +129 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.ts +399 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.ts +205 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.ts +335 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.ts +29 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.ts +53 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -1
  24. package/Server/Services/DockerHostOwnerTeamService.ts +10 -0
  25. package/Server/Services/DockerHostOwnerUserService.ts +10 -0
  26. package/Server/Services/KubernetesClusterOwnerTeamService.ts +10 -0
  27. package/Server/Services/KubernetesClusterOwnerUserService.ts +10 -0
  28. package/Server/Services/KubernetesResourceService.ts +351 -0
  29. package/Server/Services/MetricPipelineRuleService.ts +10 -0
  30. package/Server/Services/MetricRecordingRuleService.ts +10 -0
  31. package/Server/Services/TraceDropFilterService.ts +10 -0
  32. package/Server/Services/TracePipelineProcessorService.ts +10 -0
  33. package/Server/Services/TracePipelineService.ts +10 -0
  34. package/Server/Services/TraceRecordingRuleService.ts +10 -0
  35. package/Server/Services/TraceScrubRuleService.ts +10 -0
  36. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +71 -9
  37. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +483 -75
  38. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +379 -6
  39. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +502 -0
  40. package/Tests/Utils/MetricUnitUtil.test.ts +216 -0
  41. package/Tests/Utils/Metrics/MetricFormulaEvaluator.test.ts +269 -0
  42. package/Tests/Utils/Metrics/MetricResultUnitConverter.test.ts +231 -0
  43. package/Tests/Utils/RecordingRuleExpression.test.ts +177 -0
  44. package/Types/Kubernetes/KubernetesInventoryExtractor.ts +327 -0
  45. package/Types/Kubernetes/KubernetesObjectParser.ts +1949 -0
  46. package/Types/Metrics/MetricDownsamplingRetentionDays.ts +49 -0
  47. package/Types/Metrics/MetricFormulaConfigData.ts +4 -0
  48. package/Types/Metrics/MetricPipelineRuleFilterCondition.ts +136 -0
  49. package/Types/Metrics/MetricPipelineRuleType.ts +27 -0
  50. package/Types/Metrics/RecordingRuleDefinition.ts +180 -0
  51. package/Types/Monitor/CriteriaFilter.ts +43 -0
  52. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +70 -0
  53. package/Types/Permission.ts +520 -0
  54. package/Types/Trace/TraceAggregationType.ts +17 -0
  55. package/Types/Trace/TraceDropFilterAction.ts +6 -0
  56. package/Types/Trace/TracePipelineProcessorType.ts +56 -0
  57. package/Types/Trace/TraceRecordingRuleDefinition.ts +218 -0
  58. package/Types/Trace/TraceScrubAction.ts +7 -0
  59. package/Types/Trace/TraceScrubField.ts +8 -0
  60. package/Types/Trace/TraceScrubPatternType.ts +10 -0
  61. package/UI/Components/CardSelect/CardSelect.tsx +9 -1
  62. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +6 -10
  63. package/UI/Components/Forms/Fields/FormField.tsx +1 -0
  64. package/UI/Components/Forms/Types/Field.ts +1 -0
  65. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +57 -0
  66. package/UI/Components/Page/Page.tsx +6 -0
  67. package/Utils/MetricUnitUtil.ts +289 -0
  68. package/Utils/Metrics/MetricFormulaEvaluator.ts +610 -0
  69. package/Utils/Metrics/MetricResultUnitConverter.ts +91 -0
  70. package/Utils/Metrics/RecordingRuleExpression.ts +359 -0
  71. package/Utils/ValueFormatter.ts +137 -13
  72. package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js +480 -0
  73. package/build/dist/Models/DatabaseModels/DockerHostOwnerTeam.js.map +1 -0
  74. package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js +479 -0
  75. package/build/dist/Models/DatabaseModels/DockerHostOwnerUser.js.map +1 -0
  76. package/build/dist/Models/DatabaseModels/Index.js +24 -0
  77. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  78. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js +480 -0
  79. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerTeam.js.map +1 -0
  80. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js +479 -0
  81. package/build/dist/Models/DatabaseModels/KubernetesClusterOwnerUser.js.map +1 -0
  82. package/build/dist/Models/DatabaseModels/KubernetesResource.js +590 -0
  83. package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -0
  84. package/build/dist/Models/DatabaseModels/MetricPipelineRule.js +836 -0
  85. package/build/dist/Models/DatabaseModels/MetricPipelineRule.js.map +1 -0
  86. package/build/dist/Models/DatabaseModels/MetricRecordingRule.js +497 -0
  87. package/build/dist/Models/DatabaseModels/MetricRecordingRule.js.map +1 -0
  88. package/build/dist/Models/DatabaseModels/Monitor.js +2 -0
  89. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  90. package/build/dist/Models/DatabaseModels/Project.js +53 -0
  91. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  92. package/build/dist/Models/DatabaseModels/Service.js +79 -0
  93. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  94. package/build/dist/Models/DatabaseModels/TraceDropFilter.js +536 -0
  95. package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -0
  96. package/build/dist/Models/DatabaseModels/TracePipeline.js +462 -0
  97. package/build/dist/Models/DatabaseModels/TracePipeline.js.map +1 -0
  98. package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js +476 -0
  99. package/build/dist/Models/DatabaseModels/TracePipelineProcessor.js.map +1 -0
  100. package/build/dist/Models/DatabaseModels/TraceRecordingRule.js +497 -0
  101. package/build/dist/Models/DatabaseModels/TraceRecordingRule.js.map +1 -0
  102. package/build/dist/Models/DatabaseModels/TraceScrubRule.js +575 -0
  103. package/build/dist/Models/DatabaseModels/TraceScrubRule.js.map +1 -0
  104. package/build/dist/Server/API/KubernetesResourceAPI.js +98 -0
  105. package/build/dist/Server/API/KubernetesResourceAPI.js.map +1 -0
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js +144 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776504277320-MigrationName.js.map +1 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js +82 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776505976155-AddTracePipelineTables.js.map +1 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js +118 -0
  111. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776509413763-MigrationName.js.map +1 -0
  112. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js +16 -0
  113. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776541018853-MigrationName.js.map +1 -0
  114. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js +24 -0
  115. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776544084793-MigrationName.js.map +1 -0
  116. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  117. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  118. package/build/dist/Server/Services/DockerHostOwnerTeamService.js +9 -0
  119. package/build/dist/Server/Services/DockerHostOwnerTeamService.js.map +1 -0
  120. package/build/dist/Server/Services/DockerHostOwnerUserService.js +9 -0
  121. package/build/dist/Server/Services/DockerHostOwnerUserService.js.map +1 -0
  122. package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js +9 -0
  123. package/build/dist/Server/Services/KubernetesClusterOwnerTeamService.js.map +1 -0
  124. package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js +9 -0
  125. package/build/dist/Server/Services/KubernetesClusterOwnerUserService.js.map +1 -0
  126. package/build/dist/Server/Services/KubernetesResourceService.js +237 -0
  127. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -0
  128. package/build/dist/Server/Services/MetricPipelineRuleService.js +9 -0
  129. package/build/dist/Server/Services/MetricPipelineRuleService.js.map +1 -0
  130. package/build/dist/Server/Services/MetricRecordingRuleService.js +9 -0
  131. package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -0
  132. package/build/dist/Server/Services/TraceDropFilterService.js +9 -0
  133. package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -0
  134. package/build/dist/Server/Services/TracePipelineProcessorService.js +9 -0
  135. package/build/dist/Server/Services/TracePipelineProcessorService.js.map +1 -0
  136. package/build/dist/Server/Services/TracePipelineService.js +9 -0
  137. package/build/dist/Server/Services/TracePipelineService.js.map +1 -0
  138. package/build/dist/Server/Services/TraceRecordingRuleService.js +9 -0
  139. package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -0
  140. package/build/dist/Server/Services/TraceScrubRuleService.js +9 -0
  141. package/build/dist/Server/Services/TraceScrubRuleService.js.map +1 -0
  142. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +56 -9
  143. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  144. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +335 -53
  145. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  146. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +277 -5
  147. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  148. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js +407 -0
  149. package/build/dist/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.js.map +1 -0
  150. package/build/dist/Tests/Utils/MetricUnitUtil.test.js +159 -0
  151. package/build/dist/Tests/Utils/MetricUnitUtil.test.js.map +1 -0
  152. package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js +224 -0
  153. package/build/dist/Tests/Utils/Metrics/MetricFormulaEvaluator.test.js.map +1 -0
  154. package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js +180 -0
  155. package/build/dist/Tests/Utils/Metrics/MetricResultUnitConverter.test.js.map +1 -0
  156. package/build/dist/Tests/Utils/RecordingRuleExpression.test.js +142 -0
  157. package/build/dist/Tests/Utils/RecordingRuleExpression.test.js.map +1 -0
  158. package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js +200 -0
  159. package/build/dist/Types/Kubernetes/KubernetesInventoryExtractor.js.map +1 -0
  160. package/build/dist/Types/Kubernetes/KubernetesObjectParser.js +1205 -0
  161. package/build/dist/Types/Kubernetes/KubernetesObjectParser.js.map +1 -0
  162. package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js +32 -0
  163. package/build/dist/Types/Metrics/MetricDownsamplingRetentionDays.js.map +1 -0
  164. package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js +103 -0
  165. package/build/dist/Types/Metrics/MetricPipelineRuleFilterCondition.js.map +1 -0
  166. package/build/dist/Types/Metrics/MetricPipelineRuleType.js +27 -0
  167. package/build/dist/Types/Metrics/MetricPipelineRuleType.js.map +1 -0
  168. package/build/dist/Types/Metrics/RecordingRuleDefinition.js +110 -0
  169. package/build/dist/Types/Metrics/RecordingRuleDefinition.js.map +1 -0
  170. package/build/dist/Types/Monitor/CriteriaFilter.js +22 -0
  171. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  172. package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js +2 -0
  173. package/build/dist/Types/Monitor/MetricMonitor/MetricCriteriaContext.js.map +1 -0
  174. package/build/dist/Types/Permission.js +454 -0
  175. package/build/dist/Types/Permission.js.map +1 -1
  176. package/build/dist/Types/Trace/TraceAggregationType.js +18 -0
  177. package/build/dist/Types/Trace/TraceAggregationType.js.map +1 -0
  178. package/build/dist/Types/Trace/TraceDropFilterAction.js +7 -0
  179. package/build/dist/Types/Trace/TraceDropFilterAction.js.map +1 -0
  180. package/build/dist/Types/Trace/TracePipelineProcessorType.js +10 -0
  181. package/build/dist/Types/Trace/TracePipelineProcessorType.js.map +1 -0
  182. package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js +145 -0
  183. package/build/dist/Types/Trace/TraceRecordingRuleDefinition.js.map +1 -0
  184. package/build/dist/Types/Trace/TraceScrubAction.js +8 -0
  185. package/build/dist/Types/Trace/TraceScrubAction.js.map +1 -0
  186. package/build/dist/Types/Trace/TraceScrubField.js +9 -0
  187. package/build/dist/Types/Trace/TraceScrubField.js.map +1 -0
  188. package/build/dist/Types/Trace/TraceScrubPatternType.js +11 -0
  189. package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -0
  190. package/build/dist/UI/Components/CardSelect/CardSelect.js +3 -1
  191. package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
  192. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +6 -9
  193. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  194. package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
  195. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  196. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +30 -0
  197. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  198. package/build/dist/UI/Components/Page/Page.js +1 -0
  199. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  200. package/build/dist/Utils/MetricUnitUtil.js +232 -0
  201. package/build/dist/Utils/MetricUnitUtil.js.map +1 -0
  202. package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js +453 -0
  203. package/build/dist/Utils/Metrics/MetricFormulaEvaluator.js.map +1 -0
  204. package/build/dist/Utils/Metrics/MetricResultUnitConverter.js +61 -0
  205. package/build/dist/Utils/Metrics/MetricResultUnitConverter.js.map +1 -0
  206. package/build/dist/Utils/Metrics/RecordingRuleExpression.js +298 -0
  207. package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -0
  208. package/build/dist/Utils/ValueFormatter.js +123 -13
  209. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  210. package/package.json +1 -1
@@ -0,0 +1,351 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/KubernetesResource";
3
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
4
+ import ObjectID from "../../Types/ObjectID";
5
+ import OneUptimeDate from "../../Types/Date";
6
+ import { ParsedKubernetesResource } from "../../Types/Kubernetes/KubernetesInventoryExtractor";
7
+ import logger from "../Utils/Logger";
8
+
9
+ /*
10
+ * ------------------------------------------------------------------
11
+ * KubernetesResourceService
12
+ *
13
+ * Writes and reads the inventory table populated by the telemetry
14
+ * ingest path. Callers are either:
15
+ * - OtelLogsIngestService (bulkUpsert, from processLogsAsync)
16
+ * - CleanupStaleResources worker (deleteStaleForCluster)
17
+ * - KubernetesResourceAPI / KubernetesObjectFetcher (reads via
18
+ * the inherited DatabaseService CRUD)
19
+ *
20
+ * ------------------------------------------------------------------
21
+ */
22
+
23
+ export type { ParsedKubernetesResource };
24
+
25
+ export interface InventorySummary {
26
+ countsByKind: Record<string, number>;
27
+ /*
28
+ * Sum of `jsonb_array_length(spec->'containers')` across all pods in
29
+ * the cluster. Containers aren't a top-level kind in the inventory,
30
+ * so we derive the total server-side so the sidebar badge and the
31
+ * Containers page agree.
32
+ */
33
+ containerCount: number;
34
+ podPhaseCounts: {
35
+ running: number;
36
+ pending: number;
37
+ failed: number;
38
+ succeeded: number;
39
+ unknown: number;
40
+ };
41
+ nodeReadyCounts: {
42
+ ready: number;
43
+ notReady: number;
44
+ };
45
+ nodePressureCounts: {
46
+ memoryPressure: number;
47
+ diskPressure: number;
48
+ pidPressure: number;
49
+ };
50
+ }
51
+
52
+ const UPSERT_BATCH_SIZE: number = 500;
53
+ const STALE_DELETE_WARN_THRESHOLD: number = 100;
54
+
55
+ /*
56
+ * Column order used by both bulkUpsert() and its generated parameter tuples.
57
+ * Keep this and the INSERT column list in perfect sync.
58
+ */
59
+ const UPSERT_COLUMNS: Array<keyof ParsedKubernetesResource | string> = [
60
+ "projectId",
61
+ "kubernetesClusterId",
62
+ "kind",
63
+ "namespaceKey",
64
+ "name",
65
+ "uid",
66
+ "phase",
67
+ "isReady",
68
+ "hasMemoryPressure",
69
+ "hasDiskPressure",
70
+ "hasPidPressure",
71
+ "labels",
72
+ "annotations",
73
+ "ownerReferences",
74
+ "spec",
75
+ "status",
76
+ "lastSeenAt",
77
+ "resourceCreationTimestamp",
78
+ "version",
79
+ ];
80
+
81
+ export class Service extends DatabaseService<Model> {
82
+ public constructor() {
83
+ super(Model);
84
+ }
85
+
86
+ /**
87
+ * Upsert a batch of parsed resources for a single (project, cluster)
88
+ * pair. Uses ON CONFLICT on the UNIQUE (projectId, clusterId, kind,
89
+ * namespaceKey, name) index with a dominance guard on lastSeenAt
90
+ * so out-of-order ingest never regresses a newer snapshot.
91
+ */
92
+ @CaptureSpan()
93
+ public async bulkUpsert(data: {
94
+ projectId: ObjectID;
95
+ kubernetesClusterId: ObjectID;
96
+ resources: Array<ParsedKubernetesResource>;
97
+ }): Promise<void> {
98
+ if (data.resources.length === 0) {
99
+ return;
100
+ }
101
+
102
+ // Chunk to keep individual statement parameter counts reasonable.
103
+ for (let i: number = 0; i < data.resources.length; i += UPSERT_BATCH_SIZE) {
104
+ const chunk: Array<ParsedKubernetesResource> = data.resources.slice(
105
+ i,
106
+ i + UPSERT_BATCH_SIZE,
107
+ );
108
+
109
+ const valueFragments: Array<string> = [];
110
+ const params: Array<unknown> = [];
111
+ let paramIndex: number = 1;
112
+
113
+ for (const r of chunk) {
114
+ const placeholders: Array<string> = [];
115
+ for (let c: number = 0; c < UPSERT_COLUMNS.length; c++) {
116
+ placeholders.push(`$${paramIndex++}`);
117
+ }
118
+ valueFragments.push(`(${placeholders.join(", ")})`);
119
+
120
+ params.push(
121
+ data.projectId.toString(),
122
+ data.kubernetesClusterId.toString(),
123
+ r.kind,
124
+ r.namespaceKey,
125
+ r.name,
126
+ r.uid,
127
+ r.phase,
128
+ r.isReady,
129
+ r.hasMemoryPressure,
130
+ r.hasDiskPressure,
131
+ r.hasPidPressure,
132
+ r.labels ? JSON.stringify(r.labels) : null,
133
+ r.annotations ? JSON.stringify(r.annotations) : null,
134
+ r.ownerReferences ? JSON.stringify(r.ownerReferences) : null,
135
+ r.spec ? JSON.stringify(r.spec) : null,
136
+ r.status ? JSON.stringify(r.status) : null,
137
+ r.lastSeenAt,
138
+ r.resourceCreationTimestamp,
139
+ 0, // version (BaseModel @VersionColumn)
140
+ );
141
+ }
142
+
143
+ const sql: string = `
144
+ INSERT INTO "KubernetesResource" (
145
+ "projectId", "kubernetesClusterId", "kind", "namespaceKey", "name",
146
+ "uid", "phase", "isReady",
147
+ "hasMemoryPressure", "hasDiskPressure", "hasPidPressure",
148
+ "labels", "annotations", "ownerReferences", "spec", "status",
149
+ "lastSeenAt", "resourceCreationTimestamp", "version"
150
+ )
151
+ VALUES ${valueFragments.join(", ")}
152
+ ON CONFLICT ("projectId", "kubernetesClusterId", "kind", "namespaceKey", "name")
153
+ DO UPDATE SET
154
+ "uid" = EXCLUDED."uid",
155
+ "phase" = EXCLUDED."phase",
156
+ "isReady" = EXCLUDED."isReady",
157
+ "hasMemoryPressure" = EXCLUDED."hasMemoryPressure",
158
+ "hasDiskPressure" = EXCLUDED."hasDiskPressure",
159
+ "hasPidPressure" = EXCLUDED."hasPidPressure",
160
+ "labels" = EXCLUDED."labels",
161
+ "annotations" = EXCLUDED."annotations",
162
+ "ownerReferences" = EXCLUDED."ownerReferences",
163
+ "spec" = EXCLUDED."spec",
164
+ "status" = EXCLUDED."status",
165
+ "lastSeenAt" = EXCLUDED."lastSeenAt",
166
+ "resourceCreationTimestamp" = EXCLUDED."resourceCreationTimestamp",
167
+ "updatedAt" = now()
168
+ WHERE EXCLUDED."lastSeenAt" >= "KubernetesResource"."lastSeenAt"
169
+ `;
170
+
171
+ await this.getRepository().manager.query(sql, params);
172
+ }
173
+ }
174
+
175
+ /**
176
+ * Hard-delete all resources in a cluster whose last snapshot is
177
+ * older than olderThan. Returns the number of deleted rows.
178
+ */
179
+ @CaptureSpan()
180
+ public async deleteStaleForCluster(data: {
181
+ kubernetesClusterId: ObjectID;
182
+ olderThan: Date;
183
+ }): Promise<number> {
184
+ const result: Array<{ affected?: number }> | { affected?: number } =
185
+ await this.getRepository().manager.query(
186
+ `DELETE FROM "KubernetesResource" WHERE "kubernetesClusterId" = $1 AND "lastSeenAt" < $2`,
187
+ [data.kubernetesClusterId.toString(), data.olderThan],
188
+ );
189
+
190
+ // Postgres driver returns [rows, affected] for DELETE — normalize.
191
+ let affected: number = 0;
192
+ if (Array.isArray(result) && result.length >= 2) {
193
+ const second: unknown = (result as Array<unknown>)[1];
194
+ if (typeof second === "number") {
195
+ affected = second;
196
+ }
197
+ }
198
+
199
+ if (affected > STALE_DELETE_WARN_THRESHOLD) {
200
+ logger.warn(
201
+ `KubernetesResource cleanup deleted ${affected} stale rows for cluster ${data.kubernetesClusterId.toString()} — larger than expected; investigate agent health.`,
202
+ );
203
+ }
204
+
205
+ return affected;
206
+ }
207
+
208
+ /**
209
+ * Compute the overview-page summary in Postgres. Returns counts per
210
+ * resource kind plus pod phase / node condition breakdowns in a
211
+ * single round-trip.
212
+ */
213
+ @CaptureSpan()
214
+ public async getInventorySummary(data: {
215
+ projectId: ObjectID;
216
+ kubernetesClusterId: ObjectID;
217
+ }): Promise<InventorySummary> {
218
+ const manager: ReturnType<Service["getRepository"]>["manager"] =
219
+ this.getRepository().manager;
220
+
221
+ const [kindRows, podRows, nodeRows, containerRows]: [
222
+ Array<{ kind: string; count: string }>,
223
+ Array<{ phase: string | null; count: string }>,
224
+ Array<{
225
+ ready: string;
226
+ notReady: string;
227
+ memoryPressure: string;
228
+ diskPressure: string;
229
+ pidPressure: string;
230
+ }>,
231
+ Array<{ total: string }>,
232
+ ] = await Promise.all([
233
+ manager.query(
234
+ `SELECT "kind", COUNT(*)::text AS count
235
+ FROM "KubernetesResource"
236
+ WHERE "projectId" = $1 AND "kubernetesClusterId" = $2 AND "deletedAt" IS NULL
237
+ GROUP BY "kind"`,
238
+ [data.projectId.toString(), data.kubernetesClusterId.toString()],
239
+ ),
240
+ manager.query(
241
+ `SELECT "phase", COUNT(*)::text AS count
242
+ FROM "KubernetesResource"
243
+ WHERE "projectId" = $1 AND "kubernetesClusterId" = $2 AND "kind" = 'Pod' AND "deletedAt" IS NULL
244
+ GROUP BY "phase"`,
245
+ [data.projectId.toString(), data.kubernetesClusterId.toString()],
246
+ ),
247
+ manager.query(
248
+ `SELECT
249
+ COUNT(*) FILTER (WHERE "isReady" IS TRUE)::text AS "ready",
250
+ COUNT(*) FILTER (WHERE "isReady" IS FALSE)::text AS "notReady",
251
+ COUNT(*) FILTER (WHERE "hasMemoryPressure" IS TRUE)::text AS "memoryPressure",
252
+ COUNT(*) FILTER (WHERE "hasDiskPressure" IS TRUE)::text AS "diskPressure",
253
+ COUNT(*) FILTER (WHERE "hasPidPressure" IS TRUE)::text AS "pidPressure"
254
+ FROM "KubernetesResource"
255
+ WHERE "projectId" = $1 AND "kubernetesClusterId" = $2 AND "kind" = 'Node' AND "deletedAt" IS NULL`,
256
+ [data.projectId.toString(), data.kubernetesClusterId.toString()],
257
+ ),
258
+ manager.query(
259
+ `SELECT COALESCE(SUM(
260
+ CASE WHEN jsonb_typeof("spec"->'containers') = 'array'
261
+ THEN jsonb_array_length("spec"->'containers')
262
+ ELSE 0 END
263
+ ), 0)::text AS total
264
+ FROM "KubernetesResource"
265
+ WHERE "projectId" = $1 AND "kubernetesClusterId" = $2 AND "kind" = 'Pod' AND "deletedAt" IS NULL`,
266
+ [data.projectId.toString(), data.kubernetesClusterId.toString()],
267
+ ),
268
+ ]);
269
+
270
+ const countsByKind: Record<string, number> = {};
271
+ for (const row of kindRows) {
272
+ countsByKind[row.kind] = parseInt(row.count, 10) || 0;
273
+ }
274
+
275
+ const podPhaseCounts: InventorySummary["podPhaseCounts"] = {
276
+ running: 0,
277
+ pending: 0,
278
+ failed: 0,
279
+ succeeded: 0,
280
+ unknown: 0,
281
+ };
282
+ for (const row of podRows) {
283
+ const phase: string = row.phase || "Unknown";
284
+ const count: number = parseInt(row.count, 10) || 0;
285
+ if (phase === "Running") {
286
+ podPhaseCounts.running = count;
287
+ } else if (phase === "Pending") {
288
+ podPhaseCounts.pending = count;
289
+ } else if (phase === "Failed") {
290
+ podPhaseCounts.failed = count;
291
+ } else if (phase === "Succeeded") {
292
+ podPhaseCounts.succeeded = count;
293
+ } else {
294
+ podPhaseCounts.unknown += count;
295
+ }
296
+ }
297
+
298
+ const nodeRow:
299
+ | {
300
+ ready: string;
301
+ notReady: string;
302
+ memoryPressure: string;
303
+ diskPressure: string;
304
+ pidPressure: string;
305
+ }
306
+ | undefined = nodeRows[0];
307
+
308
+ const containerCount: number =
309
+ parseInt(containerRows[0]?.total || "0", 10) || 0;
310
+
311
+ return {
312
+ countsByKind,
313
+ containerCount,
314
+ podPhaseCounts,
315
+ nodeReadyCounts: {
316
+ ready: parseInt(nodeRow?.ready || "0", 10) || 0,
317
+ notReady: parseInt(nodeRow?.notReady || "0", 10) || 0,
318
+ },
319
+ nodePressureCounts: {
320
+ memoryPressure: parseInt(nodeRow?.memoryPressure || "0", 10) || 0,
321
+ diskPressure: parseInt(nodeRow?.diskPressure || "0", 10) || 0,
322
+ pidPressure: parseInt(nodeRow?.pidPressure || "0", 10) || 0,
323
+ },
324
+ };
325
+ }
326
+
327
+ /**
328
+ * Helper for the cleanup worker: snapshot-interval aware cutoff.
329
+ * 3× the 5-minute snapshot interval. Tune via CLEANUP_THRESHOLD_MINUTES.
330
+ */
331
+ public getStaleThresholdDate(nowOverride?: Date): Date {
332
+ const minutes: number = this.getStaleThresholdMinutes();
333
+ return OneUptimeDate.addRemoveMinutes(
334
+ nowOverride || OneUptimeDate.getCurrentDate(),
335
+ -minutes,
336
+ );
337
+ }
338
+
339
+ public getStaleThresholdMinutes(): number {
340
+ const raw: string | undefined = process.env["K8S_INVENTORY_STALE_MINUTES"];
341
+ if (raw) {
342
+ const parsed: number = parseInt(raw, 10);
343
+ if (!isNaN(parsed) && parsed >= 5) {
344
+ return parsed;
345
+ }
346
+ }
347
+ return 15;
348
+ }
349
+ }
350
+
351
+ export default new Service();
@@ -0,0 +1,10 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/MetricPipelineRule";
3
+
4
+ export class Service extends DatabaseService<Model> {
5
+ public constructor() {
6
+ super(Model);
7
+ }
8
+ }
9
+
10
+ export default new Service();
@@ -0,0 +1,10 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/MetricRecordingRule";
3
+
4
+ export class Service extends DatabaseService<Model> {
5
+ public constructor() {
6
+ super(Model);
7
+ }
8
+ }
9
+
10
+ export default new Service();
@@ -0,0 +1,10 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/TraceDropFilter";
3
+
4
+ export class Service extends DatabaseService<Model> {
5
+ public constructor() {
6
+ super(Model);
7
+ }
8
+ }
9
+
10
+ export default new Service();
@@ -0,0 +1,10 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/TracePipelineProcessor";
3
+
4
+ export class Service extends DatabaseService<Model> {
5
+ public constructor() {
6
+ super(Model);
7
+ }
8
+ }
9
+
10
+ export default new Service();
@@ -0,0 +1,10 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/TracePipeline";
3
+
4
+ export class Service extends DatabaseService<Model> {
5
+ public constructor() {
6
+ super(Model);
7
+ }
8
+ }
9
+
10
+ export default new Service();
@@ -0,0 +1,10 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/TraceRecordingRule";
3
+
4
+ export class Service extends DatabaseService<Model> {
5
+ public constructor() {
6
+ super(Model);
7
+ }
8
+ }
9
+
10
+ export default new Service();
@@ -0,0 +1,10 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/TraceScrubRule";
3
+
4
+ export class Service extends DatabaseService<Model> {
5
+ public constructor() {
6
+ super(Model);
7
+ }
8
+ }
9
+
10
+ export default new Service();
@@ -389,6 +389,8 @@ export default class CompareCriteria {
389
389
  value: Array<number> | number;
390
390
  threshold: number;
391
391
  criteriaFilter: CriteriaFilter;
392
+ metricDisplayName?: string | undefined;
393
+ unit?: string | undefined;
392
394
  }): string | null {
393
395
  if (data.value === null || data.value === undefined) {
394
396
  return null;
@@ -412,6 +414,8 @@ export default class CompareCriteria {
412
414
  values: data.value,
413
415
  threshold: data.threshold as number,
414
416
  criteriaFilter: data.criteriaFilter,
417
+ metricDisplayName: data.metricDisplayName,
418
+ unit: data.unit,
415
419
  });
416
420
  }
417
421
 
@@ -432,6 +436,8 @@ export default class CompareCriteria {
432
436
  values: data.value,
433
437
  threshold: data.threshold as number,
434
438
  criteriaFilter: data.criteriaFilter,
439
+ metricDisplayName: data.metricDisplayName,
440
+ unit: data.unit,
435
441
  });
436
442
  }
437
443
 
@@ -452,6 +458,8 @@ export default class CompareCriteria {
452
458
  values: data.value,
453
459
  threshold: data.threshold as number,
454
460
  criteriaFilter: data.criteriaFilter,
461
+ metricDisplayName: data.metricDisplayName,
462
+ unit: data.unit,
455
463
  });
456
464
  }
457
465
 
@@ -472,6 +480,8 @@ export default class CompareCriteria {
472
480
  values: data.value,
473
481
  threshold: data.threshold as number,
474
482
  criteriaFilter: data.criteriaFilter,
483
+ metricDisplayName: data.metricDisplayName,
484
+ unit: data.unit,
475
485
  });
476
486
  }
477
487
 
@@ -492,6 +502,8 @@ export default class CompareCriteria {
492
502
  values: data.value,
493
503
  threshold: data.threshold as number,
494
504
  criteriaFilter: data.criteriaFilter,
505
+ metricDisplayName: data.metricDisplayName,
506
+ unit: data.unit,
495
507
  });
496
508
  }
497
509
 
@@ -512,6 +524,8 @@ export default class CompareCriteria {
512
524
  values: data.value,
513
525
  threshold: data.threshold as number,
514
526
  criteriaFilter: data.criteriaFilter,
527
+ metricDisplayName: data.metricDisplayName,
528
+ unit: data.unit,
515
529
  });
516
530
  }
517
531
 
@@ -526,6 +540,8 @@ export default class CompareCriteria {
526
540
  values: Array<number | boolean> | number | boolean | string;
527
541
  threshold: number | string | boolean;
528
542
  criteriaFilter: CriteriaFilter;
543
+ metricDisplayName?: string | undefined;
544
+ unit?: string | undefined;
529
545
  }): string {
530
546
  // CPU Percent over the last 5 minutes is 10 which is less than the threshold of 20
531
547
  let message: string = "";
@@ -546,7 +562,17 @@ export default class CompareCriteria {
546
562
  message += "All values of";
547
563
  }
548
564
 
549
- message += ` ${data.criteriaFilter.checkOn}`;
565
+ /*
566
+ * Prefer a metric-specific display name over the generic "Metric Value"
567
+ * label when evaluating metric monitors.
568
+ */
569
+ const label: string =
570
+ data.metricDisplayName &&
571
+ data.criteriaFilter.checkOn === CheckOn.MetricValue
572
+ ? data.metricDisplayName
573
+ : data.criteriaFilter.checkOn;
574
+
575
+ message += ` ${label}`;
550
576
 
551
577
  if (data.criteriaFilter.checkOn === CheckOn.DiskUsagePercent) {
552
578
  const diskPath: string =
@@ -562,6 +588,8 @@ export default class CompareCriteria {
562
588
  message += ` over the last ${data.criteriaFilter.evaluateOverTimeOptions.timeValueInMinutes} minutes`;
563
589
  }
564
590
 
591
+ const unitSuffix: string = data.unit ? ` ${data.unit}` : "";
592
+
565
593
  if (
566
594
  data.criteriaFilter.filterType !== FilterType.True &&
567
595
  data.criteriaFilter.filterType !== FilterType.False
@@ -570,29 +598,29 @@ export default class CompareCriteria {
570
598
  data.values,
571
599
  );
572
600
 
573
- message += ` is ${formattedValues}`;
601
+ message += ` is ${formattedValues}${unitSuffix}`;
574
602
 
575
603
  message += " which is";
576
604
  }
577
605
 
578
606
  switch (data.criteriaFilter.filterType) {
579
607
  case FilterType.GreaterThan:
580
- message += ` greater than ${CompareCriteria.formatSingleValue(data.threshold)}. `;
608
+ message += ` greater than ${CompareCriteria.formatSingleValue(data.threshold)}${unitSuffix}. `;
581
609
  break;
582
610
  case FilterType.GreaterThanOrEqualTo:
583
- message += ` greater than or equal to ${CompareCriteria.formatSingleValue(data.threshold)}. `;
611
+ message += ` greater than or equal to ${CompareCriteria.formatSingleValue(data.threshold)}${unitSuffix}. `;
584
612
  break;
585
613
  case FilterType.LessThan:
586
- message += ` less than ${CompareCriteria.formatSingleValue(data.threshold)}. `;
614
+ message += ` less than ${CompareCriteria.formatSingleValue(data.threshold)}${unitSuffix}. `;
587
615
  break;
588
616
  case FilterType.LessThanOrEqualTo:
589
- message += ` less than or equal to ${CompareCriteria.formatSingleValue(data.threshold)}. `;
617
+ message += ` less than or equal to ${CompareCriteria.formatSingleValue(data.threshold)}${unitSuffix}. `;
590
618
  break;
591
619
  case FilterType.NotEqualTo:
592
- message += ` not equal to ${CompareCriteria.formatSingleValue(data.threshold)}. `;
620
+ message += ` not equal to ${CompareCriteria.formatSingleValue(data.threshold)}${unitSuffix}. `;
593
621
  break;
594
622
  case FilterType.EqualTo:
595
- message += ` equal to ${CompareCriteria.formatSingleValue(data.threshold)}. `;
623
+ message += ` equal to ${CompareCriteria.formatSingleValue(data.threshold)}${unitSuffix}. `;
596
624
  break;
597
625
  case FilterType.Contains:
598
626
  message += ` contains ${CompareCriteria.formatSingleValue(data.threshold)}. `;
@@ -621,11 +649,45 @@ export default class CompareCriteria {
621
649
  values: Array<number | boolean> | number | boolean | string,
622
650
  ): string {
623
651
  if (Array.isArray(values)) {
624
- return values
652
+ /*
653
+ * For a small number of values, list them verbatim so the message
654
+ * reads naturally ("is 42, 55, 60"). For larger arrays, summarize
655
+ * — dumping 30+ numbers on one line makes the root cause unreadable
656
+ * and the detailed breakdown is shown in the Breaching Samples
657
+ * table below.
658
+ */
659
+ const MAX_INLINE: number = 5;
660
+
661
+ if (values.length <= MAX_INLINE) {
662
+ return values
663
+ .map((value: number | boolean) => {
664
+ return CompareCriteria.formatSingleValue(value);
665
+ })
666
+ .join(", ");
667
+ }
668
+
669
+ const numericValues: Array<number> = values.filter(
670
+ (value: number | boolean): value is number => {
671
+ return typeof value === "number" && Number.isFinite(value);
672
+ },
673
+ );
674
+
675
+ if (numericValues.length === values.length && numericValues.length > 0) {
676
+ const min: number = Math.min(...numericValues);
677
+ const max: number = Math.max(...numericValues);
678
+ return `${numericValues.length} samples between ${CompareCriteria.formatSingleValue(
679
+ min,
680
+ )} and ${CompareCriteria.formatSingleValue(max)}`;
681
+ }
682
+
683
+ // Fall back to a truncated list when not all values are numeric
684
+ const head: string = values
685
+ .slice(0, MAX_INLINE)
625
686
  .map((value: number | boolean) => {
626
687
  return CompareCriteria.formatSingleValue(value);
627
688
  })
628
689
  .join(", ");
690
+ return `${head}, … (${values.length} values total)`;
629
691
  }
630
692
 
631
693
  return CompareCriteria.formatSingleValue(values);