@oneuptime/common 12.0.9 → 12.0.11
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/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceLink.ts +116 -0
- package/Models/DatabaseModels/RunnerJob.ts +17 -2
- package/Server/API/LlmProviderAPI.ts +8 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +240 -0
- package/Server/Services/NetworkDeviceLinkService.ts +338 -2
- package/Server/Services/NetworkDeviceService.ts +265 -12
- package/Server/Services/RunnerJobService.ts +24 -1
- package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +19 -5
- package/Server/Types/Database/JSONColumnQuery.ts +42 -4
- package/Server/Utils/LLM/LLMService.ts +108 -19
- package/Server/Utils/Monitor/MonitorAlert.ts +22 -115
- package/Server/Utils/Monitor/MonitorIncident.ts +7 -337
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +27 -2
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +32 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +469 -0
- package/Server/Utils/SSRFProtection.ts +5 -4
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +186 -38
- package/Tests/App/AdminDashboard/AdminUsersBulkAddToProject.test.tsx +955 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProject.test.ts +778 -0
- package/Tests/App/AdminDashboard/BulkAddUsersToProjectModal.test.tsx +1051 -0
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +420 -112
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +81 -6
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +543 -0
- package/Tests/App/Dashboard/PayAsYouGoFreePlan.test.ts +125 -0
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +612 -0
- package/Tests/App/Dashboard/SloWidgetChartRendering.test.tsx +536 -0
- package/Tests/App/Dashboard/UserElementEmail.test.tsx +447 -0
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +284 -0
- package/Tests/Models/AnalyticsModels/CommonModel.test.ts +312 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +250 -0
- package/Tests/Server/Infrastructure/Postgres/EpisodeMemberNotifyIndexesMigration.test.ts +46 -18
- package/Tests/Server/Services/NetworkDeviceLabelRuleRun.test.ts +618 -0
- package/Tests/Server/Services/NetworkDeviceLinkParentValidation.test.ts +1043 -0
- package/Tests/Server/Services/NetworkSiteAssignmentRuleRun.test.ts +706 -0
- package/Tests/Server/Services/RunnerJobEnqueue.test.ts +412 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredPricing.test.ts +125 -0
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +508 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +2 -2
- package/Tests/Server/Utils/AI/LLMServiceRetryPolicy.test.ts +450 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +556 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +547 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +30 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +699 -0
- package/Tests/Server/Utils/SeriesResourceLabels.test.ts +55 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +668 -0
- package/Tests/Types/API/URLMalformedTolerance.test.ts +301 -0
- package/Tests/Types/API/URLTelScheme.test.ts +261 -0
- package/Tests/Types/API/URLUnsupportedScheme.test.ts +148 -0
- package/Tests/Types/Billing/PayAsYouGoPricing.test.ts +64 -0
- package/Tests/Types/CustomField/CustomFieldTypeCorrespondence.test.ts +107 -0
- package/Tests/Types/Monitor/MonitorType.test.ts +62 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +184 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +124 -0
- package/Tests/Types/Workflow/BaseModelComponents.test.ts +17 -0
- package/Tests/UI/Components/Button.test.tsx +279 -0
- package/Tests/UI/Components/Charts/ChartXAxisDataKey.test.tsx +256 -0
- package/Tests/UI/Components/Charts/DataPointUtil.test.ts +323 -0
- package/Tests/UI/Components/DictionaryValuePlaceholderAndSuggestions.test.tsx +595 -0
- package/Tests/UI/Components/Icon.test.tsx +57 -0
- package/Tests/UI/Components/List.test.tsx +20 -3
- package/Tests/UI/Components/LogsSavedViewsDropdown.test.tsx +304 -0
- package/Tests/UI/Components/LogsViewerClearSavedView.test.tsx +179 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +349 -1
- package/Tests/UI/Components/Pagination.test.tsx +721 -146
- package/Tests/UI/Components/PaginationIntegration.test.tsx +386 -0
- package/Tests/UI/Components/PaginationUtil.test.ts +417 -0
- package/Tests/UI/Components/TelemetrySavedViewsDropdown.test.tsx +343 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnControl.test.ts +289 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnOperators.test.ts +271 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRow.test.ts +119 -0
- package/Tests/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.test.ts +968 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +766 -0
- package/Tests/UI/Utils/RunNetworkRule.test.ts +320 -0
- package/Tests/Utils/APIRetryPolicy.test.ts +823 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +502 -0
- package/Tests/Utils/Monitor/NetworkTopologyDeclaredHierarchy.test.ts +759 -0
- package/Tests/Utils/NetworkAutomation/RuleRunSummary.test.ts +286 -0
- package/Tests/Utils/Schema/BaseSchema.test.ts +651 -0
- package/Types/API/Protocol.ts +9 -0
- package/Types/API/URL.ts +286 -20
- package/Types/Billing/PayAsYouGoPricing.ts +47 -0
- package/Types/CustomField/CustomFieldType.ts +28 -0
- package/Types/Monitor/MonitorType.ts +11 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +121 -0
- package/Types/Runbook/RunbookStepType.ts +23 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +7 -0
- package/UI/Components/Button/Button.tsx +14 -22
- package/UI/Components/Charts/Area/AreaChart.tsx +4 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +4 -2
- package/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.ts +13 -0
- package/UI/Components/Charts/Line/LineChart.tsx +4 -2
- package/UI/Components/Charts/Types/XAxis/XAxis.ts +6 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +15 -12
- package/UI/Components/Dictionary/Dictionary.tsx +4 -39
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/UI/Components/LogsViewer/LogsViewer.tsx +5 -1
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +8 -0
- package/UI/Components/LogsViewer/components/LogsPagination.tsx +29 -91
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +2 -0
- package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +55 -0
- package/UI/Components/Pagination/Pagination.tsx +373 -328
- package/UI/Components/Pagination/PaginationUtil.ts +263 -0
- package/UI/Components/Pagination/Screenshots/README.md +15 -0
- package/UI/Components/Pagination/Screenshots/pagination-before-after.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-go-to-page-modal.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-long-list.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-mobile.png +0 -0
- package/UI/Components/Pagination/Screenshots/pagination-telemetry.png +0 -0
- package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +66 -1
- package/UI/Components/TelemetryViewer/components/TelemetryPagination.tsx +38 -92
- package/UI/Components/TextArea/TextArea.tsx +3 -0
- package/UI/Components/Workflow/ArgumentsForm.tsx +18 -1
- package/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.tsx +177 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.tsx +243 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnControl.ts +255 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.tsx +178 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnOperators.ts +206 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRow.ts +157 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.ts +517 -0
- package/UI/Components/Workflow/ColumnEditor/ColumnValueInput.tsx +482 -0
- package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +131 -0
- package/UI/Components/Workflow/ColumnEditor/ModelRecordForm.tsx +163 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +284 -100
- package/UI/Utils/NetworkAutomation/RunNetworkRule.ts +122 -0
- package/Utils/API.ts +243 -36
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +45 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +42 -2
- package/Utils/NetworkAutomation/RuleRunSummary.ts +147 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js +115 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLink.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +17 -2
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Server/API/LlmProviderAPI.js +9 -1
- package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js +33 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787400000000-AddDeviceRoleAndDeclaredLinkParent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +202 -0
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js +253 -1
- package/build/dist/Server/Services/NetworkDeviceLinkService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +223 -12
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +20 -2
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +12 -5
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
- package/build/dist/Server/Types/Database/JSONColumnQuery.js +40 -4
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +78 -22
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -98
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +7 -254
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +14 -2
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +27 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +285 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -0
- package/build/dist/Server/Utils/SSRFProtection.js +5 -4
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +128 -35
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Types/API/Protocol.js +9 -0
- package/build/dist/Types/API/Protocol.js.map +1 -1
- package/build/dist/Types/API/URL.js +241 -18
- package/build/dist/Types/API/URL.js.map +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js +41 -0
- package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldType.js +26 -0
- package/build/dist/Types/CustomField/CustomFieldType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +10 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +39 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +21 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +10 -16
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js +12 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Types/ChartDataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +17 -10
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/Dictionary.js +4 -18
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +3 -3
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +5 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js +14 -44
- package/build/dist/UI/Components/LogsViewer/components/LogsPagination.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +42 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +174 -199
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- package/build/dist/UI/Components/Pagination/PaginationUtil.js +160 -0
- package/build/dist/UI/Components/Pagination/PaginationUtil.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +20 -2
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js +23 -47
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryPagination.js.map +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js +1 -1
- package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +14 -2
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js +86 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/AddColumnDropdown.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js +119 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnConditionRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js +206 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnControl.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js +57 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnFieldRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js +150 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnOperators.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js +67 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRow.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js +309 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnRowSerialization.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js +204 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ColumnValueInput.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +66 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js +83 -0
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelRecordForm.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +187 -68
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -1
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js +75 -0
- package/build/dist/UI/Utils/NetworkAutomation/RunNetworkRule.js.map +1 -0
- package/build/dist/Utils/API.js +175 -34
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +37 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +25 -3
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js +78 -0
- package/build/dist/Utils/NetworkAutomation/RuleRunSummary.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,547 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The incident creation path pulls the native isolated-vm addon through
|
|
3
|
+
* its template renderer (MonitorIncident → MonitorTemplateUtil → VMAPI →
|
|
4
|
+
* VMRunner). Nothing under test here touches the sandbox, and the
|
|
5
|
+
* prebuilt binary cannot always dlopen in the test environment — so stub
|
|
6
|
+
* the module out before anything imports it.
|
|
7
|
+
*/
|
|
8
|
+
jest.mock("isolated-vm", () => {
|
|
9
|
+
return {};
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
import Incident from "../../../../Models/DatabaseModels/Incident";
|
|
13
|
+
import Monitor from "../../../../Models/DatabaseModels/Monitor";
|
|
14
|
+
import CephClusterService from "../../../../Server/Services/CephClusterService";
|
|
15
|
+
import DockerHostService from "../../../../Server/Services/DockerHostService";
|
|
16
|
+
import DockerSwarmClusterService from "../../../../Server/Services/DockerSwarmClusterService";
|
|
17
|
+
import HostService from "../../../../Server/Services/HostService";
|
|
18
|
+
import IncidentService from "../../../../Server/Services/IncidentService";
|
|
19
|
+
import IoTFleetService from "../../../../Server/Services/IoTFleetService";
|
|
20
|
+
import KubernetesClusterService from "../../../../Server/Services/KubernetesClusterService";
|
|
21
|
+
import NetworkDeviceOwnerUserService from "../../../../Server/Services/NetworkDeviceOwnerUserService";
|
|
22
|
+
import PodmanHostService from "../../../../Server/Services/PodmanHostService";
|
|
23
|
+
import ProxmoxClusterService from "../../../../Server/Services/ProxmoxClusterService";
|
|
24
|
+
import ServiceService from "../../../../Server/Services/ServiceService";
|
|
25
|
+
import ProjectScopedReferenceValidator from "../../../../Server/Utils/Database/ProjectScopedReferenceValidator";
|
|
26
|
+
import MonitorClusterContextUtil from "../../../../Server/Utils/Monitor/MonitorClusterContext";
|
|
27
|
+
import MonitorIncident from "../../../../Server/Utils/Monitor/MonitorIncident";
|
|
28
|
+
import Dictionary from "../../../../Types/Dictionary";
|
|
29
|
+
import { JSONObject } from "../../../../Types/JSON";
|
|
30
|
+
import MonitorCriteriaInstance from "../../../../Types/Monitor/MonitorCriteriaInstance";
|
|
31
|
+
import MonitorType from "../../../../Types/Monitor/MonitorType";
|
|
32
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
33
|
+
import { PerSeriesCriteriaMatch } from "../../../../Types/Probe/ProbeApiIngestResponse";
|
|
34
|
+
import ProbeMonitorResponse from "../../../../Types/Probe/ProbeMonitorResponse";
|
|
35
|
+
import {
|
|
36
|
+
afterEach,
|
|
37
|
+
beforeEach,
|
|
38
|
+
describe,
|
|
39
|
+
expect,
|
|
40
|
+
it,
|
|
41
|
+
jest,
|
|
42
|
+
} from "@jest/globals";
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* The incident half of the series-resource-linking contract, driven
|
|
46
|
+
* through the real incident creation path.
|
|
47
|
+
*
|
|
48
|
+
* Incidents were never affected by the alert bug — they already resolved
|
|
49
|
+
* every host spelling through the shared SeriesResourceLabels key map,
|
|
50
|
+
* including `oneuptime.host.name`. The first test pins that, so a future
|
|
51
|
+
* refactor cannot quietly take it away.
|
|
52
|
+
*
|
|
53
|
+
* They did share three narrower gaps with alerts, and each of the next
|
|
54
|
+
* three tests fails on the pre-fix code:
|
|
55
|
+
* - `oneuptime.service.name` was absent from the service key list even
|
|
56
|
+
* though ingest stamps it alongside `oneuptime.service.id`;
|
|
57
|
+
* - IoT fleet names were extracted into the refs and then dropped —
|
|
58
|
+
* nothing ever resolved them;
|
|
59
|
+
* - Docker Swarm clusters had no key at all, so a monitor grouped by
|
|
60
|
+
* the attribute the Swarm agent stamps linked nothing.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
64
|
+
"11111111-1111-4111-8111-111111111111",
|
|
65
|
+
);
|
|
66
|
+
const MONITOR_ID: ObjectID = new ObjectID(
|
|
67
|
+
"22222222-2222-4222-8222-222222222222",
|
|
68
|
+
);
|
|
69
|
+
const SEVERITY_ID: ObjectID = new ObjectID(
|
|
70
|
+
"55555555-5555-4555-8555-555555555555",
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
function monitor(): Monitor {
|
|
74
|
+
const model: Monitor = new Monitor();
|
|
75
|
+
model._id = MONITOR_ID.toString();
|
|
76
|
+
model.projectId = PROJECT_ID;
|
|
77
|
+
model.monitorType = MonitorType.Metrics;
|
|
78
|
+
model.name = "StarShip Green node performance CPU";
|
|
79
|
+
return model;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function criteriaInstance(): MonitorCriteriaInstance {
|
|
83
|
+
const instance: MonitorCriteriaInstance = new MonitorCriteriaInstance();
|
|
84
|
+
instance.data!.id = "criteria-1";
|
|
85
|
+
instance.data!.name = "CPU utilization > 90%";
|
|
86
|
+
instance.data!.createIncidents = true;
|
|
87
|
+
instance.data!.createAlerts = false;
|
|
88
|
+
instance.data!.incidents = [
|
|
89
|
+
{
|
|
90
|
+
id: "incident-template-1",
|
|
91
|
+
title: "CPU utilization > 90%",
|
|
92
|
+
description: "A node breached the CPU threshold.",
|
|
93
|
+
incidentSeverityId: SEVERITY_ID,
|
|
94
|
+
autoResolveIncident: false,
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
return instance;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const dataToProcess: ProbeMonitorResponse = {
|
|
101
|
+
projectId: PROJECT_ID,
|
|
102
|
+
monitorId: MONITOR_ID,
|
|
103
|
+
monitoredAt: new Date("2026-08-13T03:33:00.000Z"),
|
|
104
|
+
} as unknown as ProbeMonitorResponse;
|
|
105
|
+
|
|
106
|
+
const NO_AUTO_RESOLVE: Dictionary<Array<string>> = {};
|
|
107
|
+
|
|
108
|
+
function series(
|
|
109
|
+
labels: JSONObject,
|
|
110
|
+
fingerprint: string,
|
|
111
|
+
): PerSeriesCriteriaMatch {
|
|
112
|
+
return {
|
|
113
|
+
criteriaMetId: "criteria-1",
|
|
114
|
+
fingerprint: fingerprint,
|
|
115
|
+
labels: labels,
|
|
116
|
+
rootCause: "CPU is above 90%",
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function idsOn(
|
|
121
|
+
relation: Array<{ _id?: string | undefined }> | undefined,
|
|
122
|
+
): Array<string> {
|
|
123
|
+
return (relation || []).map((item: { _id?: string | undefined }): string => {
|
|
124
|
+
return String(item._id);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
describe("Incidents link the resources their series identifies", () => {
|
|
129
|
+
let createdIncidents: Array<Incident> = [];
|
|
130
|
+
|
|
131
|
+
let hostRows: Array<{ _id: string }> = [];
|
|
132
|
+
let serviceRows: Array<{ _id: string }> = [];
|
|
133
|
+
let kubernetesClusterRows: Array<{ _id: string }> = [];
|
|
134
|
+
let dockerHostRows: Array<{ _id: string }> = [];
|
|
135
|
+
let podmanHostRows: Array<{ _id: string }> = [];
|
|
136
|
+
let proxmoxClusterRows: Array<{ _id: string }> = [];
|
|
137
|
+
let cephClusterRows: Array<{ _id: string }> = [];
|
|
138
|
+
let dockerSwarmClusterRows: Array<{ _id: string }> = [];
|
|
139
|
+
let iotFleetRows: Array<{ _id: string }> = [];
|
|
140
|
+
|
|
141
|
+
let clusterContext: {
|
|
142
|
+
proxmoxClusterIds: Array<string>;
|
|
143
|
+
cephClusterIds: Array<string>;
|
|
144
|
+
dockerSwarmClusterIds: Array<string>;
|
|
145
|
+
iotFleetIds: Array<string>;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
beforeEach(() => {
|
|
149
|
+
createdIncidents = [];
|
|
150
|
+
|
|
151
|
+
hostRows = [];
|
|
152
|
+
serviceRows = [];
|
|
153
|
+
kubernetesClusterRows = [];
|
|
154
|
+
dockerHostRows = [];
|
|
155
|
+
podmanHostRows = [];
|
|
156
|
+
proxmoxClusterRows = [];
|
|
157
|
+
cephClusterRows = [];
|
|
158
|
+
dockerSwarmClusterRows = [];
|
|
159
|
+
iotFleetRows = [];
|
|
160
|
+
|
|
161
|
+
clusterContext = {
|
|
162
|
+
proxmoxClusterIds: [],
|
|
163
|
+
cephClusterIds: [],
|
|
164
|
+
dockerSwarmClusterIds: [],
|
|
165
|
+
iotFleetIds: [],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
// No incident is already open for this monitor.
|
|
169
|
+
jest.spyOn(IncidentService, "findBy").mockResolvedValue([]);
|
|
170
|
+
|
|
171
|
+
jest
|
|
172
|
+
.spyOn(ProjectScopedReferenceValidator, "isUsableInProject")
|
|
173
|
+
.mockResolvedValue(true);
|
|
174
|
+
|
|
175
|
+
jest
|
|
176
|
+
.spyOn(MonitorClusterContextUtil, "resolveClusterContextForMonitor")
|
|
177
|
+
.mockImplementation(async () => {
|
|
178
|
+
return clusterContext;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
jest
|
|
182
|
+
.spyOn(NetworkDeviceOwnerUserService, "getDeviceOwnersForMonitor")
|
|
183
|
+
.mockResolvedValue({ ownerUserIds: [], ownerTeamIds: [] });
|
|
184
|
+
|
|
185
|
+
jest.spyOn(HostService, "findBy").mockImplementation(async () => {
|
|
186
|
+
return hostRows as never;
|
|
187
|
+
});
|
|
188
|
+
jest.spyOn(ServiceService, "findBy").mockImplementation(async () => {
|
|
189
|
+
return serviceRows as never;
|
|
190
|
+
});
|
|
191
|
+
jest
|
|
192
|
+
.spyOn(KubernetesClusterService, "findBy")
|
|
193
|
+
.mockImplementation(async () => {
|
|
194
|
+
return kubernetesClusterRows as never;
|
|
195
|
+
});
|
|
196
|
+
jest.spyOn(DockerHostService, "findBy").mockImplementation(async () => {
|
|
197
|
+
return dockerHostRows as never;
|
|
198
|
+
});
|
|
199
|
+
jest.spyOn(PodmanHostService, "findBy").mockImplementation(async () => {
|
|
200
|
+
return podmanHostRows as never;
|
|
201
|
+
});
|
|
202
|
+
jest.spyOn(ProxmoxClusterService, "findBy").mockImplementation(async () => {
|
|
203
|
+
return proxmoxClusterRows as never;
|
|
204
|
+
});
|
|
205
|
+
jest.spyOn(CephClusterService, "findBy").mockImplementation(async () => {
|
|
206
|
+
return cephClusterRows as never;
|
|
207
|
+
});
|
|
208
|
+
jest
|
|
209
|
+
.spyOn(DockerSwarmClusterService, "findBy")
|
|
210
|
+
.mockImplementation(async () => {
|
|
211
|
+
return dockerSwarmClusterRows as never;
|
|
212
|
+
});
|
|
213
|
+
jest.spyOn(IoTFleetService, "findBy").mockImplementation(async () => {
|
|
214
|
+
return iotFleetRows as never;
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
jest
|
|
218
|
+
.spyOn(IncidentService, "create")
|
|
219
|
+
.mockImplementation(async (createBy: unknown): Promise<Incident> => {
|
|
220
|
+
const incident: Incident = (createBy as { data: Incident }).data;
|
|
221
|
+
createdIncidents.push(incident);
|
|
222
|
+
incident._id = new ObjectID(
|
|
223
|
+
"66666666-6666-4666-8666-666666666666",
|
|
224
|
+
).toString();
|
|
225
|
+
return incident;
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
jest.spyOn(IncidentService, "addOwners").mockResolvedValue(undefined);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
afterEach(() => {
|
|
232
|
+
jest.restoreAllMocks();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("links the host when the series is grouped by oneuptime.host.name", async () => {
|
|
236
|
+
/*
|
|
237
|
+
* The spelling that broke alerts. Incidents always handled it — this
|
|
238
|
+
* test exists to keep it that way.
|
|
239
|
+
*/
|
|
240
|
+
hostRows = [{ _id: "host-prodgreen000002" }];
|
|
241
|
+
|
|
242
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
243
|
+
criteriaInstance: criteriaInstance(),
|
|
244
|
+
monitor: monitor(),
|
|
245
|
+
dataToProcess: dataToProcess,
|
|
246
|
+
rootCause: "CPU is above 90%",
|
|
247
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
248
|
+
matchesPerSeries: [
|
|
249
|
+
series({ "oneuptime.host.name": "prodgreen000002" }, "fp-1"),
|
|
250
|
+
],
|
|
251
|
+
props: {},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
expect(createdIncidents).toHaveLength(1);
|
|
255
|
+
expect(idsOn(createdIncidents[0]!.hosts)).toEqual(["host-prodgreen000002"]);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it.each([
|
|
259
|
+
["resource.oneuptime.host.name"],
|
|
260
|
+
["oneuptime.host.name"],
|
|
261
|
+
["resource.host.name"],
|
|
262
|
+
["host.name"],
|
|
263
|
+
])("links the host from the %s spelling", async (key: string) => {
|
|
264
|
+
hostRows = [{ _id: "host-1" }];
|
|
265
|
+
|
|
266
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
267
|
+
criteriaInstance: criteriaInstance(),
|
|
268
|
+
monitor: monitor(),
|
|
269
|
+
dataToProcess: dataToProcess,
|
|
270
|
+
rootCause: "CPU is above 90%",
|
|
271
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
272
|
+
matchesPerSeries: [series({ [key]: "web-1" }, "fp-1")],
|
|
273
|
+
props: {},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
expect(idsOn(createdIncidents[0]!.hosts)).toEqual(["host-1"]);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("links the service from the oneuptime-stamped service name", async () => {
|
|
280
|
+
// Gap shared with alerts: the key was missing from the service list.
|
|
281
|
+
serviceRows = [{ _id: "service-1" }];
|
|
282
|
+
|
|
283
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
284
|
+
criteriaInstance: criteriaInstance(),
|
|
285
|
+
monitor: monitor(),
|
|
286
|
+
dataToProcess: dataToProcess,
|
|
287
|
+
rootCause: "CPU is above 90%",
|
|
288
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
289
|
+
matchesPerSeries: [
|
|
290
|
+
series({ "oneuptime.service.name": "checkout" }, "fp-1"),
|
|
291
|
+
],
|
|
292
|
+
props: {},
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
expect(idsOn(createdIncidents[0]!.services)).toEqual(["service-1"]);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it("links the IoT fleet the series names", async () => {
|
|
299
|
+
/*
|
|
300
|
+
* Gap shared with alerts: iotFleetNames was extracted from the
|
|
301
|
+
* labels and then never resolved by either creation path.
|
|
302
|
+
*/
|
|
303
|
+
iotFleetRows = [{ _id: "fleet-1" }];
|
|
304
|
+
|
|
305
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
306
|
+
criteriaInstance: criteriaInstance(),
|
|
307
|
+
monitor: monitor(),
|
|
308
|
+
dataToProcess: dataToProcess,
|
|
309
|
+
rootCause: "Fleet battery is low",
|
|
310
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
311
|
+
matchesPerSeries: [
|
|
312
|
+
series({ "iot.fleet.name": "warehouse-sensors" }, "fp-1"),
|
|
313
|
+
],
|
|
314
|
+
props: {},
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
expect(idsOn(createdIncidents[0]!.iotFleets)).toEqual(["fleet-1"]);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("links the docker swarm cluster the series names", async () => {
|
|
321
|
+
// Gap shared with alerts: no swarm key existed in the map at all.
|
|
322
|
+
dockerSwarmClusterRows = [{ _id: "swarm-1" }];
|
|
323
|
+
|
|
324
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
325
|
+
criteriaInstance: criteriaInstance(),
|
|
326
|
+
monitor: monitor(),
|
|
327
|
+
dataToProcess: dataToProcess,
|
|
328
|
+
rootCause: "Swarm service is down",
|
|
329
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
330
|
+
matchesPerSeries: [
|
|
331
|
+
series({ "docker.swarm.cluster.name": "prod-swarm" }, "fp-1"),
|
|
332
|
+
],
|
|
333
|
+
props: {},
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
expect(idsOn(createdIncidents[0]!.dockerSwarmClusters)).toEqual([
|
|
337
|
+
"swarm-1",
|
|
338
|
+
]);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("links every resource type one series names", async () => {
|
|
342
|
+
hostRows = [{ _id: "host-1" }];
|
|
343
|
+
serviceRows = [{ _id: "service-1" }];
|
|
344
|
+
kubernetesClusterRows = [{ _id: "cluster-1" }];
|
|
345
|
+
dockerHostRows = [{ _id: "docker-1" }];
|
|
346
|
+
podmanHostRows = [{ _id: "podman-1" }];
|
|
347
|
+
proxmoxClusterRows = [{ _id: "pve-1" }];
|
|
348
|
+
cephClusterRows = [{ _id: "ceph-1" }];
|
|
349
|
+
dockerSwarmClusterRows = [{ _id: "swarm-1" }];
|
|
350
|
+
iotFleetRows = [{ _id: "fleet-1" }];
|
|
351
|
+
|
|
352
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
353
|
+
criteriaInstance: criteriaInstance(),
|
|
354
|
+
monitor: monitor(),
|
|
355
|
+
dataToProcess: dataToProcess,
|
|
356
|
+
rootCause: "CPU is above 90%",
|
|
357
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
358
|
+
matchesPerSeries: [
|
|
359
|
+
series(
|
|
360
|
+
{
|
|
361
|
+
"oneuptime.host.name": "web-1",
|
|
362
|
+
"oneuptime.service.name": "checkout",
|
|
363
|
+
"k8s.cluster.name": "prod",
|
|
364
|
+
"oneuptime.docker.host.name": "docker-box",
|
|
365
|
+
"oneuptime.podman.host.name": "podman-box",
|
|
366
|
+
"proxmox.cluster.name": "pve",
|
|
367
|
+
"ceph.cluster.name": "ceph",
|
|
368
|
+
"docker.swarm.cluster.name": "swarm",
|
|
369
|
+
"iot.fleet.name": "fleet",
|
|
370
|
+
},
|
|
371
|
+
"fp-1",
|
|
372
|
+
),
|
|
373
|
+
],
|
|
374
|
+
props: {},
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const incident: Incident = createdIncidents[0]!;
|
|
378
|
+
|
|
379
|
+
expect(idsOn(incident.hosts)).toEqual(["host-1"]);
|
|
380
|
+
expect(idsOn(incident.services)).toEqual(["service-1"]);
|
|
381
|
+
expect(idsOn(incident.kubernetesClusters)).toEqual(["cluster-1"]);
|
|
382
|
+
expect(idsOn(incident.dockerHosts)).toEqual(["docker-1"]);
|
|
383
|
+
expect(idsOn(incident.podmanHosts)).toEqual(["podman-1"]);
|
|
384
|
+
expect(idsOn(incident.proxmoxClusters)).toEqual(["pve-1"]);
|
|
385
|
+
expect(idsOn(incident.cephClusters)).toEqual(["ceph-1"]);
|
|
386
|
+
expect(idsOn(incident.dockerSwarmClusters)).toEqual(["swarm-1"]);
|
|
387
|
+
expect(idsOn(incident.iotFleets)).toEqual(["fleet-1"]);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("gives each series its own resource, with no cross-contamination", async () => {
|
|
391
|
+
const hostByName: Record<string, string> = {
|
|
392
|
+
prodgreen000002: "host-2",
|
|
393
|
+
prodgreen000007: "host-7",
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
jest.spyOn(HostService, "findBy").mockImplementation((async (
|
|
397
|
+
args: unknown,
|
|
398
|
+
): Promise<Array<{ _id: string | undefined }>> => {
|
|
399
|
+
const query: JSONObject = (args as { query: JSONObject }).query;
|
|
400
|
+
const includes: { values?: Array<string> } = query["hostIdentifier"] as {
|
|
401
|
+
values?: Array<string>;
|
|
402
|
+
};
|
|
403
|
+
const name: string = String((includes.values || [])[0]);
|
|
404
|
+
return [{ _id: hostByName[name] }];
|
|
405
|
+
}) as never);
|
|
406
|
+
|
|
407
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
408
|
+
criteriaInstance: criteriaInstance(),
|
|
409
|
+
monitor: monitor(),
|
|
410
|
+
dataToProcess: dataToProcess,
|
|
411
|
+
rootCause: "CPU is above 90%",
|
|
412
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
413
|
+
matchesPerSeries: [
|
|
414
|
+
series({ "oneuptime.host.name": "prodgreen000002" }, "fp-1"),
|
|
415
|
+
series({ "oneuptime.host.name": "prodgreen000007" }, "fp-2"),
|
|
416
|
+
],
|
|
417
|
+
props: {},
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
expect(createdIncidents).toHaveLength(2);
|
|
421
|
+
expect(idsOn(createdIncidents[0]!.hosts)).toEqual(["host-2"]);
|
|
422
|
+
expect(idsOn(createdIncidents[1]!.hosts)).toEqual(["host-7"]);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it("links nothing, and still opens the incident, for an ungrouped monitor", async () => {
|
|
426
|
+
hostRows = [{ _id: "host-1" }];
|
|
427
|
+
|
|
428
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
429
|
+
criteriaInstance: criteriaInstance(),
|
|
430
|
+
monitor: monitor(),
|
|
431
|
+
dataToProcess: dataToProcess,
|
|
432
|
+
rootCause: "CPU is above 90%",
|
|
433
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
434
|
+
props: {},
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
expect(createdIncidents).toHaveLength(1);
|
|
438
|
+
expect(createdIncidents[0]!.hosts).toBeUndefined();
|
|
439
|
+
expect(HostService.findBy).not.toHaveBeenCalled();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it("merges the monitor's step-config cluster with the one the series names", async () => {
|
|
443
|
+
proxmoxClusterRows = [{ _id: "pve-from-label" }];
|
|
444
|
+
clusterContext = {
|
|
445
|
+
proxmoxClusterIds: ["pve-from-step-config"],
|
|
446
|
+
cephClusterIds: [],
|
|
447
|
+
dockerSwarmClusterIds: [],
|
|
448
|
+
iotFleetIds: [],
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
452
|
+
criteriaInstance: criteriaInstance(),
|
|
453
|
+
monitor: monitor(),
|
|
454
|
+
dataToProcess: dataToProcess,
|
|
455
|
+
rootCause: "CPU is above 90%",
|
|
456
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
457
|
+
matchesPerSeries: [series({ "proxmox.cluster.name": "pve" }, "fp-1")],
|
|
458
|
+
props: {},
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
expect(idsOn(createdIncidents[0]!.proxmoxClusters).sort()).toEqual([
|
|
462
|
+
"pve-from-label",
|
|
463
|
+
"pve-from-step-config",
|
|
464
|
+
]);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it("still attaches the step-config cluster to an ungrouped incident", async () => {
|
|
468
|
+
clusterContext = {
|
|
469
|
+
proxmoxClusterIds: [],
|
|
470
|
+
cephClusterIds: ["ceph-1"],
|
|
471
|
+
dockerSwarmClusterIds: [],
|
|
472
|
+
iotFleetIds: [],
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
476
|
+
criteriaInstance: criteriaInstance(),
|
|
477
|
+
monitor: monitor(),
|
|
478
|
+
dataToProcess: dataToProcess,
|
|
479
|
+
rootCause: "Ceph health is in error",
|
|
480
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
481
|
+
props: {},
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
expect(idsOn(createdIncidents[0]!.cephClusters)).toEqual(["ceph-1"]);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it("leaves the incident unlinked when the resource is not in this project", async () => {
|
|
488
|
+
hostRows = [];
|
|
489
|
+
|
|
490
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
491
|
+
criteriaInstance: criteriaInstance(),
|
|
492
|
+
monitor: monitor(),
|
|
493
|
+
dataToProcess: dataToProcess,
|
|
494
|
+
rootCause: "CPU is above 90%",
|
|
495
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
496
|
+
matchesPerSeries: [
|
|
497
|
+
series({ "oneuptime.host.name": "elsewhere" }, "fp-1"),
|
|
498
|
+
],
|
|
499
|
+
props: {},
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
expect(createdIncidents).toHaveLength(1);
|
|
503
|
+
expect(createdIncidents[0]!.hosts).toBeUndefined();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it("scopes the inventory lookup to the monitor's project", async () => {
|
|
507
|
+
hostRows = [{ _id: "host-1" }];
|
|
508
|
+
|
|
509
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
510
|
+
criteriaInstance: criteriaInstance(),
|
|
511
|
+
monitor: monitor(),
|
|
512
|
+
dataToProcess: dataToProcess,
|
|
513
|
+
rootCause: "CPU is above 90%",
|
|
514
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
515
|
+
matchesPerSeries: [series({ "oneuptime.host.name": "web-1" }, "fp-1")],
|
|
516
|
+
props: {},
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
const call: { query: JSONObject } = (
|
|
520
|
+
HostService.findBy as unknown as {
|
|
521
|
+
mock: { calls: Array<Array<{ query: JSONObject }>> };
|
|
522
|
+
}
|
|
523
|
+
).mock.calls[0]![0]!;
|
|
524
|
+
|
|
525
|
+
expect(call.query["projectId"]).toBe(PROJECT_ID);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it("keeps the raw series labels on the incident", async () => {
|
|
529
|
+
hostRows = [{ _id: "host-1" }];
|
|
530
|
+
|
|
531
|
+
await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
|
|
532
|
+
criteriaInstance: criteriaInstance(),
|
|
533
|
+
monitor: monitor(),
|
|
534
|
+
dataToProcess: dataToProcess,
|
|
535
|
+
rootCause: "CPU is above 90%",
|
|
536
|
+
autoResolveCriteriaInstanceIdIncidentIdsDictionary: NO_AUTO_RESOLVE,
|
|
537
|
+
matchesPerSeries: [
|
|
538
|
+
series({ "oneuptime.host.name": "prodgreen000002" }, "fp-1"),
|
|
539
|
+
],
|
|
540
|
+
props: {},
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
expect(createdIncidents[0]!.seriesLabels).toEqual({
|
|
544
|
+
"oneuptime.host.name": "prodgreen000002",
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
});
|
|
@@ -15,6 +15,7 @@ function emptyMaintained(): MaintainedResourceKeys {
|
|
|
15
15
|
kubernetesClusters: { ids: new Set<string>(), names: new Set<string>() },
|
|
16
16
|
proxmoxClusters: { ids: new Set<string>(), names: new Set<string>() },
|
|
17
17
|
cephClusters: { ids: new Set<string>(), names: new Set<string>() },
|
|
18
|
+
dockerSwarmClusters: { ids: new Set<string>(), names: new Set<string>() },
|
|
18
19
|
iotFleets: { ids: new Set<string>(), names: new Set<string>() },
|
|
19
20
|
services: { ids: new Set<string>(), names: new Set<string>() },
|
|
20
21
|
};
|
|
@@ -207,6 +208,35 @@ describe("MonitorMaintenanceSuppression.getSuppressedFingerprintsForMaintainedRe
|
|
|
207
208
|
expect(Array.from(result)).toEqual(["fpFleet"]);
|
|
208
209
|
});
|
|
209
210
|
|
|
211
|
+
/*
|
|
212
|
+
* Linking and suppression read the same key map on purpose. Once a
|
|
213
|
+
* series carrying `docker.swarm.cluster.name` could be LINKED to a
|
|
214
|
+
* Swarm cluster, a maintenance window on that cluster had to silence
|
|
215
|
+
* it too — otherwise the alert it raises mid-window shows up on the
|
|
216
|
+
* very cluster the window covers.
|
|
217
|
+
*/
|
|
218
|
+
it("suppresses a swarm series whose cluster is under maintenance", () => {
|
|
219
|
+
const maintained: MaintainedResourceKeys = emptyMaintained();
|
|
220
|
+
maintained.dockerSwarmClusters.names.add("prod-swarm");
|
|
221
|
+
|
|
222
|
+
const result: Set<string> =
|
|
223
|
+
MonitorMaintenanceSuppression.getSuppressedFingerprintsForMaintainedResources(
|
|
224
|
+
{
|
|
225
|
+
matchesPerSeries: [
|
|
226
|
+
series("fpSwarm", {
|
|
227
|
+
"resource.docker.swarm.cluster.name": "prod-swarm",
|
|
228
|
+
}),
|
|
229
|
+
series("fpClear", {
|
|
230
|
+
"resource.docker.swarm.cluster.name": "staging-swarm",
|
|
231
|
+
}),
|
|
232
|
+
],
|
|
233
|
+
maintained,
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
expect(Array.from(result)).toEqual(["fpSwarm"]);
|
|
238
|
+
});
|
|
239
|
+
|
|
210
240
|
it("does not cross-match resource types that happen to share a name", () => {
|
|
211
241
|
/*
|
|
212
242
|
* A service named the same string as the breaching host must not
|