@oneuptime/common 13.0.7 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/AnalyticsModels/AuditLog.ts +23 -15
- package/Models/DatabaseModels/GlobalConfig.ts +24 -0
- package/Models/DatabaseModels/Monitor.ts +3 -2
- package/Models/DatabaseModels/MonitorTemplate.ts +2 -1
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.ts +1 -1
- package/Models/DatabaseModels/ProjectOnCallCalendarFeed.ts +1 -1
- package/Models/DatabaseModels/UserOnCallCalendarFeed.ts +1 -1
- package/Scripts/generate-service-worker.js +65 -4
- package/Server/API/AIBillingAPI.ts +1 -0
- package/Server/API/AIInsightAPI.ts +1 -4
- package/Server/API/AIInvestigationAPI.ts +1 -4
- package/Server/API/AIReadinessAPI.ts +4 -5
- package/Server/API/AlertAPI.ts +1 -0
- package/Server/API/AutoRemediationAPI.ts +1 -4
- package/Server/API/BaseAPI.ts +34 -0
- package/Server/API/BaseAnalyticsAPI.ts +80 -9
- package/Server/API/BillingAPI.ts +2 -0
- package/Server/API/BillingInvoiceAPI.ts +1 -0
- package/Server/API/BillingPaymentMethodAPI.ts +2 -0
- package/Server/API/CodeFixRunAPI.ts +1 -3
- package/Server/API/CommonAPI.ts +68 -4
- package/Server/API/DashboardAPI.ts +12 -12
- package/Server/API/GlobalConfigAPI.ts +243 -460
- package/Server/API/IncidentAPI.ts +2 -0
- package/Server/API/IncidentEpisodeAPI.ts +1 -0
- package/Server/API/NotificationAPI.ts +1 -0
- package/Server/API/OnCallCalendarAPI.ts +4 -5
- package/Server/API/OnCallDutyPolicyAPI.ts +7 -0
- package/Server/API/OnCallReadinessAPI.ts +4 -1
- package/Server/API/OnCallScheduleTimelineAPI.ts +731 -0
- package/Server/API/ProjectAPI.ts +1 -0
- package/Server/API/ProjectOIDC.ts +25 -16
- package/Server/API/ProjectSSO.ts +24 -16
- package/Server/API/ScheduledMaintenanceAPI.ts +1 -0
- package/Server/API/StatusPageAPI.ts +261 -74
- package/Server/API/TeamMemberAPI.ts +13 -1
- package/Server/API/UserCallAPI.ts +2 -0
- package/Server/API/UserEmailAPI.ts +2 -0
- package/Server/API/UserIncomingCallNumberAPI.ts +2 -0
- package/Server/API/UserMicrosoftTeamsAPI.ts +1 -0
- package/Server/API/UserSlackAPI.ts +1 -0
- package/Server/API/UserSmsAPI.ts +2 -0
- package/Server/API/UserTotpAuthAPI.ts +1 -0
- package/Server/API/UserWebAuthnAPI.ts +2 -0
- package/Server/API/UserWebhookAPI.ts +1 -0
- package/Server/API/UserWhatsAppAPI.ts +2 -0
- package/Server/Enterprise/EnterpriseEdition.ts +649 -0
- package/Server/Enterprise/EnterpriseFeature.ts +48 -0
- package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +192 -0
- package/Server/Enterprise/EnterpriseServerModule.ts +233 -0
- package/Server/EnvironmentConfig.ts +147 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +13 -2
- package/Server/Middleware/UserAuthorization.ts +117 -21
- package/Server/Services/ApiKeyPermissionService.ts +101 -4
- package/Server/Services/AuditLogService.ts +84 -1079
- package/Server/Services/DatabaseService.ts +245 -0
- package/Server/Services/DetectionRuleService.ts +5 -4
- package/Server/Services/EnterpriseLicenseService.ts +38 -0
- package/Server/Services/GlobalConfigService.ts +80 -1
- package/Server/Services/MonitorProbeService.ts +116 -20
- package/Server/Services/MonitorService.ts +56 -4
- package/Server/Services/MonitorStatusTimelineService.ts +284 -0
- package/Server/Services/MonitorTemplateService.ts +26 -0
- package/Server/Services/OnCallDutyPolicyChildService.ts +261 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +2 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -2
- package/Server/Services/PayAsYouGoBillingService.ts +23 -28
- package/Server/Services/ProjectService.ts +302 -0
- package/Server/Services/RumSessionPinService.ts +293 -22
- package/Server/Services/StatusPagePrivateUserService.ts +7 -3
- package/Server/Services/StatusPageService.ts +78 -1
- package/Server/Services/TeamMemberService.ts +11 -0
- package/Server/Services/TeamPermissionService.ts +120 -26
- package/Server/Services/TeamService.ts +10 -0
- package/Server/Services/ThreatIntelFeedService.ts +6 -3
- package/Server/Services/UserService.ts +10 -17
- package/Server/Types/Database/Permissions/BasePermission.ts +9 -7
- package/Server/Types/Database/Permissions/CreatePermission.ts +15 -0
- package/Server/Types/Database/Permissions/EditionPermission.ts +208 -18
- package/Server/Types/Database/Permissions/Index.ts +75 -11
- package/Server/Types/Database/Permissions/TablePermission.ts +34 -31
- package/Server/Types/Database/Permissions/UpdatePermission.ts +29 -0
- package/Server/Types/Markdown.ts +41 -0
- package/Server/Utils/AI/AIChatPrivacyFilter.ts +8 -0
- package/Server/Utils/AI/AIRunPrivacyFilter.ts +8 -0
- package/Server/Utils/CommunityEditionSsoReport.ts +361 -0
- package/Server/Utils/EditionEnforcement.ts +233 -0
- package/Server/Utils/FrontendEnvironment.ts +41 -2
- package/Server/Utils/InstanceHealth/RedisHealth.ts +12 -277
- package/Server/Utils/MasterAdminPageAccess.ts +61 -0
- package/Server/Utils/Monitor/AffectedResourceList.ts +190 -0
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +10 -1
- package/Server/Utils/Monitor/MonitorAlert.ts +3 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +543 -333
- package/Server/Utils/Monitor/MonitorIncident.ts +3 -1
- package/Server/Utils/Monitor/MonitoringIntervalValidator.ts +116 -0
- package/Server/Utils/OnCall/OnCallCalendarFeedRenderer.ts +1 -1
- package/Server/Utils/Realtime.ts +163 -45
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +11 -28
- package/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.ts +170 -0
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +1 -1
- package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +6 -2
- package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.ts +10 -9
- package/Server/Utils/Workspace/Slack/Slack.ts +423 -10
- package/Tests/App/Accounts/LoginPasskey.test.tsx +18 -0
- package/Tests/App/Accounts/LoginSsoEdition.test.tsx +616 -0
- package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +217 -0
- package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +357 -0
- package/Tests/App/AdminDashboard/EnterpriseLicensesShell.test.tsx +214 -0
- package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +271 -0
- package/Tests/App/AdminDashboard/HealthShells.test.tsx +659 -0
- package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +1059 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerEditForm.test.tsx +376 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +757 -0
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +307 -0
- package/Tests/App/Dashboard/ArgumentsFormGroupByUnlistedAttributes.test.tsx +131 -0
- package/Tests/App/Dashboard/AuditLogsAndComplianceShells.test.tsx +572 -0
- package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +1 -1
- package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +9 -1
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +24 -0
- package/Tests/App/Dashboard/DashboardVariableSelector.test.tsx +162 -8
- package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +547 -0
- package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +667 -0
- package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +105 -6
- package/Tests/App/Dashboard/EntityFilterDropdownUnlistedSelection.test.tsx +434 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +276 -1
- package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +319 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedSideMenus.test.tsx +9 -1
- package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +185 -0
- package/Tests/App/Dashboard/PayAsYouGoConsentGate.test.tsx +58 -42
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +42 -5
- package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +34 -23
- package/Tests/App/Dashboard/ScheduleTimeline.test.tsx +1580 -0
- package/Tests/App/Dashboard/ScheduleTimelineAPI.test.ts +280 -0
- package/Tests/App/Dashboard/ScheduleTimelineModel.test.ts +673 -0
- package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +133 -70
- package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +399 -0
- package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +214 -0
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +86 -3
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +1 -58
- package/Tests/App/Dashboard/SecurityEventsGuideContent.test.ts +1313 -0
- package/Tests/App/Dashboard/SecurityEventsHowItWorksCard.test.tsx +536 -0
- package/Tests/App/Dashboard/SecurityEventsNavTabs.test.tsx +248 -0
- package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +222 -0
- package/Tests/App/Dashboard/SecurityEventsThreatIntelPage.test.tsx +199 -0
- package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +1203 -0
- package/Tests/App/Dashboard/SloOverviewHero.test.tsx +171 -36
- package/Tests/App/Dashboard/SloViewSideMenu.test.tsx +215 -0
- package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +40 -3
- package/Tests/App/Dashboard/UserEmailCallSites.test.tsx +11 -91
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +12 -4
- package/Tests/App/PublicDashboard/PublicDashboardWidgetListAuth.test.tsx +623 -0
- package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +319 -0
- package/Tests/Helpers/RefreshAwareApiScan.ts +757 -0
- package/Tests/Helpers/TenantScopedRequestScan.ts +515 -0
- package/Tests/Models/OnCallCalendarFeedModels.test.ts +23 -1
- package/Tests/Scripts/GenerateServiceWorker.test.ts +931 -0
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +138 -5
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +187 -0
- package/Tests/Server/API/AIInvestigationAvailabilityAuthorization.test.ts +51 -3
- package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +55 -2
- package/Tests/Server/API/BaseAPICreateItemDoesNotMutateModel.test.ts +130 -0
- package/Tests/Server/API/BaseAnalyticsAPIAggregateCache.test.ts +729 -0
- package/Tests/Server/API/BillingPayAsYouGoStatus.test.ts +93 -7
- package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +36 -3
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +1003 -24
- package/Tests/Server/API/DashboardPublicRateLimit.test.ts +6 -3
- package/Tests/Server/API/ExpiredSessionAIRoutes.test.ts +708 -0
- package/Tests/Server/API/ExpiredSessionCustomRoutes.test.ts +928 -0
- package/Tests/Server/API/ExpiredSessionMonitorTemplate.test.ts +319 -0
- package/Tests/Server/API/ExpiredSessionOnCallDutyPolicy.test.ts +336 -0
- package/Tests/Server/API/ExpiredSessionRouteGuards.test.ts +820 -0
- package/Tests/Server/API/GlobalConfigLicense.test.ts +766 -381
- package/Tests/Server/API/NotificationChannelVerificationAPI.test.ts +115 -12
- package/Tests/Server/API/OnCallCalendarAPI.test.ts +375 -8
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +196 -912
- package/Tests/Server/API/OnCallScheduleTimelineAPI.test.ts +1513 -0
- package/Tests/Server/API/OnCallSetupReminderAPI.test.ts +119 -7
- package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +111 -4
- package/Tests/Server/API/PublicRouteInvalidAccessToken.test.ts +615 -0
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +89 -2
- package/Tests/Server/API/SessionReplayAPI.test.ts +121 -0
- package/Tests/Server/API/SsoProviderListingsEdition.test.ts +432 -0
- package/Tests/Server/API/StatusPageMasterPasswordRateLimit.test.ts +4 -2
- package/Tests/Server/API/StatusPagePostmortemVisibility.test.ts +441 -0
- package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +664 -0
- package/Tests/Server/API/TeamMemberLeaveScimLockEdition.test.ts +252 -0
- package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +7 -0
- package/Tests/Server/API/UserIncomingCallNumberApi.test.ts +6 -0
- package/Tests/Server/API/UserNotificationSettingAPI.test.ts +65 -4
- package/Tests/Server/API/UserProjectsAPI.test.ts +63 -3
- package/Tests/Server/API/UserTotpAuthAPI.test.ts +48 -2
- package/Tests/Server/API/UserWebAuthnAuthenticationOptionsRoute.test.ts +11 -2
- package/Tests/Server/API/UserWebAuthnBackupCodeMinting.test.ts +46 -3
- package/Tests/Server/API/UserWebAuthnPasskeyAPI.test.ts +18 -2
- package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +24 -7
- package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +824 -0
- package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +643 -0
- package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +294 -0
- package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +93 -0
- package/Tests/Server/Enterprise/EnterpriseOnlyModulesLiveInEe.test.ts +582 -0
- package/Tests/Server/Enterprise/EnterpriseServerModule.test.ts +247 -0
- package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +502 -0
- package/Tests/Server/Enterprise/TestBillingFlag.ts +70 -0
- package/Tests/Server/EnvironmentConfigEnterpriseEdition.test.ts +314 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +289 -0
- package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +5 -1
- package/Tests/Server/Middleware/ExpiredSessionUserMiddleware.test.ts +563 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +299 -19
- package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +453 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +37 -2
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +516 -0
- package/Tests/Server/Services/AIChatPrivacyPin.test.ts +24 -0
- package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +2 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +3 -3
- package/Tests/Server/Services/AuditLogService.test.ts +460 -990
- package/Tests/Server/Services/CreateAndReadResponsesKeepIds.test.ts +263 -0
- package/Tests/Server/Services/DatabaseServiceCreateSuppliedId.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServiceLoginBeforeHooks.test.ts +1078 -0
- package/Tests/Server/Services/DatabaseServiceTenantRelationGuard.test.ts +259 -0
- package/Tests/Server/Services/DatabaseServiceWriteOperatorValues.test.ts +147 -0
- package/Tests/Server/Services/EnterpriseLicenseServiceKeyGeneration.test.ts +151 -0
- package/Tests/Server/Services/GlobalConfigServiceLicenseColumns.test.ts +357 -0
- package/Tests/Server/Services/MonitorDestinationInfoIPv6.test.ts +101 -0
- package/Tests/Server/Services/MonitorPayAsYouGoBilling.test.ts +115 -1
- package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +333 -0
- package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +303 -0
- package/Tests/Server/Services/OnCallChildCreateAuthorizationPostgres.test.ts +711 -0
- package/Tests/Server/Services/OnCallDutyPolicyChildService.test.ts +733 -0
- package/Tests/Server/Services/PayAsYouGoBillingService.test.ts +190 -140
- package/Tests/Server/Services/PayAsYouGoStripeRateLimitBudget.test.ts +4 -9
- package/Tests/Server/Services/PermissionBlockDeletion.test.ts +780 -0
- package/Tests/Server/Services/PermissionBlockDeletionPostgres.test.ts +628 -0
- package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +701 -0
- package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +31 -35
- package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +77 -1
- package/Tests/Server/Services/RumSessionPinCreatePermission.test.ts +715 -0
- package/Tests/Server/Services/ScimPushGroupsTeamLocksEdition.test.ts +431 -0
- package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +419 -0
- package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +769 -0
- package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +94 -0
- package/Tests/Server/Services/TeamMemberMultiTenantReadPostgres.test.ts +394 -0
- package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +6 -0
- package/Tests/Server/Services/TelemetryPayAsYouGoBilling.test.ts +131 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +35 -0
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +13 -2
- package/Tests/Server/Services/UserServiceSeatLimit.test.ts +94 -56
- package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +1823 -0
- package/Tests/Server/Types/Database/Permissions/ModelPermissionAnonymousRefusal.test.ts +518 -0
- package/Tests/Server/Types/Database/Permissions/MultiTenantPermissions.test.ts +350 -0
- package/Tests/Server/Types/Database/Permissions/OnCallCalendarFeedPermissions.test.ts +282 -25
- package/Tests/Server/Types/Database/Permissions/TableLevelBlockPermission.test.ts +459 -0
- package/Tests/Server/Types/Markdown.test.ts +181 -0
- package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +101 -9
- package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +88 -4
- package/Tests/Server/Utils/AI/Toolbox/ExceptionTools.test.ts +459 -0
- package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +794 -0
- package/Tests/Server/Utils/AI/Toolbox/RecentChangesTools.test.ts +574 -0
- package/Tests/Server/Utils/AI/Toolbox/SecurityEventTools.test.ts +596 -0
- package/Tests/Server/Utils/CaptchaRequestAndLogging.test.ts +395 -0
- package/Tests/Server/Utils/CommunityEditionSsoReport.test.ts +565 -0
- package/Tests/Server/Utils/EditionEnforcement.test.ts +303 -0
- package/Tests/Server/Utils/FrontendEnvironment.test.ts +367 -0
- package/Tests/Server/Utils/InstanceHealth/RedisHealth.test.ts +38 -418
- package/Tests/Server/Utils/MasterAdminPageAccess.test.ts +251 -0
- package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +518 -0
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +24 -4
- package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +616 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +934 -96
- package/Tests/Server/Utils/Monitor/MonitoringIntervalValidator.test.ts +188 -0
- package/Tests/Server/Utils/Realtime.test.ts +502 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorSeverity.test.ts +216 -0
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +142 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.test.ts +140 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepairClickhouseIntegration.test.ts +542 -0
- package/Tests/Server/Utils/Telemetry/ChangeEventRow.test.ts +743 -0
- package/Tests/Server/Utils/Telemetry/RuntimeMetrics.test.ts +465 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.test.ts +202 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.test.ts +231 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/Alert.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/AlertEpisode.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/Incident.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/IncidentEpisode.test.ts +194 -0
- package/Tests/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.test.ts +247 -0
- package/Tests/Server/Utils/Workspace/Slack/SlackMarkdownSectionSplit.test.ts +1209 -0
- package/Tests/Types/AI/AIInsightEvidence.test.ts +206 -0
- package/Tests/Types/API/HostnameFromAuthority.test.ts +20 -10
- package/Tests/Types/API/HostnameIPv6.test.ts +149 -0
- package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtilAnonymous.test.ts +144 -0
- package/Tests/Types/Call/CallProvider.test.ts +528 -0
- package/Tests/Types/Database/CanonicalModelInstance.test.ts +190 -0
- package/Tests/Types/Kubernetes/KubernetesCostIngest.test.ts +236 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleTimeline.test.ts +736 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleTimelineLayout.test.ts +996 -0
- package/Tests/Types/SecurityEvent/SigmaRule.test.ts +142 -0
- package/Tests/Types/Workflow/Components/JSON.test.ts +182 -0
- package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +116 -1
- package/Tests/UI/Components/Dropdown/DropdownAsyncOptions.test.tsx +81 -0
- package/Tests/UI/Components/EditionLabelLapseCopy.ts +104 -0
- package/Tests/UI/Components/EditionLabelLicenseManagerWiring.test.tsx +254 -0
- package/Tests/UI/Components/EditionLabelLicenseRefresh.test.tsx +165 -171
- package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +1586 -0
- package/Tests/UI/Components/EditionLabelLicenseWritesGuard.test.tsx +157 -0
- package/Tests/UI/Components/EventAttachmentList.test.tsx +174 -0
- package/Tests/UI/Components/Forms/BasicFormBurnRateTemplateDefaults.test.tsx +395 -0
- package/Tests/UI/Components/Forms/BasicFormCollapsibleSections.test.tsx +286 -0
- package/Tests/UI/Components/Forms/FormSummary.test.tsx +128 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +49 -2
- package/Tests/UI/Components/MarkdownConverters.test.ts +684 -0
- package/Tests/UI/Components/ModelListEmptyState.test.tsx +289 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableQueryChange.test.tsx +365 -0
- package/Tests/UI/Components/OpenAuthenticatedUrl.test.ts +358 -0
- package/Tests/UI/Components/SessionAwareImage.test.tsx +496 -0
- package/Tests/UI/Components/TelemetryHistogramZeroBuckets.test.tsx +190 -0
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerEmptyContent.test.tsx +131 -0
- package/Tests/UI/Components/TooltipInteractive.test.tsx +97 -0
- package/Tests/UI/EsbuildEnterpriseAlias.test.ts +1127 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardInstallGuidance.test.tsx +1 -1
- package/Tests/UI/Monitor/EvaluationLogListTimestamps.test.tsx +367 -0
- package/Tests/UI/Utils/API/BaseAPIRefreshSession.test.ts +1087 -0
- package/Tests/UI/Utils/API/DashboardHost.ts +12 -0
- package/Tests/UI/Utils/API/FakeAxiosServer.ts +183 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +10 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/UI/Utils/Cookie.test.ts +440 -0
- package/Tests/UI/Utils/ModelAPIRequestClient.test.ts +278 -0
- package/Tests/UI/Utils/Navigation.test.ts +33 -0
- package/Tests/UI/Utils/PricingPlan.test.ts +295 -0
- package/Tests/UI/Utils/Realtime.test.ts +759 -0
- package/Tests/UI/Utils/Telemetry/Telemetry.test.ts +500 -0
- package/Tests/Utils/APIInstanceDispatch.test.ts +311 -0
- package/Tests/Utils/HostAddressUtil.test.ts +138 -0
- package/Tests/Utils/Monitor/MonitorDestinationUtil.test.ts +313 -0
- package/Tests/Utils/Monitor/MonitoringIntervalUtil.test.ts +304 -0
- package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizerSeverity.test.ts +565 -0
- package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +248 -0
- package/Types/API/Hostname.ts +63 -16
- package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +40 -0
- package/Types/Database/AccessControl/ColumnAccessControl.ts +19 -7
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +19 -7
- package/Types/Database/AccessControl/OwnerOnlyColumn.ts +15 -6
- package/Types/Database/CanonicalModelInstance.ts +102 -0
- package/Types/Database/TableColumn.ts +20 -6
- package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +29 -0
- package/Types/IP/IPv6.ts +6 -1
- package/Types/Monitor/MonitorEvaluationSummary.ts +3 -0
- package/Types/Monitor/MonitorStep.ts +6 -1
- package/Types/Monitor/ProxmoxAlertTemplates.ts +1 -1
- package/Types/OnCallDutyPolicy/ScheduleTimeline.ts +507 -0
- package/Types/OnCallDutyPolicy/ScheduleTimelineLayout.ts +589 -0
- package/Types/Realtime/EventName.ts +11 -0
- package/Types/SecurityEvent/DetectionFindingConstants.ts +14 -0
- package/Types/SecurityEvent/OcsfSeverity.ts +31 -26
- package/Types/SecurityEvent/SigmaRule.ts +51 -0
- package/Types/SecurityEvent/ThreatIntelConstants.ts +18 -0
- package/Types/StatusPage/UptimeDailyAggregate.ts +110 -0
- package/UI/Components/AttachmentList/EventAttachmentList.tsx +30 -2
- package/UI/Components/Dashboard/DashboardVariableControl.tsx +12 -2
- package/UI/Components/Dropdown/Dropdown.tsx +6 -1
- package/UI/Components/EditionLabel/EditionLabel.tsx +808 -1195
- package/UI/Components/EditionLabel/LicenseManager.ts +119 -0
- package/UI/Components/EventItem/EventItem.tsx +12 -1
- package/UI/Components/Forms/BasicForm.tsx +151 -126
- package/UI/Components/Forms/CollapsibleFormSection.tsx +63 -0
- package/UI/Components/Forms/Fields/FormField.tsx +10 -4
- package/UI/Components/Forms/FormSummary.tsx +7 -0
- package/UI/Components/Forms/Types/Field.ts +10 -0
- package/UI/Components/Graphs/DayUptimeGraph.tsx +123 -15
- package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +2 -1
- package/UI/Components/Markdown.tsx/SessionAwareImage.tsx +122 -0
- package/UI/Components/ModelList/ModelList.tsx +11 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +34 -5
- package/UI/Components/ModelTable/TableView.tsx +1 -1
- package/UI/Components/MonitorGraphs/Uptime.tsx +69 -0
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +28 -12
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +8 -1
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Utils/API/API.ts +173 -12
- package/UI/Utils/API/RequestOptions.ts +11 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +31 -7
- package/UI/Utils/ModelAPI/ModelAPI.ts +30 -9
- package/UI/Utils/Navigation.ts +9 -0
- package/UI/Utils/OpenAuthenticatedUrl.ts +154 -0
- package/UI/Utils/Realtime.ts +176 -30
- package/UI/esbuild-enterprise.js +263 -0
- package/Utils/API.ts +14 -5
- package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +7 -2
- package/Utils/HostAddressUtil.ts +94 -0
- package/Utils/Monitor/MonitorDestinationUtil.ts +157 -0
- package/Utils/Monitor/MonitoringIntervalUtil.ts +436 -0
- package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +157 -7
- package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +9 -17
- package/Utils/Slo/SloBurnRateTemplate.ts +2 -2
- package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +192 -0
- package/build/dist/Models/AnalyticsModels/AuditLog.js +23 -15
- package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +25 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +2 -2
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +2 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleCalendarFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js +1 -1
- package/build/dist/Models/DatabaseModels/ProjectOnCallCalendarFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallCalendarFeed.js.map +1 -1
- package/build/dist/Server/API/AIBillingAPI.js +1 -1
- package/build/dist/Server/API/AIBillingAPI.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +1 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +1 -4
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AIReadinessAPI.js +1 -3
- package/build/dist/Server/API/AIReadinessAPI.js.map +1 -1
- package/build/dist/Server/API/AlertAPI.js +1 -1
- package/build/dist/Server/API/AlertAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +1 -4
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +33 -0
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js +55 -10
- package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
- package/build/dist/Server/API/BillingAPI.js +2 -2
- package/build/dist/Server/API/BillingAPI.js.map +1 -1
- package/build/dist/Server/API/BillingInvoiceAPI.js +1 -1
- package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
- package/build/dist/Server/API/BillingPaymentMethodAPI.js +2 -2
- package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
- package/build/dist/Server/API/CodeFixRunAPI.js +1 -3
- package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +50 -5
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +12 -12
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +158 -342
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +2 -2
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
- package/build/dist/Server/API/NotificationAPI.js +1 -1
- package/build/dist/Server/API/NotificationAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallCalendarAPI.js +1 -4
- package/build/dist/Server/API/OnCallCalendarAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallDutyPolicyAPI.js +6 -0
- package/build/dist/Server/API/OnCallDutyPolicyAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js +4 -1
- package/build/dist/Server/API/OnCallReadinessAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +494 -0
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -0
- package/build/dist/Server/API/ProjectAPI.js +1 -1
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectOIDC.js +24 -16
- package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
- package/build/dist/Server/API/ProjectSSO.js +24 -16
- package/build/dist/Server/API/ProjectSSO.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +218 -76
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +11 -4
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/API/UserCallAPI.js +2 -2
- package/build/dist/Server/API/UserCallAPI.js.map +1 -1
- package/build/dist/Server/API/UserEmailAPI.js +2 -2
- package/build/dist/Server/API/UserEmailAPI.js.map +1 -1
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js +2 -2
- package/build/dist/Server/API/UserIncomingCallNumberAPI.js.map +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/UserSlackAPI.js +1 -1
- package/build/dist/Server/API/UserSlackAPI.js.map +1 -1
- package/build/dist/Server/API/UserSmsAPI.js +2 -2
- package/build/dist/Server/API/UserSmsAPI.js.map +1 -1
- package/build/dist/Server/API/UserTotpAuthAPI.js +1 -1
- package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -1
- package/build/dist/Server/API/UserWebAuthnAPI.js +2 -2
- package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -1
- package/build/dist/Server/API/UserWebhookAPI.js +1 -1
- package/build/dist/Server/API/UserWebhookAPI.js.map +1 -1
- package/build/dist/Server/API/UserWhatsAppAPI.js +2 -2
- package/build/dist/Server/API/UserWhatsAppAPI.js.map +1 -1
- package/build/dist/Server/Enterprise/EnterpriseEdition.js +466 -0
- package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js +39 -0
- package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +70 -0
- package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js.map +1 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js +84 -0
- package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +84 -2
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793900000000-AddEnterpriseEditionFirstSeenAt.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/Middleware/MasterAdminAuthorization.js +11 -2
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +94 -15
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +80 -4
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/AuditLogService.js +71 -740
- package/build/dist/Server/Services/AuditLogService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +171 -0
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +4 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +34 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/GlobalConfigService.js +63 -0
- package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
- package/build/dist/Server/Services/MonitorProbeService.js +90 -17
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +45 -4
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +242 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +16 -0
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyChildService.js +170 -0
- package/build/dist/Server/Services/OnCallDutyPolicyChildService.js.map +1 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -2
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
- package/build/dist/Server/Services/PayAsYouGoBillingService.js +20 -27
- package/build/dist/Server/Services/PayAsYouGoBillingService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +231 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +198 -16
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +7 -3
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +71 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +10 -0
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TeamPermissionService.js +89 -27
- package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
- package/build/dist/Server/Services/TeamService.js +9 -0
- package/build/dist/Server/Services/TeamService.js.map +1 -1
- package/build/dist/Server/Services/ThreatIntelFeedService.js +5 -4
- package/build/dist/Server/Services/ThreatIntelFeedService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +10 -16
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +9 -6
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +10 -0
- package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +154 -14
- package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/Index.js +60 -6
- package/build/dist/Server/Types/Database/Permissions/Index.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js +24 -16
- package/build/dist/Server/Types/Database/Permissions/TablePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +17 -0
- package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +31 -0
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +7 -0
- package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -1
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +7 -0
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -1
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js +223 -0
- package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +1 -0
- package/build/dist/Server/Utils/EditionEnforcement.js +187 -0
- package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -0
- package/build/dist/Server/Utils/FrontendEnvironment.js +31 -2
- package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -1
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js +6 -193
- package/build/dist/Server/Utils/InstanceHealth/RedisHealth.js.map +1 -1
- package/build/dist/Server/Utils/MasterAdminPageAccess.js +39 -0
- package/build/dist/Server/Utils/MasterAdminPageAccess.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js +96 -0
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +3 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +449 -261
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +3 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js +88 -0
- package/build/dist/Server/Utils/Monitor/MonitoringIntervalValidator.js.map +1 -0
- package/build/dist/Server/Utils/Realtime.js +126 -37
- package/build/dist/Server/Utils/Realtime.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +7 -25
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js +127 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOpsSeverityRepair.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +3 -2
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js +4 -8
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelMatcher.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +331 -11
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +59 -11
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +31 -1
- package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +19 -7
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js +13 -6
- package/build/dist/Types/Database/AccessControl/OwnerOnlyColumn.js.map +1 -1
- package/build/dist/Types/Database/CanonicalModelInstance.js +88 -0
- package/build/dist/Types/Database/CanonicalModelInstance.js.map +1 -0
- package/build/dist/Types/Database/TableColumn.js +19 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +28 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js.map +1 -0
- package/build/dist/Types/IP/IPv6.js +6 -1
- package/build/dist/Types/IP/IPv6.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +5 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js +321 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimeline.js.map +1 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js +372 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleTimelineLayout.js.map +1 -0
- package/build/dist/Types/Realtime/EventName.js +10 -0
- package/build/dist/Types/Realtime/EventName.js.map +1 -1
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +11 -0
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
- package/build/dist/Types/SecurityEvent/OcsfSeverity.js +30 -25
- package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -1
- package/build/dist/Types/SecurityEvent/SigmaRule.js +47 -0
- package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -1
- package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js +14 -0
- package/build/dist/Types/SecurityEvent/ThreatIntelConstants.js.map +1 -1
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js +32 -0
- package/build/dist/Types/StatusPage/UptimeDailyAggregate.js.map +1 -0
- package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +9 -4
- package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -1
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +10 -2
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +5 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +425 -585
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js +2 -0
- package/build/dist/UI/Components/EditionLabel/LicenseManager.js.map +1 -0
- package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
- package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +70 -64
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/CollapsibleFormSection.js +27 -0
- package/build/dist/UI/Components/Forms/CollapsibleFormSection.js.map +1 -0
- package/build/dist/UI/Components/Forms/Fields/FormField.js +10 -7
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/FormSummary.js +7 -0
- package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +90 -17
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +2 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js +84 -0
- package/build/dist/UI/Components/Markdown.tsx/SessionAwareImage.js.map +1 -0
- package/build/dist/UI/Components/ModelList/ModelList.js +4 -1
- package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +14 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
- package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js +42 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +2 -2
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +2 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/UI/Utils/API/API.js +143 -19
- package/build/dist/UI/Utils/API/API.js.map +1 -1
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +21 -7
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +26 -9
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +8 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/UI/Utils/OpenAuthenticatedUrl.js +92 -0
- package/build/dist/UI/Utils/OpenAuthenticatedUrl.js.map +1 -0
- package/build/dist/UI/Utils/Realtime.js +120 -15
- package/build/dist/UI/Utils/Realtime.js.map +1 -1
- package/build/dist/Utils/API.js +13 -5
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +6 -2
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
- package/build/dist/Utils/HostAddressUtil.js +80 -0
- package/build/dist/Utils/HostAddressUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js +127 -0
- package/build/dist/Utils/Monitor/MonitorDestinationUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js +374 -0
- package/build/dist/Utils/Monitor/MonitoringIntervalUtil.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +123 -6
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
- package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +4 -18
- package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -1
- package/build/dist/Utils/Slo/SloBurnRateTemplate.js +2 -2
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -0
- package/jest.config.json +3 -1
- package/package.json +1 -1
- package/test-setup.sh +103 -34
- package/tsconfig.json +13 -0
- package/Server/API/EnterpriseLicenseAPI.ts +0 -925
- package/Server/API/TeamComplianceAPI.ts +0 -158
- package/Server/Middleware/SCIMAuthorization.ts +0 -141
- package/Server/Services/TeamComplianceService.ts +0 -733
- package/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.ts +0 -186
- package/Server/Utils/InstanceHealth/PostgresHealth.ts +0 -286
- package/Tests/App/AdminDashboard/EnterpriseLicenseInstanceActivity.test.tsx +0 -451
- package/Tests/App/Dashboard/AuditLogsTable.test.tsx +0 -598
- package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +0 -247
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +0 -382
- package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +0 -1851
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +0 -525
- package/Tests/Server/Services/TeamComplianceServiceBehaviour.test.ts +0 -1845
- package/Tests/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.test.ts +0 -495
- package/Tests/Server/Utils/InstanceHealth/PostgresHealth.test.ts +0 -337
- package/Tests/Utils/EnterpriseLicenseSeats.test.ts +0 -576
- package/Tests/Utils/EnterpriseLicenseSync.test.ts +0 -568
- package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +0 -287
- package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +0 -244
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +0 -646
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +0 -1
- package/build/dist/Server/API/TeamComplianceAPI.js +0 -111
- package/build/dist/Server/API/TeamComplianceAPI.js.map +0 -1
- package/build/dist/Server/Middleware/SCIMAuthorization.js +0 -124
- package/build/dist/Server/Middleware/SCIMAuthorization.js.map +0 -1
- package/build/dist/Server/Services/TeamComplianceService.js +0 -537
- package/build/dist/Server/Services/TeamComplianceService.js.map +0 -1
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js +0 -145
- package/build/dist/Server/Utils/EnterpriseLicense/EnterpriseLicenseSeatUtil.js.map +0 -1
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js +0 -194
- package/build/dist/Server/Utils/InstanceHealth/PostgresHealth.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +0 -168
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +0 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +0 -174
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +0 -1
|
@@ -6,7 +6,11 @@ import MetricMonitorResponse, {
|
|
|
6
6
|
VMwareAffectedResource,
|
|
7
7
|
CephAffectedResource,
|
|
8
8
|
KubernetesAffectedResource,
|
|
9
|
+
DockerSwarmAffectedResource,
|
|
9
10
|
} from "../../../../Types/Monitor/MetricMonitor/MetricMonitorResponse";
|
|
11
|
+
import Markdown, {
|
|
12
|
+
MarkdownContentType,
|
|
13
|
+
} from "../../../../Server/Types/Markdown";
|
|
10
14
|
import {
|
|
11
15
|
getProxmoxAlertTemplateById,
|
|
12
16
|
ProxmoxAlertTemplate,
|
|
@@ -36,12 +40,14 @@ import FilterCondition from "../../../../Types/Filter/FilterCondition";
|
|
|
36
40
|
* the incident root-cause context. These tests drive the evaluator's
|
|
37
41
|
* Proxmox/Ceph branches directly and lock in the render contract:
|
|
38
42
|
*
|
|
39
|
-
* -
|
|
40
|
-
*
|
|
43
|
+
* - the breakdown is a ranked list, not a table: one numbered item per
|
|
44
|
+
* resource titled "**Kind** `name` — **value**", with the rest of
|
|
45
|
+
* its identity (a Proxmox guest's node, a Ceph daemon's pool and
|
|
46
|
+
* host, a vSphere object's host and cluster) as nested bullets,
|
|
41
47
|
* - zero-value rows are dropped (supplementary context — the
|
|
42
48
|
* per-series criteria still alert on them), worst value first,
|
|
43
49
|
* top 10 with an "... and N more" suffix,
|
|
44
|
-
* - identity-less (cluster-wide) breakdowns render NO
|
|
50
|
+
* - identity-less (cluster-wide) breakdowns render NO list and fall
|
|
45
51
|
* back to the metric summary,
|
|
46
52
|
* - the cluster context lines surface the monitor step's
|
|
47
53
|
* clusterIdentifier and resource filters (the pve.scope / pve.id /
|
|
@@ -68,6 +74,16 @@ type EvaluatorPrivate = {
|
|
|
68
74
|
monitor: Monitor;
|
|
69
75
|
criteriaInstance?: MonitorCriteriaInstance | undefined;
|
|
70
76
|
}) => string | null;
|
|
77
|
+
buildKubernetesRootCauseContext: (input: {
|
|
78
|
+
dataToProcess: unknown;
|
|
79
|
+
monitorStep: MonitorStep;
|
|
80
|
+
monitor: Monitor;
|
|
81
|
+
}) => Promise<string | null>;
|
|
82
|
+
buildDockerSwarmRootCauseContext: (input: {
|
|
83
|
+
dataToProcess: unknown;
|
|
84
|
+
monitorStep: MonitorStep;
|
|
85
|
+
monitor: Monitor;
|
|
86
|
+
}) => string | null;
|
|
71
87
|
buildKubernetesRootCauseAnalysis: (input: {
|
|
72
88
|
breakdown: {
|
|
73
89
|
clusterName: string;
|
|
@@ -142,12 +158,12 @@ function metricResponse(
|
|
|
142
158
|
}
|
|
143
159
|
|
|
144
160
|
describe("MonitorCriteriaEvaluator - Proxmox root cause breakdown", () => {
|
|
145
|
-
test("renders
|
|
161
|
+
test("renders a ranked list of kind, name and node: zero rows dropped, worst first", () => {
|
|
146
162
|
const affectedResources: Array<ProxmoxAffectedResource> = [
|
|
147
163
|
/*
|
|
148
164
|
* pve_cpu_usage_ratio is a [0, 1] ratio — the catalog says so and
|
|
149
165
|
* pve-exporter reports it that way — so these are 42% and 97% of a
|
|
150
|
-
* node's CPU, and the
|
|
166
|
+
* node's CPU, and the list is expected to say exactly that.
|
|
151
167
|
*/
|
|
152
168
|
{
|
|
153
169
|
resourceId: "qemu/100",
|
|
@@ -165,7 +181,7 @@ describe("MonitorCriteriaEvaluator - Proxmox root cause breakdown", () => {
|
|
|
165
181
|
nodeName: "pve2",
|
|
166
182
|
metricValue: 0.97,
|
|
167
183
|
},
|
|
168
|
-
// Zero-value row — must be dropped from the
|
|
184
|
+
// Zero-value row — must be dropped from the list.
|
|
169
185
|
{
|
|
170
186
|
resourceId: "qemu/102",
|
|
171
187
|
resourceName: "idle-vm",
|
|
@@ -195,7 +211,9 @@ describe("MonitorCriteriaEvaluator - Proxmox root cause breakdown", () => {
|
|
|
195
211
|
expect(context).toContain("- Cluster: prod-cluster");
|
|
196
212
|
expect(context).toContain("- Metric: CPU Usage (`pve_cpu_usage_ratio`)");
|
|
197
213
|
|
|
198
|
-
|
|
214
|
+
// A list, not a table: no GFM table row survives anywhere.
|
|
215
|
+
expect(context).not.toContain("| --- |");
|
|
216
|
+
expect(context).not.toMatch(/^\|/m);
|
|
199
217
|
// Worst (0.97) sorts above 0.42; the zero row is gone entirely.
|
|
200
218
|
const dbIndex: number = context!.indexOf("`db-vm` (`qemu/101`)");
|
|
201
219
|
const webIndex: number = context!.indexOf("`web-vm` (`qemu/100`)");
|
|
@@ -205,20 +223,26 @@ describe("MonitorCriteriaEvaluator - Proxmox root cause breakdown", () => {
|
|
|
205
223
|
expect(context).toContain("**Affected Resources** (2 total)");
|
|
206
224
|
|
|
207
225
|
/*
|
|
208
|
-
* The
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* otherwise have to recognise as a percentage themselves.
|
|
226
|
+
* The value is rendered in the unit the catalog declares for the
|
|
227
|
+
* metric — a "ratio" on a `_ratio`-suffixed metric is a fraction, so
|
|
228
|
+
* the reader gets "97.00%" rather than the bare "0.97" they would
|
|
229
|
+
* otherwise have to recognise as a percentage themselves. A qemu
|
|
230
|
+
* guest is titled "Virtual Machine", and the node it runs on sits
|
|
231
|
+
* under it.
|
|
212
232
|
*/
|
|
213
233
|
expect(context).toContain(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
234
|
+
[
|
|
235
|
+
"**Affected Resources** (2 total)",
|
|
236
|
+
"",
|
|
237
|
+
"1. **Virtual Machine** `db-vm` (`qemu/101`) — **97.00%**",
|
|
238
|
+
" - Node: `pve2`",
|
|
239
|
+
"2. **Virtual Machine** `web-vm` (`qemu/100`) — **42.00%**",
|
|
240
|
+
" - Node: `pve1`",
|
|
241
|
+
].join("\n"),
|
|
218
242
|
);
|
|
219
243
|
});
|
|
220
244
|
|
|
221
|
-
test("renders
|
|
245
|
+
test("renders byte values at human scale", () => {
|
|
222
246
|
const context: string | null = Evaluator.buildProxmoxRootCauseContext({
|
|
223
247
|
dataToProcess: metricResponse({
|
|
224
248
|
proxmoxResourceBreakdown: {
|
|
@@ -247,12 +271,12 @@ describe("MonitorCriteriaEvaluator - Proxmox root cause breakdown", () => {
|
|
|
247
271
|
* 3am should not have to divide by 2^30 in their head.
|
|
248
272
|
*/
|
|
249
273
|
expect(context).toContain(
|
|
250
|
-
"
|
|
274
|
+
"1. **Virtual Machine** `db-vm` (`qemu/101`) — **8.59 GB**",
|
|
251
275
|
);
|
|
252
276
|
expect(context).not.toContain("**8589934592**");
|
|
253
277
|
});
|
|
254
278
|
|
|
255
|
-
test("caps the
|
|
279
|
+
test("caps the list at 10 items, worst first, with an overflow suffix", () => {
|
|
256
280
|
const affectedResources: Array<ProxmoxAffectedResource> = [];
|
|
257
281
|
for (let i: number = 1; i <= 12; i++) {
|
|
258
282
|
affectedResources.push({
|
|
@@ -286,13 +310,18 @@ describe("MonitorCriteriaEvaluator - Proxmox root cause breakdown", () => {
|
|
|
286
310
|
expect(context).toContain("**Affected Resources** (12 total)");
|
|
287
311
|
expect(context).toContain("*... and 2 more affected resources*");
|
|
288
312
|
// The worst rows survive the cap; the mildest two are cut.
|
|
289
|
-
expect(context).toContain("**12**");
|
|
290
|
-
expect(context).toContain("**3**");
|
|
291
|
-
expect(context).not.toContain("
|
|
292
|
-
expect(context).not.toContain("
|
|
313
|
+
expect(context).toContain("1. **Virtual Machine** `qemu/112` — **12**");
|
|
314
|
+
expect(context).toContain("10. **Virtual Machine** `qemu/103` — **3**");
|
|
315
|
+
expect(context).not.toContain("`qemu/102`");
|
|
316
|
+
expect(context).not.toContain("`qemu/101`");
|
|
317
|
+
expect(context).not.toContain("11. ");
|
|
318
|
+
// The summary is its own paragraph, not a continuation of item 10.
|
|
319
|
+
expect(context).toContain(
|
|
320
|
+
"10. **Virtual Machine** `qemu/103` — **3**\n\n*... and 2 more affected resources*",
|
|
321
|
+
);
|
|
293
322
|
});
|
|
294
323
|
|
|
295
|
-
test("identity-less (cluster-wide) breakdowns render no
|
|
324
|
+
test("identity-less (cluster-wide) breakdowns render no list and fall back to the metric summary", () => {
|
|
296
325
|
const context: string | null = Evaluator.buildProxmoxRootCauseContext({
|
|
297
326
|
dataToProcess: metricResponse({
|
|
298
327
|
proxmoxResourceBreakdown: {
|
|
@@ -311,7 +340,8 @@ describe("MonitorCriteriaEvaluator - Proxmox root cause breakdown", () => {
|
|
|
311
340
|
monitor: new Monitor(),
|
|
312
341
|
});
|
|
313
342
|
|
|
314
|
-
expect(context).not.toContain("
|
|
343
|
+
expect(context).not.toContain("**Affected Resources**");
|
|
344
|
+
expect(context).not.toContain("1. ");
|
|
315
345
|
expect(context).toContain("**Metric Summary**");
|
|
316
346
|
expect(context).toContain("- 2 metric data point(s) returned");
|
|
317
347
|
});
|
|
@@ -346,12 +376,13 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
346
376
|
/*
|
|
347
377
|
* VMware twin of the Proxmox block. The differences are deliberate:
|
|
348
378
|
* identity comes from the vcenter receiver's RESOURCE attributes
|
|
349
|
-
* (stored `resource.vcenter.*`),
|
|
350
|
-
*
|
|
351
|
-
* are already 0–100 percentages (catalog unit
|
|
352
|
-
* ratios — so the
|
|
379
|
+
* (stored `resource.vcenter.*`), each item is titled by the vSphere
|
|
380
|
+
* object (VM, host, pool, ...) with its host and cluster beneath it,
|
|
381
|
+
* and utilization metrics are already 0–100 percentages (catalog unit
|
|
382
|
+
* "%") — never [0, 1] ratios — so the value must print as-is and never
|
|
383
|
+
* ×100.
|
|
353
384
|
*/
|
|
354
|
-
test("renders
|
|
385
|
+
test("renders a ranked list of object, host and cluster: zero rows dropped, worst first, % as-is", () => {
|
|
355
386
|
const affectedResources: Array<VMwareAffectedResource> = [
|
|
356
387
|
{
|
|
357
388
|
datacenterName: "DC1",
|
|
@@ -365,7 +396,7 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
365
396
|
hostName: "esx-02",
|
|
366
397
|
metricValue: 97.25,
|
|
367
398
|
},
|
|
368
|
-
// Zero-value row — must be dropped from the
|
|
399
|
+
// Zero-value row — must be dropped from the list.
|
|
369
400
|
{
|
|
370
401
|
datacenterName: "DC1",
|
|
371
402
|
clusterName: "prod-cluster",
|
|
@@ -395,7 +426,7 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
395
426
|
"- Metric: Host CPU Utilization (`vcenter.host.cpu.utilization`)",
|
|
396
427
|
);
|
|
397
428
|
|
|
398
|
-
expect(context).toContain("|
|
|
429
|
+
expect(context).not.toContain("| --- |");
|
|
399
430
|
// Worst (97.25) sorts above 42.5; the zero row is gone entirely.
|
|
400
431
|
const worstIndex: number = context!.indexOf("`esx-02`");
|
|
401
432
|
const mildIndex: number = context!.indexOf("`esx-01`");
|
|
@@ -407,15 +438,18 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
407
438
|
/*
|
|
408
439
|
* The catalog declares "%" for the receiver's utilization metrics,
|
|
409
440
|
* whose values are already percentages: 97.25 must read "97.25%",
|
|
410
|
-
* never "9725.00%". A host IS the resource, so
|
|
411
|
-
*
|
|
441
|
+
* never "9725.00%". A host IS the resource, so there is no "Host:"
|
|
442
|
+
* detail repeating it — only the cluster it belongs to.
|
|
412
443
|
*/
|
|
413
444
|
expect(context).toContain(
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
445
|
+
[
|
|
446
|
+
"1. **Host** `esx-02` — **97.25%**",
|
|
447
|
+
" - Cluster: `prod-cluster`",
|
|
448
|
+
"2. **Host** `esx-01` — **42.50%**",
|
|
449
|
+
" - Cluster: `prod-cluster`",
|
|
450
|
+
].join("\n"),
|
|
418
451
|
);
|
|
452
|
+
expect(context).not.toContain("- Host: `esx-0");
|
|
419
453
|
expect(context).not.toContain("9725");
|
|
420
454
|
});
|
|
421
455
|
|
|
@@ -441,11 +475,11 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
441
475
|
monitor: new Monitor(),
|
|
442
476
|
});
|
|
443
477
|
|
|
444
|
-
expect(context).toContain("
|
|
478
|
+
expect(context).toContain("1. **Host** `esx-01` — **0.42%**");
|
|
445
479
|
expect(context).not.toContain("42.00%");
|
|
446
480
|
});
|
|
447
481
|
|
|
448
|
-
test("renders
|
|
482
|
+
test("renders VMs with their parent host and cluster, and bytes at human scale", () => {
|
|
449
483
|
const context: string | null = Evaluator.buildVMwareRootCauseContext({
|
|
450
484
|
dataToProcess: metricResponse({
|
|
451
485
|
vmwareResourceBreakdown: {
|
|
@@ -480,22 +514,25 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
480
514
|
|
|
481
515
|
/*
|
|
482
516
|
* A VM row carries a resource pool AND a host, but it is still a
|
|
483
|
-
* VM: the
|
|
484
|
-
*
|
|
485
|
-
*
|
|
517
|
+
* VM: the title must say so (VM check runs first), the Host detail
|
|
518
|
+
* is the ESXi host running it, and the on-call engineer should not
|
|
519
|
+
* have to divide by 2^30 in their head. A VM on a standalone ESXi
|
|
520
|
+
* host has no cluster, so it simply has no Cluster detail.
|
|
486
521
|
*/
|
|
487
522
|
expect(context).toContain(
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
523
|
+
[
|
|
524
|
+
"1. **Virtual Machine** `db-01` — **8.59 GB**",
|
|
525
|
+
" - Host: `esx-02`",
|
|
526
|
+
" - Cluster: `prod-cluster`",
|
|
527
|
+
"2. **Virtual Machine** `lab-01` — **1.07 GB**",
|
|
528
|
+
" - Host: `esx-standalone`",
|
|
529
|
+
].join("\n"),
|
|
493
530
|
);
|
|
494
531
|
expect(context).not.toContain("**8589934592**");
|
|
495
532
|
expect(context).not.toContain("Resource Pool");
|
|
496
533
|
});
|
|
497
534
|
|
|
498
|
-
test("renders
|
|
535
|
+
test("renders datastores, clusters, datacenters and resource pools by their own identity", () => {
|
|
499
536
|
const context: string | null = Evaluator.buildVMwareRootCauseContext({
|
|
500
537
|
dataToProcess: metricResponse({
|
|
501
538
|
vmwareResourceBreakdown: {
|
|
@@ -533,15 +570,18 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
533
570
|
* `{hosts}` is an annotation-only unit, so counts render as bare
|
|
534
571
|
* integers. Each kind resolves by the most specific identity
|
|
535
572
|
* attribute it carries: a datacenter-only row is a Datacenter, a
|
|
536
|
-
* cluster
|
|
537
|
-
*
|
|
538
|
-
*
|
|
573
|
+
* cluster shows no duplicate Cluster detail, a pool shows its name
|
|
574
|
+
* with the inventory path (pool names are only unique within a
|
|
575
|
+
* parent).
|
|
539
576
|
*/
|
|
540
|
-
expect(context).toContain("| `DC1` | Datacenter | - | - | **4** |");
|
|
541
|
-
expect(context).toContain("| `prod-cluster` | Cluster | - | - | **3** |");
|
|
542
|
-
expect(context).toContain("| `vsan-ds-01` | Datastore | - | - | **2** |");
|
|
543
577
|
expect(context).toContain(
|
|
544
|
-
|
|
578
|
+
[
|
|
579
|
+
"1. **Datacenter** `DC1` — **4**",
|
|
580
|
+
"2. **Cluster** `prod-cluster` — **3**",
|
|
581
|
+
"3. **Datastore** `vsan-ds-01` — **2**",
|
|
582
|
+
"4. **Resource Pool** `batch` (`/DC1/host/prod-cluster/Resources/batch`) — **1**",
|
|
583
|
+
" - Cluster: `prod-cluster`",
|
|
584
|
+
].join("\n"),
|
|
545
585
|
);
|
|
546
586
|
});
|
|
547
587
|
|
|
@@ -563,12 +603,10 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
563
603
|
});
|
|
564
604
|
|
|
565
605
|
// Unit from the catalog ("ms"), not a bare, unit-less 85.
|
|
566
|
-
expect(context).toMatch(
|
|
567
|
-
/\| `esx-01` \| Host \| - \| - \| \*\*85(\.00)? ms\*\* \|/,
|
|
568
|
-
);
|
|
606
|
+
expect(context).toMatch(/1\. \*\*Host\*\* `esx-01` — \*\*85(\.00)? ms\*\*/);
|
|
569
607
|
});
|
|
570
608
|
|
|
571
|
-
test("caps the
|
|
609
|
+
test("caps the list at 10 items, worst first, with an overflow suffix", () => {
|
|
572
610
|
const affectedResources: Array<VMwareAffectedResource> = [];
|
|
573
611
|
for (let i: number = 1; i <= 12; i++) {
|
|
574
612
|
affectedResources.push({
|
|
@@ -595,13 +633,13 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
595
633
|
expect(context).toContain("**Affected Resources** (12 total)");
|
|
596
634
|
expect(context).toContain("*... and 2 more affected resources*");
|
|
597
635
|
// The worst rows survive the cap; the mildest two are cut.
|
|
598
|
-
expect(context).toContain("`esx-12`");
|
|
599
|
-
expect(context).toContain("`esx-03`");
|
|
636
|
+
expect(context).toContain("1. **Host** `esx-12` — **12**");
|
|
637
|
+
expect(context).toContain("10. **Host** `esx-03` — **3**");
|
|
600
638
|
expect(context).not.toContain("`esx-02`");
|
|
601
639
|
expect(context).not.toContain("`esx-01`");
|
|
602
640
|
});
|
|
603
641
|
|
|
604
|
-
test("identity-less breakdowns render no
|
|
642
|
+
test("identity-less breakdowns render no list and fall back to the metric summary", () => {
|
|
605
643
|
const context: string | null = Evaluator.buildVMwareRootCauseContext({
|
|
606
644
|
dataToProcess: metricResponse({
|
|
607
645
|
vmwareResourceBreakdown: {
|
|
@@ -620,9 +658,7 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
620
658
|
monitor: new Monitor(),
|
|
621
659
|
});
|
|
622
660
|
|
|
623
|
-
expect(context).not.toContain(
|
|
624
|
-
"| Resource | Kind | Host | Cluster | Value |",
|
|
625
|
-
);
|
|
661
|
+
expect(context).not.toContain("**Affected Resources**");
|
|
626
662
|
expect(context).toContain("**Metric Summary**");
|
|
627
663
|
expect(context).toContain("- 2 metric data point(s) returned");
|
|
628
664
|
});
|
|
@@ -678,7 +714,7 @@ describe("MonitorCriteriaEvaluator - VMware root cause breakdown", () => {
|
|
|
678
714
|
});
|
|
679
715
|
|
|
680
716
|
describe("MonitorCriteriaEvaluator - Ceph root cause breakdown", () => {
|
|
681
|
-
test("renders
|
|
717
|
+
test("renders a ranked list of daemons and pools with pool name+id", () => {
|
|
682
718
|
const affectedResources: Array<CephAffectedResource> = [
|
|
683
719
|
{ daemon: "osd.3", hostname: "ceph-node-1", metricValue: 250 },
|
|
684
720
|
{ poolId: "2", poolName: "rbd", metricValue: 91 },
|
|
@@ -706,15 +742,20 @@ describe("MonitorCriteriaEvaluator - Ceph root cause breakdown", () => {
|
|
|
706
742
|
"- Metric: OSD Apply Latency (`ceph_osd_apply_latency_ms`)",
|
|
707
743
|
);
|
|
708
744
|
|
|
709
|
-
expect(context).toContain("| Daemon | Pool | Host | Value |");
|
|
710
745
|
/*
|
|
711
746
|
* ceph_osd_apply_latency_ms carries the catalog unit "ms", so the
|
|
712
|
-
*
|
|
747
|
+
* value names it. 250 ms stays "250 ms" rather than scaling to
|
|
713
748
|
* seconds — the ladder only moves a value when the magnitude asks
|
|
714
|
-
* for it.
|
|
749
|
+
* for it. A row with no daemon is titled by its pool.
|
|
715
750
|
*/
|
|
716
|
-
expect(context).toContain(
|
|
717
|
-
|
|
751
|
+
expect(context).toContain(
|
|
752
|
+
[
|
|
753
|
+
"1. **Daemon** `osd.3` — **250 ms**",
|
|
754
|
+
" - Host: `ceph-node-1`",
|
|
755
|
+
"2. **Pool** `rbd` (`2`) — **91 ms**",
|
|
756
|
+
].join("\n"),
|
|
757
|
+
);
|
|
758
|
+
expect(context).not.toContain("| --- |");
|
|
718
759
|
expect(context).not.toContain("osd.5");
|
|
719
760
|
expect(context).toContain("**Affected Resources** (2 total)");
|
|
720
761
|
|
|
@@ -726,14 +767,14 @@ describe("MonitorCriteriaEvaluator - Ceph root cause breakdown", () => {
|
|
|
726
767
|
});
|
|
727
768
|
|
|
728
769
|
/*
|
|
729
|
-
* BACKWARD COMPATIBILITY for the platform
|
|
770
|
+
* BACKWARD COMPATIBILITY for the platform breakdowns.
|
|
730
771
|
*
|
|
731
772
|
* A metric whose catalog unit is "count" or absent has no dimension to
|
|
732
|
-
* report, and its
|
|
773
|
+
* report, and its value must keep every digit — never a chart's
|
|
733
774
|
* abbreviated "1.2K", and never the noise "250 count". ceph_osd_up is
|
|
734
775
|
* `unit: "count"`; ceph_health_status has no unit at all.
|
|
735
776
|
*/
|
|
736
|
-
test("a count metric keeps exact bare digits in the
|
|
777
|
+
test("a count metric keeps exact bare digits in the value", () => {
|
|
737
778
|
const context: string | null = Evaluator.buildCephRootCauseContext({
|
|
738
779
|
dataToProcess: metricResponse({
|
|
739
780
|
cephResourceBreakdown: {
|
|
@@ -750,16 +791,18 @@ describe("MonitorCriteriaEvaluator - Ceph root cause breakdown", () => {
|
|
|
750
791
|
monitor: new Monitor(),
|
|
751
792
|
});
|
|
752
793
|
|
|
753
|
-
expect(context).toContain(
|
|
794
|
+
expect(context).toContain(
|
|
795
|
+
"1. **Daemon** `osd.3` — **5000**\n - Host: `ceph-node-1`",
|
|
796
|
+
);
|
|
754
797
|
expect(context).not.toContain("5K");
|
|
755
798
|
expect(context).not.toContain("count");
|
|
756
799
|
});
|
|
757
800
|
|
|
758
801
|
/*
|
|
759
|
-
* ORDERING INVARIANT. The
|
|
802
|
+
* ORDERING INVARIANT. The list sorts and filters on the RAW numeric
|
|
760
803
|
* metricValue and only formats at render time. If a refactor ever sorted
|
|
761
804
|
* the formatted strings instead, "1.07 GB" would sort below "900 KB" and
|
|
762
|
-
* the worst-first
|
|
805
|
+
* the worst-first list would silently invert — putting the healthiest
|
|
763
806
|
* daemon at the top of an incident.
|
|
764
807
|
*/
|
|
765
808
|
test("rows still sort worst-first on the raw value, not the formatted string", () => {
|
|
@@ -780,8 +823,8 @@ describe("MonitorCriteriaEvaluator - Ceph root cause breakdown", () => {
|
|
|
780
823
|
monitor: new Monitor(),
|
|
781
824
|
});
|
|
782
825
|
|
|
783
|
-
expect(context).toContain("
|
|
784
|
-
expect(context).toContain("
|
|
826
|
+
expect(context).toContain("1. **Daemon** `osd.bigger` — **1.07 GB**");
|
|
827
|
+
expect(context).toContain("2. **Daemon** `osd.smaller` — **922 KB**");
|
|
785
828
|
|
|
786
829
|
// "1.07 GB" < "922 KB" as strings — the raw numbers must drive this.
|
|
787
830
|
const biggerIndex: number = context!.indexOf("`osd.bigger`");
|
|
@@ -790,7 +833,7 @@ describe("MonitorCriteriaEvaluator - Ceph root cause breakdown", () => {
|
|
|
790
833
|
expect(smallerIndex).toBeGreaterThan(biggerIndex);
|
|
791
834
|
});
|
|
792
835
|
|
|
793
|
-
test("cluster-wide series (ceph_health_status) render no
|
|
836
|
+
test("cluster-wide series (ceph_health_status) render no list", () => {
|
|
794
837
|
const context: string | null = Evaluator.buildCephRootCauseContext({
|
|
795
838
|
dataToProcess: metricResponse({
|
|
796
839
|
cephResourceBreakdown: {
|
|
@@ -806,7 +849,7 @@ describe("MonitorCriteriaEvaluator - Ceph root cause breakdown", () => {
|
|
|
806
849
|
});
|
|
807
850
|
|
|
808
851
|
expect(context).toContain("- Cluster: prod-cluster");
|
|
809
|
-
expect(context).not.toContain("
|
|
852
|
+
expect(context).not.toContain("**Affected Resources**");
|
|
810
853
|
});
|
|
811
854
|
|
|
812
855
|
test("surfaces the worker's resource-filter mapping (ceph_daemon / pool_id) in the cluster context", () => {
|
|
@@ -835,16 +878,16 @@ describe("MonitorCriteriaEvaluator - Ceph root cause breakdown", () => {
|
|
|
835
878
|
* ceph_osd_up / ceph_osd_in / ceph_mon_quorum_status are 0/1 gauges and
|
|
836
879
|
* the OSD Down / OSD Out / Quorum Degraded criteria fire on `< 1`, so the
|
|
837
880
|
* ZERO rows are the down daemons — the filter dropped exactly the daemons
|
|
838
|
-
* the incident was about and rendered a
|
|
881
|
+
* the incident was about and rendered a list of the HEALTHY ones, under
|
|
839
882
|
* an incident description that ends "Check the root cause for the affected
|
|
840
883
|
* ceph_daemon label."
|
|
841
884
|
*
|
|
842
885
|
* The predicate now mirrors the criteria that matched, but ONLY inverts
|
|
843
886
|
* for a criteria that fires when the metric FALLS. Every upward comparison
|
|
844
887
|
* (`> 0` health checks, PG counts, latency, capacity ratios) and every
|
|
845
|
-
* `= 0` RECOVERY criteria keeps the old `> 0`, worst-highest-first
|
|
888
|
+
* `= 0` RECOVERY criteria keeps the old `> 0`, worst-highest-first list
|
|
846
889
|
* byte for byte — the recovery case matters because deriving the predicate
|
|
847
|
-
* from an `= 0` criteria would newly render an "Affected Resources"
|
|
890
|
+
* from an `= 0` criteria would newly render an "Affected Resources" list
|
|
848
891
|
* of every resource sitting at 0 on the all-clear.
|
|
849
892
|
*/
|
|
850
893
|
describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate", () => {
|
|
@@ -933,8 +976,9 @@ describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate",
|
|
|
933
976
|
],
|
|
934
977
|
});
|
|
935
978
|
|
|
936
|
-
expect(context).toContain(
|
|
937
|
-
|
|
979
|
+
expect(context).toContain(
|
|
980
|
+
"1. **Daemon** `osd.3` — **0**\n - Host: `ceph-node-1`",
|
|
981
|
+
);
|
|
938
982
|
expect(context).not.toContain("osd.4");
|
|
939
983
|
expect(context).not.toContain("osd.5");
|
|
940
984
|
expect(context).toContain("**Affected Resources** (1 total)");
|
|
@@ -960,8 +1004,8 @@ describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate",
|
|
|
960
1004
|
});
|
|
961
1005
|
|
|
962
1006
|
expect(context).toContain("**Affected Resources** (2 total)");
|
|
963
|
-
expect(context).toContain("`osd.1`");
|
|
964
|
-
expect(context).toContain("`osd.2`");
|
|
1007
|
+
expect(context).toContain("1. **Daemon** `osd.1` — **0**");
|
|
1008
|
+
expect(context).toContain("2. **Daemon** `osd.2` — **0**");
|
|
965
1009
|
expect(context).not.toContain("`osd.10`");
|
|
966
1010
|
expect(context).not.toContain("*... and");
|
|
967
1011
|
});
|
|
@@ -1009,8 +1053,13 @@ describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate",
|
|
|
1009
1053
|
],
|
|
1010
1054
|
});
|
|
1011
1055
|
|
|
1012
|
-
expect(context).toContain(
|
|
1013
|
-
|
|
1056
|
+
expect(context).toContain(
|
|
1057
|
+
[
|
|
1058
|
+
"1. **Daemon** `osd.3` — **250 ms**",
|
|
1059
|
+
" - Host: `ceph-node-1`",
|
|
1060
|
+
"2. **Pool** `rbd` (`2`) — **91 ms**",
|
|
1061
|
+
].join("\n"),
|
|
1062
|
+
);
|
|
1014
1063
|
expect(context).not.toContain("osd.5");
|
|
1015
1064
|
expect(context).toContain("**Affected Resources** (2 total)");
|
|
1016
1065
|
|
|
@@ -1020,7 +1069,7 @@ describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate",
|
|
|
1020
1069
|
expect(poolIndex).toBeGreaterThan(osdIndex);
|
|
1021
1070
|
});
|
|
1022
1071
|
|
|
1023
|
-
test("an `= 0` recovery criteria does NOT turn the all-clear into a
|
|
1072
|
+
test("an `= 0` recovery criteria does NOT turn the all-clear into a list of zeroes", () => {
|
|
1024
1073
|
const context: string | null = cephContext({
|
|
1025
1074
|
metricName: "ceph_pg_degraded",
|
|
1026
1075
|
criteriaInstance: syntheticCriteria([
|
|
@@ -1036,7 +1085,7 @@ describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate",
|
|
|
1036
1085
|
],
|
|
1037
1086
|
});
|
|
1038
1087
|
|
|
1039
|
-
expect(context).not.toContain("
|
|
1088
|
+
expect(context).not.toContain("**Pool**");
|
|
1040
1089
|
expect(context).not.toContain("**Affected Resources**");
|
|
1041
1090
|
});
|
|
1042
1091
|
|
|
@@ -1049,7 +1098,7 @@ describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate",
|
|
|
1049
1098
|
],
|
|
1050
1099
|
});
|
|
1051
1100
|
|
|
1052
|
-
expect(context).toContain("
|
|
1101
|
+
expect(context).toContain("1. **Daemon** `osd.4` — **1**");
|
|
1053
1102
|
expect(context).not.toContain("osd.3");
|
|
1054
1103
|
});
|
|
1055
1104
|
|
|
@@ -1061,7 +1110,7 @@ describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate",
|
|
|
1061
1110
|
],
|
|
1062
1111
|
});
|
|
1063
1112
|
|
|
1064
|
-
expect(context).toContain("
|
|
1113
|
+
expect(context).toContain("1. **Daemon** `osd.4` — **1**");
|
|
1065
1114
|
expect(context).not.toContain("osd.3");
|
|
1066
1115
|
});
|
|
1067
1116
|
|
|
@@ -1080,7 +1129,7 @@ describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate",
|
|
|
1080
1129
|
],
|
|
1081
1130
|
});
|
|
1082
1131
|
|
|
1083
|
-
expect(context).toContain("
|
|
1132
|
+
expect(context).toContain("1. **Daemon** `osd.4` — **1**");
|
|
1084
1133
|
expect(context).not.toContain("osd.3");
|
|
1085
1134
|
});
|
|
1086
1135
|
|
|
@@ -1104,7 +1153,7 @@ describe("MonitorCriteriaEvaluator - Ceph affected-resources breach predicate",
|
|
|
1104
1153
|
],
|
|
1105
1154
|
});
|
|
1106
1155
|
|
|
1107
|
-
expect(context).toContain("
|
|
1156
|
+
expect(context).toContain("1. **Daemon** `osd.4` — **1**");
|
|
1108
1157
|
expect(context).not.toContain("osd.3");
|
|
1109
1158
|
});
|
|
1110
1159
|
});
|
|
@@ -1333,3 +1382,792 @@ describe("MonitorCriteriaEvaluator - Kubernetes root cause analysis scoping", ()
|
|
|
1333
1382
|
});
|
|
1334
1383
|
});
|
|
1335
1384
|
});
|
|
1385
|
+
|
|
1386
|
+
/*
|
|
1387
|
+
* The Kubernetes "Affected Resources" block — the one the table-to-list
|
|
1388
|
+
* change was made for. A pod-restart incident on a GKE cluster used to
|
|
1389
|
+
* carry a six-column table (Namespace / Workload Type / Workload / Pod /
|
|
1390
|
+
* Node / Value) that wrapped every identifier in the email card. Each
|
|
1391
|
+
* resource is now one numbered item titled by the most specific object
|
|
1392
|
+
* the series names, with the rest of its identity as nested bullets.
|
|
1393
|
+
*/
|
|
1394
|
+
describe("MonitorCriteriaEvaluator - Kubernetes affected resources list", () => {
|
|
1395
|
+
// The first line of a top-level list item: "1. ", "10. ", ...
|
|
1396
|
+
const NUMBERED_ITEM: RegExp = /^\d+\. /;
|
|
1397
|
+
|
|
1398
|
+
async function kubernetesContext(input: {
|
|
1399
|
+
affectedResources: Array<KubernetesAffectedResource>;
|
|
1400
|
+
metricName?: string;
|
|
1401
|
+
clusterName?: string;
|
|
1402
|
+
}): Promise<string | null> {
|
|
1403
|
+
return Evaluator.buildKubernetesRootCauseContext({
|
|
1404
|
+
dataToProcess: metricResponse({
|
|
1405
|
+
kubernetesResourceBreakdown: {
|
|
1406
|
+
clusterName:
|
|
1407
|
+
input.clusterName === undefined ? "prod-gke" : input.clusterName,
|
|
1408
|
+
metricName: input.metricName || "k8s.container.restarts",
|
|
1409
|
+
metricFriendlyName: "Container Restarts",
|
|
1410
|
+
affectedResources: input.affectedResources,
|
|
1411
|
+
attributes: {},
|
|
1412
|
+
},
|
|
1413
|
+
}),
|
|
1414
|
+
monitorStep: new MonitorStep(),
|
|
1415
|
+
// No projectId, so the restart branch never reaches for container logs.
|
|
1416
|
+
monitor: new Monitor(),
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
function podRestart(
|
|
1421
|
+
overrides: Partial<KubernetesAffectedResource>,
|
|
1422
|
+
): KubernetesAffectedResource {
|
|
1423
|
+
return {
|
|
1424
|
+
namespace: "default",
|
|
1425
|
+
workloadType: "Deployment",
|
|
1426
|
+
workloadName: "oneuptime-worker",
|
|
1427
|
+
podName: "oneuptime-worker-6b67db69c9-gtcp6",
|
|
1428
|
+
nodeName: "gke-prod-default-pool-662f6819-e9gq",
|
|
1429
|
+
metricValue: 2,
|
|
1430
|
+
...overrides,
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
test("renders the screenshot's pod restarts as a ranked list, not a table", async () => {
|
|
1435
|
+
const affectedResources: Array<KubernetesAffectedResource> = [
|
|
1436
|
+
podRestart({ metricValue: 2 }),
|
|
1437
|
+
podRestart({
|
|
1438
|
+
workloadType: "Job",
|
|
1439
|
+
workloadName: "oneuptime-migrate-267",
|
|
1440
|
+
podName: "oneuptime-migrate-267-k64qm",
|
|
1441
|
+
nodeName: "gke-prod-default-pool-662f6819-c6w3",
|
|
1442
|
+
metricValue: 3,
|
|
1443
|
+
}),
|
|
1444
|
+
// kube-proxy is a static pod: no owning workload at all.
|
|
1445
|
+
podRestart({
|
|
1446
|
+
namespace: "kube-system",
|
|
1447
|
+
workloadType: undefined,
|
|
1448
|
+
workloadName: undefined,
|
|
1449
|
+
podName: "kube-proxy-gke-prod-default-pool-662f6819-c6w3",
|
|
1450
|
+
nodeName: "gke-prod-default-pool-662f6819-c6w3",
|
|
1451
|
+
metricValue: 1,
|
|
1452
|
+
}),
|
|
1453
|
+
];
|
|
1454
|
+
|
|
1455
|
+
const context: string | null = await kubernetesContext({
|
|
1456
|
+
affectedResources,
|
|
1457
|
+
});
|
|
1458
|
+
|
|
1459
|
+
expect(context).toContain("**Kubernetes Cluster Details**");
|
|
1460
|
+
expect(context).toContain(
|
|
1461
|
+
[
|
|
1462
|
+
"**Affected Resources** (3 total)",
|
|
1463
|
+
"",
|
|
1464
|
+
"1. **Pod** `oneuptime-migrate-267-k64qm` — **3**",
|
|
1465
|
+
" - Namespace: `default`",
|
|
1466
|
+
" - Job: `oneuptime-migrate-267`",
|
|
1467
|
+
" - Node: `gke-prod-default-pool-662f6819-c6w3`",
|
|
1468
|
+
"2. **Pod** `oneuptime-worker-6b67db69c9-gtcp6` — **2**",
|
|
1469
|
+
" - Namespace: `default`",
|
|
1470
|
+
" - Deployment: `oneuptime-worker`",
|
|
1471
|
+
" - Node: `gke-prod-default-pool-662f6819-e9gq`",
|
|
1472
|
+
"3. **Pod** `kube-proxy-gke-prod-default-pool-662f6819-c6w3` — **1**",
|
|
1473
|
+
" - Namespace: `kube-system`",
|
|
1474
|
+
" - Node: `gke-prod-default-pool-662f6819-c6w3`",
|
|
1475
|
+
].join("\n"),
|
|
1476
|
+
);
|
|
1477
|
+
|
|
1478
|
+
// None of the old table survives.
|
|
1479
|
+
expect(context).not.toContain("| Namespace |");
|
|
1480
|
+
expect(context).not.toContain("Workload Type");
|
|
1481
|
+
expect(context).not.toContain("| --- |");
|
|
1482
|
+
expect(context).not.toMatch(/^\|/m);
|
|
1483
|
+
// A missing workload is left out, not printed as "-".
|
|
1484
|
+
expect(context).not.toContain(": -");
|
|
1485
|
+
expect(context).not.toContain("| - |");
|
|
1486
|
+
});
|
|
1487
|
+
|
|
1488
|
+
test("caps the list at 10 with an '... and N more' summary, like the 83-pod incident", async () => {
|
|
1489
|
+
const affectedResources: Array<KubernetesAffectedResource> = [];
|
|
1490
|
+
for (let i: number = 1; i <= 83; i++) {
|
|
1491
|
+
affectedResources.push(
|
|
1492
|
+
podRestart({ podName: `worker-${i}`, metricValue: i }),
|
|
1493
|
+
);
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
const context: string | null = await kubernetesContext({
|
|
1497
|
+
affectedResources,
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
expect(context).toContain("**Affected Resources** (83 total)");
|
|
1501
|
+
expect(context).toContain("1. **Pod** `worker-83` — **83**");
|
|
1502
|
+
expect(context).toContain(
|
|
1503
|
+
[
|
|
1504
|
+
"10. **Pod** `worker-74` — **74**",
|
|
1505
|
+
" - Namespace: `default`",
|
|
1506
|
+
" - Deployment: `oneuptime-worker`",
|
|
1507
|
+
" - Node: `gke-prod-default-pool-662f6819-e9gq`",
|
|
1508
|
+
"",
|
|
1509
|
+
"*... and 73 more affected resources*",
|
|
1510
|
+
].join("\n"),
|
|
1511
|
+
);
|
|
1512
|
+
expect(context).not.toContain("11. ");
|
|
1513
|
+
expect(context).not.toContain("`worker-73`");
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
test("a container-level series is titled by the container and its pod", async () => {
|
|
1517
|
+
const context: string | null = await kubernetesContext({
|
|
1518
|
+
affectedResources: [
|
|
1519
|
+
podRestart({ containerName: "app", podName: "checkout-7d9f" }),
|
|
1520
|
+
],
|
|
1521
|
+
});
|
|
1522
|
+
|
|
1523
|
+
expect(context).toContain(
|
|
1524
|
+
[
|
|
1525
|
+
"1. **Container** `app` in pod `checkout-7d9f` — **2**",
|
|
1526
|
+
" - Namespace: `default`",
|
|
1527
|
+
" - Deployment: `oneuptime-worker`",
|
|
1528
|
+
" - Node: `gke-prod-default-pool-662f6819-e9gq`",
|
|
1529
|
+
].join("\n"),
|
|
1530
|
+
);
|
|
1531
|
+
// The pod is in the title, so it is not repeated as a bullet.
|
|
1532
|
+
expect(context).not.toContain("- Pod:");
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
/*
|
|
1536
|
+
* Every replica of a workload runs a container of the same name. Titled
|
|
1537
|
+
* by the container alone, three crash-looping replicas would read
|
|
1538
|
+
* "Container `checkout`" three times over.
|
|
1539
|
+
*/
|
|
1540
|
+
test("replicas that share a container name still get distinct titles", async () => {
|
|
1541
|
+
const context: string | null = await kubernetesContext({
|
|
1542
|
+
affectedResources: [
|
|
1543
|
+
podRestart({
|
|
1544
|
+
containerName: "checkout",
|
|
1545
|
+
podName: "checkout-7d9f6c5b8-abcde",
|
|
1546
|
+
metricValue: 5,
|
|
1547
|
+
}),
|
|
1548
|
+
podRestart({
|
|
1549
|
+
containerName: "checkout",
|
|
1550
|
+
podName: "checkout-7d9f6c5b8-fghij",
|
|
1551
|
+
metricValue: 4,
|
|
1552
|
+
}),
|
|
1553
|
+
podRestart({
|
|
1554
|
+
containerName: "checkout",
|
|
1555
|
+
podName: "checkout-7d9f6c5b8-klmno",
|
|
1556
|
+
metricValue: 3,
|
|
1557
|
+
}),
|
|
1558
|
+
],
|
|
1559
|
+
});
|
|
1560
|
+
|
|
1561
|
+
const titles: Array<string> = (context || "")
|
|
1562
|
+
.split("\n")
|
|
1563
|
+
.filter((line: string) => {
|
|
1564
|
+
return NUMBERED_ITEM.test(line);
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
expect(titles).toEqual([
|
|
1568
|
+
"1. **Container** `checkout` in pod `checkout-7d9f6c5b8-abcde` — **5**",
|
|
1569
|
+
"2. **Container** `checkout` in pod `checkout-7d9f6c5b8-fghij` — **4**",
|
|
1570
|
+
"3. **Container** `checkout` in pod `checkout-7d9f6c5b8-klmno` — **3**",
|
|
1571
|
+
]);
|
|
1572
|
+
expect(new Set(titles).size).toBe(3);
|
|
1573
|
+
});
|
|
1574
|
+
|
|
1575
|
+
test("a container without a pod is titled by the container alone", async () => {
|
|
1576
|
+
const context: string | null = await kubernetesContext({
|
|
1577
|
+
affectedResources: [
|
|
1578
|
+
{ containerName: "app", namespace: "default", metricValue: 2 },
|
|
1579
|
+
],
|
|
1580
|
+
});
|
|
1581
|
+
|
|
1582
|
+
expect(context).toContain(
|
|
1583
|
+
"1. **Container** `app` — **2**\n - Namespace: `default`",
|
|
1584
|
+
);
|
|
1585
|
+
});
|
|
1586
|
+
|
|
1587
|
+
test("a node-level series is titled by the node and has nothing to list under it", async () => {
|
|
1588
|
+
const context: string | null = await kubernetesContext({
|
|
1589
|
+
metricName: "k8s.node.memory.usage",
|
|
1590
|
+
affectedResources: [
|
|
1591
|
+
{ nodeName: "gke-prod-pool-a", metricValue: 8589934592 },
|
|
1592
|
+
{ nodeName: "gke-prod-pool-b", metricValue: 1073741824 },
|
|
1593
|
+
],
|
|
1594
|
+
});
|
|
1595
|
+
|
|
1596
|
+
expect(context).toContain(
|
|
1597
|
+
[
|
|
1598
|
+
"1. **Node** `gke-prod-pool-a` — **8.59 GB**",
|
|
1599
|
+
"2. **Node** `gke-prod-pool-b` — **1.07 GB**",
|
|
1600
|
+
].join("\n"),
|
|
1601
|
+
);
|
|
1602
|
+
expect(context).not.toContain("- Node:");
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
test("a workload-level series is titled by its workload type and does not repeat it", async () => {
|
|
1606
|
+
const context: string | null = await kubernetesContext({
|
|
1607
|
+
metricName: "k8s.deployment.unavailable_replicas",
|
|
1608
|
+
affectedResources: [
|
|
1609
|
+
{
|
|
1610
|
+
namespace: "payments",
|
|
1611
|
+
workloadType: "StatefulSet",
|
|
1612
|
+
workloadName: "ledger",
|
|
1613
|
+
metricValue: 2,
|
|
1614
|
+
},
|
|
1615
|
+
],
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
expect(context).toContain(
|
|
1619
|
+
"1. **StatefulSet** `ledger` — **2**\n - Namespace: `payments`",
|
|
1620
|
+
);
|
|
1621
|
+
expect(context).not.toContain("- StatefulSet:");
|
|
1622
|
+
});
|
|
1623
|
+
|
|
1624
|
+
test("a workload name without a type falls back to the generic 'Workload' label", async () => {
|
|
1625
|
+
const context: string | null = await kubernetesContext({
|
|
1626
|
+
affectedResources: [
|
|
1627
|
+
podRestart({ workloadType: undefined, workloadName: "mystery" }),
|
|
1628
|
+
],
|
|
1629
|
+
});
|
|
1630
|
+
|
|
1631
|
+
expect(context).toContain(" - Workload: `mystery`");
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1634
|
+
test("a workload-level series without a type is titled 'Workload'", async () => {
|
|
1635
|
+
const context: string | null = await kubernetesContext({
|
|
1636
|
+
affectedResources: [
|
|
1637
|
+
{ namespace: "payments", workloadName: "ledger", metricValue: 1 },
|
|
1638
|
+
],
|
|
1639
|
+
});
|
|
1640
|
+
|
|
1641
|
+
expect(context).toContain(
|
|
1642
|
+
"1. **Workload** `ledger` — **1**\n - Namespace: `payments`",
|
|
1643
|
+
);
|
|
1644
|
+
});
|
|
1645
|
+
|
|
1646
|
+
test("a namespace-level series is titled by the namespace", async () => {
|
|
1647
|
+
const context: string | null = await kubernetesContext({
|
|
1648
|
+
affectedResources: [{ namespace: "payments", metricValue: 4 }],
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
expect(context).toContain("1. **Namespace** `payments` — **4**");
|
|
1652
|
+
expect(context).not.toContain("- Namespace:");
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
test("a series with no Kubernetes identity is a cluster-level series and is titled by the cluster", async () => {
|
|
1656
|
+
const context: string | null = await kubernetesContext({
|
|
1657
|
+
affectedResources: [{ metricValue: 5 }],
|
|
1658
|
+
});
|
|
1659
|
+
|
|
1660
|
+
expect(context).toContain("1. **Cluster** `prod-gke` — **5**");
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
test("items of different shapes in one breakdown are each titled by their own kind", async () => {
|
|
1664
|
+
const context: string | null = await kubernetesContext({
|
|
1665
|
+
affectedResources: [
|
|
1666
|
+
podRestart({ containerName: "app", metricValue: 4 }),
|
|
1667
|
+
podRestart({ metricValue: 3 }),
|
|
1668
|
+
{ nodeName: "gke-prod-pool-a", metricValue: 2 },
|
|
1669
|
+
{ metricValue: 1 },
|
|
1670
|
+
],
|
|
1671
|
+
});
|
|
1672
|
+
|
|
1673
|
+
const titles: Array<string> = (context || "")
|
|
1674
|
+
.split("\n")
|
|
1675
|
+
.filter((line: string) => {
|
|
1676
|
+
return NUMBERED_ITEM.test(line);
|
|
1677
|
+
});
|
|
1678
|
+
|
|
1679
|
+
expect(titles).toEqual([
|
|
1680
|
+
"1. **Container** `app` in pod `oneuptime-worker-6b67db69c9-gtcp6` — **4**",
|
|
1681
|
+
"2. **Pod** `oneuptime-worker-6b67db69c9-gtcp6` — **3**",
|
|
1682
|
+
"3. **Node** `gke-prod-pool-a` — **2**",
|
|
1683
|
+
"4. **Cluster** `prod-gke` — **1**",
|
|
1684
|
+
]);
|
|
1685
|
+
});
|
|
1686
|
+
|
|
1687
|
+
test("zero-value resources are dropped, and an all-zero breakdown renders no list", async () => {
|
|
1688
|
+
const some: string | null = await kubernetesContext({
|
|
1689
|
+
affectedResources: [
|
|
1690
|
+
podRestart({ podName: "healthy", metricValue: 0 }),
|
|
1691
|
+
podRestart({ podName: "crashing", metricValue: 7 }),
|
|
1692
|
+
],
|
|
1693
|
+
});
|
|
1694
|
+
|
|
1695
|
+
expect(some).toContain("**Affected Resources** (1 total)");
|
|
1696
|
+
expect(some).toContain("1. **Pod** `crashing` — **7**");
|
|
1697
|
+
expect(some).not.toContain("healthy");
|
|
1698
|
+
|
|
1699
|
+
const none: string | null = await kubernetesContext({
|
|
1700
|
+
affectedResources: [podRestart({ metricValue: 0 })],
|
|
1701
|
+
});
|
|
1702
|
+
|
|
1703
|
+
expect(none).toContain("**Kubernetes Cluster Details**");
|
|
1704
|
+
expect(none).not.toContain("**Affected Resources**");
|
|
1705
|
+
expect(none).not.toContain("1. ");
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
test("the Root Cause Analysis still names the worst resource and follows the list as its own block", async () => {
|
|
1709
|
+
const context: string | null = await kubernetesContext({
|
|
1710
|
+
affectedResources: [
|
|
1711
|
+
podRestart({ containerName: "app", podName: "checkout-7d9f" }),
|
|
1712
|
+
],
|
|
1713
|
+
});
|
|
1714
|
+
|
|
1715
|
+
expect(context).toContain(
|
|
1716
|
+
" - Node: `gke-prod-default-pool-662f6819-e9gq`\n\n\n**Root Cause Analysis**",
|
|
1717
|
+
);
|
|
1718
|
+
expect(context).toContain("kubectl logs checkout-7d9f -c app --previous");
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
test("a name containing a backtick cannot break out of its code span", async () => {
|
|
1722
|
+
const context: string | null = await kubernetesContext({
|
|
1723
|
+
affectedResources: [podRestart({ podName: "odd`pod" })],
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1726
|
+
expect(context).toContain("1. **Pod** `` odd`pod `` — **2**");
|
|
1727
|
+
});
|
|
1728
|
+
|
|
1729
|
+
test("the whole root cause renders to an email as nested lists, not a table", async () => {
|
|
1730
|
+
const context: string | null = await kubernetesContext({
|
|
1731
|
+
affectedResources: [
|
|
1732
|
+
podRestart({ metricValue: 3 }),
|
|
1733
|
+
podRestart({ podName: "second", metricValue: 2 }),
|
|
1734
|
+
],
|
|
1735
|
+
});
|
|
1736
|
+
|
|
1737
|
+
const html: string = await Markdown.convertToHTML(
|
|
1738
|
+
context!,
|
|
1739
|
+
MarkdownContentType.Email,
|
|
1740
|
+
);
|
|
1741
|
+
|
|
1742
|
+
expect(html).not.toContain("<table");
|
|
1743
|
+
expect(html).not.toContain("<th");
|
|
1744
|
+
expect((html.match(/<ol /g) || []).length).toBe(1);
|
|
1745
|
+
// Cluster details list + one nested details list per resource.
|
|
1746
|
+
expect((html.match(/<ul /g) || []).length).toBe(3);
|
|
1747
|
+
expect(html).toMatch(
|
|
1748
|
+
/<li style="[^"]+"><strong>Pod<\/strong> <code[^>]*>oneuptime-worker-6b67db69c9-gtcp6<\/code> — <strong>3<\/strong><ul style="[^"]+"><li style="[^"]+">Namespace: <code[^>]*>default<\/code><\/li>/,
|
|
1749
|
+
);
|
|
1750
|
+
});
|
|
1751
|
+
});
|
|
1752
|
+
|
|
1753
|
+
describe("MonitorCriteriaEvaluator - Docker Swarm affected tasks list", () => {
|
|
1754
|
+
function swarmContext(input: {
|
|
1755
|
+
affectedResources: Array<DockerSwarmAffectedResource>;
|
|
1756
|
+
metricResult?: Array<any>;
|
|
1757
|
+
}): string | null {
|
|
1758
|
+
return Evaluator.buildDockerSwarmRootCauseContext({
|
|
1759
|
+
dataToProcess: metricResponse({
|
|
1760
|
+
dockerSwarmResourceBreakdown: {
|
|
1761
|
+
clusterName: "swarm-prod",
|
|
1762
|
+
metricName: "container.pids.count",
|
|
1763
|
+
metricFriendlyName: "Process Count",
|
|
1764
|
+
affectedResources: input.affectedResources,
|
|
1765
|
+
attributes: {},
|
|
1766
|
+
},
|
|
1767
|
+
metricResult: input.metricResult || [],
|
|
1768
|
+
}),
|
|
1769
|
+
monitorStep: new MonitorStep(),
|
|
1770
|
+
monitor: new Monitor(),
|
|
1771
|
+
});
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
test("renders each task with its service and node beneath it, worst first", () => {
|
|
1775
|
+
const context: string | null = swarmContext({
|
|
1776
|
+
affectedResources: [
|
|
1777
|
+
{
|
|
1778
|
+
containerName: "web.1.abc123",
|
|
1779
|
+
serviceName: "web",
|
|
1780
|
+
nodeName: "worker-1",
|
|
1781
|
+
metricValue: 40,
|
|
1782
|
+
},
|
|
1783
|
+
{
|
|
1784
|
+
containerName: "api.2.def456",
|
|
1785
|
+
serviceName: "api",
|
|
1786
|
+
nodeName: "worker-2",
|
|
1787
|
+
metricValue: 90,
|
|
1788
|
+
},
|
|
1789
|
+
// Zero row dropped.
|
|
1790
|
+
{
|
|
1791
|
+
containerName: "idle.1.xyz",
|
|
1792
|
+
serviceName: "idle",
|
|
1793
|
+
nodeName: "worker-3",
|
|
1794
|
+
metricValue: 0,
|
|
1795
|
+
},
|
|
1796
|
+
],
|
|
1797
|
+
});
|
|
1798
|
+
|
|
1799
|
+
expect(context).toContain("**Docker Swarm Cluster Details**");
|
|
1800
|
+
expect(context).toContain(
|
|
1801
|
+
[
|
|
1802
|
+
"**Affected Tasks** (2 total)",
|
|
1803
|
+
"",
|
|
1804
|
+
"1. **Task** `api.2.def456` — **90**",
|
|
1805
|
+
" - Service: `api`",
|
|
1806
|
+
" - Node: `worker-2`",
|
|
1807
|
+
"2. **Task** `web.1.abc123` — **40**",
|
|
1808
|
+
" - Service: `web`",
|
|
1809
|
+
" - Node: `worker-1`",
|
|
1810
|
+
].join("\n"),
|
|
1811
|
+
);
|
|
1812
|
+
expect(context).not.toContain("idle");
|
|
1813
|
+
expect(context).not.toContain("Task / Container");
|
|
1814
|
+
expect(context).not.toContain("| --- |");
|
|
1815
|
+
});
|
|
1816
|
+
|
|
1817
|
+
test("a series without a task is titled by its service, then its node", () => {
|
|
1818
|
+
const context: string | null = swarmContext({
|
|
1819
|
+
affectedResources: [
|
|
1820
|
+
{ serviceName: "web", nodeName: "worker-1", metricValue: 2 },
|
|
1821
|
+
{ nodeName: "worker-2", metricValue: 1 },
|
|
1822
|
+
],
|
|
1823
|
+
});
|
|
1824
|
+
|
|
1825
|
+
expect(context).toContain(
|
|
1826
|
+
[
|
|
1827
|
+
"1. **Service** `web` — **2**",
|
|
1828
|
+
" - Node: `worker-1`",
|
|
1829
|
+
"2. **Node** `worker-2` — **1**",
|
|
1830
|
+
].join("\n"),
|
|
1831
|
+
);
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1834
|
+
test("overflow is counted in tasks", () => {
|
|
1835
|
+
const affectedResources: Array<DockerSwarmAffectedResource> = [];
|
|
1836
|
+
for (let i: number = 1; i <= 13; i++) {
|
|
1837
|
+
affectedResources.push({
|
|
1838
|
+
containerName: `web.${i}.task`,
|
|
1839
|
+
serviceName: "web",
|
|
1840
|
+
metricValue: i,
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
|
|
1844
|
+
const context: string | null = swarmContext({ affectedResources });
|
|
1845
|
+
|
|
1846
|
+
expect(context).toContain("**Affected Tasks** (13 total)");
|
|
1847
|
+
expect(context).toContain("*... and 3 more affected tasks*");
|
|
1848
|
+
expect(context).toContain("10. **Task** `web.4.task` — **4**");
|
|
1849
|
+
});
|
|
1850
|
+
|
|
1851
|
+
test("a row with no identity inside an attributed breakdown is titled by the cluster", () => {
|
|
1852
|
+
const context: string | null = swarmContext({
|
|
1853
|
+
affectedResources: [
|
|
1854
|
+
{ containerName: "web.1", serviceName: "web", metricValue: 2 },
|
|
1855
|
+
{ metricValue: 1 },
|
|
1856
|
+
],
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
expect(context).toContain(
|
|
1860
|
+
[
|
|
1861
|
+
"1. **Task** `web.1` — **2**",
|
|
1862
|
+
" - Service: `web`",
|
|
1863
|
+
"2. **Cluster** `swarm-prod` — **1**",
|
|
1864
|
+
].join("\n"),
|
|
1865
|
+
);
|
|
1866
|
+
});
|
|
1867
|
+
|
|
1868
|
+
test("an identity-less breakdown renders no list and falls back to the metric summary", () => {
|
|
1869
|
+
const context: string | null = swarmContext({
|
|
1870
|
+
affectedResources: [{ metricValue: 3 }],
|
|
1871
|
+
metricResult: [{ data: [{}, {}] }],
|
|
1872
|
+
});
|
|
1873
|
+
|
|
1874
|
+
expect(context).not.toContain("**Affected Tasks**");
|
|
1875
|
+
expect(context).toContain("**Metric Summary**");
|
|
1876
|
+
});
|
|
1877
|
+
});
|
|
1878
|
+
|
|
1879
|
+
describe("MonitorCriteriaEvaluator - per-platform resource kinds in the list", () => {
|
|
1880
|
+
function proxmoxContext(
|
|
1881
|
+
affectedResources: Array<ProxmoxAffectedResource>,
|
|
1882
|
+
): string | null {
|
|
1883
|
+
return Evaluator.buildProxmoxRootCauseContext({
|
|
1884
|
+
dataToProcess: metricResponse({
|
|
1885
|
+
proxmoxResourceBreakdown: {
|
|
1886
|
+
clusterName: "prod-cluster",
|
|
1887
|
+
metricName: "pve_replication_failed_syncs",
|
|
1888
|
+
metricFriendlyName: "Failed Replication Syncs",
|
|
1889
|
+
affectedResources,
|
|
1890
|
+
attributes: {},
|
|
1891
|
+
},
|
|
1892
|
+
}),
|
|
1893
|
+
monitorStep: proxmoxStep(),
|
|
1894
|
+
monitor: new Monitor(),
|
|
1895
|
+
});
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
test("Proxmox: each pve.type gets a readable kind, and a node does not list itself as its node", () => {
|
|
1899
|
+
const context: string | null = proxmoxContext([
|
|
1900
|
+
{
|
|
1901
|
+
resourceId: "lxc/200",
|
|
1902
|
+
resourceName: "cache",
|
|
1903
|
+
resourceType: "lxc",
|
|
1904
|
+
scope: "guest",
|
|
1905
|
+
nodeName: "pve1",
|
|
1906
|
+
metricValue: 5,
|
|
1907
|
+
},
|
|
1908
|
+
{
|
|
1909
|
+
resourceId: "node/pve1",
|
|
1910
|
+
resourceName: "pve1",
|
|
1911
|
+
resourceType: "node",
|
|
1912
|
+
scope: "node",
|
|
1913
|
+
nodeName: "pve1",
|
|
1914
|
+
metricValue: 4,
|
|
1915
|
+
},
|
|
1916
|
+
{
|
|
1917
|
+
resourceId: "storage/pve1/local-lvm",
|
|
1918
|
+
resourceName: "local-lvm",
|
|
1919
|
+
resourceType: "storage",
|
|
1920
|
+
scope: "storage",
|
|
1921
|
+
nodeName: "pve1",
|
|
1922
|
+
metricValue: 3,
|
|
1923
|
+
},
|
|
1924
|
+
]);
|
|
1925
|
+
|
|
1926
|
+
expect(context).toContain(
|
|
1927
|
+
[
|
|
1928
|
+
"1. **Container** `cache` (`lxc/200`) — **5**",
|
|
1929
|
+
" - Node: `pve1`",
|
|
1930
|
+
"2. **Node** `pve1` (`node/pve1`) — **4**",
|
|
1931
|
+
"3. **Storage** `local-lvm` (`storage/pve1/local-lvm`) — **3**",
|
|
1932
|
+
" - Node: `pve1`",
|
|
1933
|
+
].join("\n"),
|
|
1934
|
+
);
|
|
1935
|
+
});
|
|
1936
|
+
|
|
1937
|
+
test("Proxmox: an unknown pve.type is shown as it arrived, and scope fills in when type is missing", () => {
|
|
1938
|
+
const context: string | null = proxmoxContext([
|
|
1939
|
+
{ resourceId: "sdn/zone1", resourceType: "sdn", metricValue: 2 },
|
|
1940
|
+
{ resourceId: "qemu/300", scope: "guest", metricValue: 1 },
|
|
1941
|
+
]);
|
|
1942
|
+
|
|
1943
|
+
expect(context).toContain("1. **sdn** `sdn/zone1` — **2**");
|
|
1944
|
+
expect(context).toContain("2. **Guest** `qemu/300` — **1**");
|
|
1945
|
+
});
|
|
1946
|
+
|
|
1947
|
+
/*
|
|
1948
|
+
* The agent's transform/pve-identity processor sets pve.scope=cluster
|
|
1949
|
+
* and leaves pve.type unset on `cluster/*` series, so this is the one
|
|
1950
|
+
* scope-only input that really arrives.
|
|
1951
|
+
*/
|
|
1952
|
+
test("Proxmox: cluster/* series (scope only) are titled Cluster; no type or scope falls back to Resource", () => {
|
|
1953
|
+
const context: string | null = proxmoxContext([
|
|
1954
|
+
{ resourceId: "cluster/prod", scope: "cluster", metricValue: 4 },
|
|
1955
|
+
{ resourceId: "node/pve3", scope: "node", metricValue: 3 },
|
|
1956
|
+
{ resourceId: "pool/backups", scope: "pool", metricValue: 2 },
|
|
1957
|
+
{ resourceId: "qemu/100", metricValue: 1 },
|
|
1958
|
+
]);
|
|
1959
|
+
|
|
1960
|
+
expect(context).toContain(
|
|
1961
|
+
[
|
|
1962
|
+
"1. **Cluster** `cluster/prod` — **4**",
|
|
1963
|
+
"2. **Node** `node/pve3` — **3**",
|
|
1964
|
+
"3. **pool** `pool/backups` — **2**",
|
|
1965
|
+
"4. **Resource** `qemu/100` — **1**",
|
|
1966
|
+
].join("\n"),
|
|
1967
|
+
);
|
|
1968
|
+
});
|
|
1969
|
+
|
|
1970
|
+
test("Proxmox: a node-only row is titled by the node; a row with no identity by the cluster", () => {
|
|
1971
|
+
const context: string | null = proxmoxContext([
|
|
1972
|
+
{ nodeName: "pve2", metricValue: 2 },
|
|
1973
|
+
{ metricValue: 1 },
|
|
1974
|
+
]);
|
|
1975
|
+
|
|
1976
|
+
expect(context).toContain(
|
|
1977
|
+
"1. **Node** `pve2` — **2**\n2. **Cluster** `prod-cluster` — **1**",
|
|
1978
|
+
);
|
|
1979
|
+
});
|
|
1980
|
+
|
|
1981
|
+
test("VMware: a VM with no display name is named by its instance UUID", () => {
|
|
1982
|
+
const context: string | null = Evaluator.buildVMwareRootCauseContext({
|
|
1983
|
+
dataToProcess: metricResponse({
|
|
1984
|
+
vmwareResourceBreakdown: {
|
|
1985
|
+
vcenterName: "vcsa-prod",
|
|
1986
|
+
metricName: "vcenter.vm.disk.usage",
|
|
1987
|
+
metricFriendlyName: "VM Disk Usage",
|
|
1988
|
+
affectedResources: [
|
|
1989
|
+
{
|
|
1990
|
+
datacenterName: "DC1",
|
|
1991
|
+
hostName: "esx-01",
|
|
1992
|
+
vmId: "5029abcd-1111-2222-3333-444455556666",
|
|
1993
|
+
metricValue: 1073741824,
|
|
1994
|
+
},
|
|
1995
|
+
],
|
|
1996
|
+
attributes: {},
|
|
1997
|
+
},
|
|
1998
|
+
}),
|
|
1999
|
+
monitorStep: vmwareStep(),
|
|
2000
|
+
monitor: new Monitor(),
|
|
2001
|
+
});
|
|
2002
|
+
|
|
2003
|
+
expect(context).toContain(
|
|
2004
|
+
"1. **Virtual Machine** `5029abcd-1111-2222-3333-444455556666` — **1.07 GB**\n - Host: `esx-01`",
|
|
2005
|
+
);
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
test("VMware: a pool on a standalone host lists its owner host; a row with no identity is the vCenter", () => {
|
|
2009
|
+
const context: string | null = Evaluator.buildVMwareRootCauseContext({
|
|
2010
|
+
dataToProcess: metricResponse({
|
|
2011
|
+
vmwareResourceBreakdown: {
|
|
2012
|
+
vcenterName: "vcsa-prod",
|
|
2013
|
+
metricName: "vcenter.datacenter.vm.count",
|
|
2014
|
+
metricFriendlyName: "Datacenter VM Count",
|
|
2015
|
+
affectedResources: [
|
|
2016
|
+
{
|
|
2017
|
+
datacenterName: "DC1",
|
|
2018
|
+
hostName: "esx-standalone",
|
|
2019
|
+
resourcePoolName: "Resources",
|
|
2020
|
+
resourcePoolPath: "/DC1/host/esx-standalone/Resources",
|
|
2021
|
+
metricValue: 2,
|
|
2022
|
+
},
|
|
2023
|
+
{ metricValue: 1 },
|
|
2024
|
+
],
|
|
2025
|
+
attributes: {},
|
|
2026
|
+
},
|
|
2027
|
+
}),
|
|
2028
|
+
monitorStep: vmwareStep(),
|
|
2029
|
+
monitor: new Monitor(),
|
|
2030
|
+
});
|
|
2031
|
+
|
|
2032
|
+
expect(context).toContain(
|
|
2033
|
+
[
|
|
2034
|
+
"1. **Resource Pool** `Resources` (`/DC1/host/esx-standalone/Resources`) — **2**",
|
|
2035
|
+
" - Host: `esx-standalone`",
|
|
2036
|
+
"2. **vCenter** `vcsa-prod` — **1**",
|
|
2037
|
+
].join("\n"),
|
|
2038
|
+
);
|
|
2039
|
+
});
|
|
2040
|
+
|
|
2041
|
+
/*
|
|
2042
|
+
* The worker stores every Ceph series' `name` label as poolName. On
|
|
2043
|
+
* ceph_health_detail that label is the health check — RECENT_CRASH,
|
|
2044
|
+
* OSD_NEARFULL — not a pool, and there is no pool_id beside it.
|
|
2045
|
+
*/
|
|
2046
|
+
test("Ceph: a ceph_health_detail series is titled as a health check, not a pool", () => {
|
|
2047
|
+
const template: CephAlertTemplate | undefined =
|
|
2048
|
+
getCephAlertTemplateById("ceph-daemon-crash");
|
|
2049
|
+
if (!template) {
|
|
2050
|
+
throw new Error("ceph-daemon-crash template missing");
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
const step: MonitorStep = template.getMonitorStep(templateArgs());
|
|
2054
|
+
|
|
2055
|
+
const context: string | null = Evaluator.buildCephRootCauseContext({
|
|
2056
|
+
dataToProcess: metricResponse({
|
|
2057
|
+
cephResourceBreakdown: {
|
|
2058
|
+
clusterName: "prod-cluster",
|
|
2059
|
+
metricName: "ceph_health_detail",
|
|
2060
|
+
metricFriendlyName: "Health Detail",
|
|
2061
|
+
affectedResources: [{ poolName: "RECENT_CRASH", metricValue: 1 }],
|
|
2062
|
+
attributes: {},
|
|
2063
|
+
},
|
|
2064
|
+
}),
|
|
2065
|
+
monitorStep: step,
|
|
2066
|
+
monitor: new Monitor(),
|
|
2067
|
+
criteriaInstance:
|
|
2068
|
+
step.data!.monitorCriteria!.data!.monitorCriteriaInstanceArray[0],
|
|
2069
|
+
});
|
|
2070
|
+
|
|
2071
|
+
expect(context).toContain(
|
|
2072
|
+
"**Affected Resources** (1 total)\n\n1. **Health Check** `RECENT_CRASH` — **1**",
|
|
2073
|
+
);
|
|
2074
|
+
expect(context).not.toContain("**Pool**");
|
|
2075
|
+
expect(context).not.toContain("- Pool:");
|
|
2076
|
+
});
|
|
2077
|
+
|
|
2078
|
+
test("Ceph: a name without a pool id on another metric is a generic resource, and a daemon keeps it as a detail", () => {
|
|
2079
|
+
const context: string | null = Evaluator.buildCephRootCauseContext({
|
|
2080
|
+
dataToProcess: metricResponse({
|
|
2081
|
+
cephResourceBreakdown: {
|
|
2082
|
+
clusterName: "prod-cluster",
|
|
2083
|
+
metricName: "ceph_fs_metadata",
|
|
2084
|
+
metricFriendlyName: "Filesystem Metadata",
|
|
2085
|
+
affectedResources: [
|
|
2086
|
+
{ poolName: "cephfs", metricValue: 2 },
|
|
2087
|
+
{
|
|
2088
|
+
daemon: "mds.a",
|
|
2089
|
+
poolName: "cephfs",
|
|
2090
|
+
hostname: "ceph-node-1",
|
|
2091
|
+
metricValue: 1,
|
|
2092
|
+
},
|
|
2093
|
+
],
|
|
2094
|
+
attributes: {},
|
|
2095
|
+
},
|
|
2096
|
+
}),
|
|
2097
|
+
monitorStep: cephStep(),
|
|
2098
|
+
monitor: new Monitor(),
|
|
2099
|
+
});
|
|
2100
|
+
|
|
2101
|
+
expect(context).toContain(
|
|
2102
|
+
[
|
|
2103
|
+
"1. **Resource** `cephfs` — **2**",
|
|
2104
|
+
"2. **Daemon** `mds.a` — **1**",
|
|
2105
|
+
" - Name: `cephfs`",
|
|
2106
|
+
" - Host: `ceph-node-1`",
|
|
2107
|
+
].join("\n"),
|
|
2108
|
+
);
|
|
2109
|
+
expect(context).not.toContain("**Pool**");
|
|
2110
|
+
});
|
|
2111
|
+
|
|
2112
|
+
test("Ceph: a pool id alone still makes a pool", () => {
|
|
2113
|
+
const context: string | null = Evaluator.buildCephRootCauseContext({
|
|
2114
|
+
dataToProcess: metricResponse({
|
|
2115
|
+
cephResourceBreakdown: {
|
|
2116
|
+
clusterName: "prod-cluster",
|
|
2117
|
+
metricName: "ceph_pool_stored",
|
|
2118
|
+
metricFriendlyName: "Pool Stored",
|
|
2119
|
+
affectedResources: [{ poolId: "7", metricValue: 3 }],
|
|
2120
|
+
attributes: {},
|
|
2121
|
+
},
|
|
2122
|
+
}),
|
|
2123
|
+
monitorStep: cephStep(),
|
|
2124
|
+
monitor: new Monitor(),
|
|
2125
|
+
});
|
|
2126
|
+
|
|
2127
|
+
expect(context).toContain("1. **Pool** `7` — **3 B**");
|
|
2128
|
+
});
|
|
2129
|
+
|
|
2130
|
+
test("Ceph: a daemon lists its pool and host; a pool lists its host; a host and a cluster stand alone", () => {
|
|
2131
|
+
const context: string | null = Evaluator.buildCephRootCauseContext({
|
|
2132
|
+
dataToProcess: metricResponse({
|
|
2133
|
+
cephResourceBreakdown: {
|
|
2134
|
+
clusterName: "prod-cluster",
|
|
2135
|
+
metricName: "ceph_osd_apply_latency_ms",
|
|
2136
|
+
metricFriendlyName: "OSD Apply Latency",
|
|
2137
|
+
affectedResources: [
|
|
2138
|
+
{
|
|
2139
|
+
daemon: "osd.7",
|
|
2140
|
+
poolId: "2",
|
|
2141
|
+
poolName: "rbd",
|
|
2142
|
+
hostname: "ceph-node-1",
|
|
2143
|
+
metricValue: 400,
|
|
2144
|
+
},
|
|
2145
|
+
{
|
|
2146
|
+
poolId: "3",
|
|
2147
|
+
poolName: "cephfs_data",
|
|
2148
|
+
hostname: "ceph-node-2",
|
|
2149
|
+
metricValue: 300,
|
|
2150
|
+
},
|
|
2151
|
+
{ hostname: "ceph-node-3", metricValue: 200 },
|
|
2152
|
+
{ metricValue: 100 },
|
|
2153
|
+
],
|
|
2154
|
+
attributes: {},
|
|
2155
|
+
},
|
|
2156
|
+
}),
|
|
2157
|
+
monitorStep: cephStep(),
|
|
2158
|
+
monitor: new Monitor(),
|
|
2159
|
+
});
|
|
2160
|
+
|
|
2161
|
+
expect(context).toContain(
|
|
2162
|
+
[
|
|
2163
|
+
"1. **Daemon** `osd.7` — **400 ms**",
|
|
2164
|
+
" - Pool: `rbd` (`2`)",
|
|
2165
|
+
" - Host: `ceph-node-1`",
|
|
2166
|
+
"2. **Pool** `cephfs_data` (`3`) — **300 ms**",
|
|
2167
|
+
" - Host: `ceph-node-2`",
|
|
2168
|
+
"3. **Host** `ceph-node-3` — **200 ms**",
|
|
2169
|
+
"4. **Cluster** `prod-cluster` — **100 ms**",
|
|
2170
|
+
].join("\n"),
|
|
2171
|
+
);
|
|
2172
|
+
});
|
|
2173
|
+
});
|