@oneuptime/common 14.0.1 → 14.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/AlertEpisodeMember.ts +8 -1
- package/Models/DatabaseModels/AlertFeed.ts +2 -0
- package/Models/DatabaseModels/AutoRemediationSuggestion.ts +63 -0
- package/Models/DatabaseModels/IncidentAlert.ts +540 -0
- package/Models/DatabaseModels/IncidentEpisodeMember.ts +8 -1
- package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +9 -2
- package/Models/DatabaseModels/IncidentFeed.ts +2 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +9 -2
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +429 -0
- package/Models/DatabaseModels/Monitor.ts +48 -0
- package/Models/DatabaseModels/Project.ts +60 -0
- package/Models/DatabaseModels/RumApplication.ts +52 -1
- package/Models/DatabaseModels/Runner.ts +5 -4
- package/Models/DatabaseModels/RunnerJob.ts +66 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +10 -2
- package/Models/DatabaseModels/WorkflowVariable.ts +479 -2
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +79 -25
- package/Server/API/AIInvestigationAPI.ts +341 -0
- package/Server/API/AutoRemediationAPI.ts +184 -2
- package/Server/API/CommonAPI.ts +70 -0
- package/Server/API/KubernetesClusterAiAccessAPI.ts +793 -0
- package/Server/API/MicrosoftTeamsAPI.ts +770 -315
- package/Server/API/MonitorAPI.ts +140 -0
- package/Server/API/OnCallScheduleTimelineAPI.ts +5 -21
- package/Server/API/SlackAPI.ts +447 -99
- package/Server/API/StatusPageAPI.ts +146 -26
- package/Server/API/TelemetryAPI.ts +216 -5
- package/Server/API/UserTwoFactorBackupCodeAPI.ts +1 -0
- package/Server/API/WorkflowVariableAPI.ts +156 -0
- package/Server/Infrastructure/GlobalCache.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.ts +97 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.ts +129 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.ts +20 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.ts +38 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +28 -0
- package/Server/Middleware/TelemetryIngest.ts +47 -2
- package/Server/Middleware/UserAuthorization.ts +56 -2
- package/Server/Services/AIAgentService.ts +1 -0
- package/Server/Services/AccessTokenService.ts +121 -48
- package/Server/Services/AlertEpisodeMemberService.ts +9 -0
- package/Server/Services/AlertEpisodeService.ts +14 -1
- package/Server/Services/AlertService.ts +134 -22
- package/Server/Services/AutoRemediationRuleEngineService.ts +968 -39
- package/Server/Services/AutoRemediationRuleService.ts +118 -0
- package/Server/Services/BillingService.ts +1 -0
- package/Server/Services/InboundEmail/Providers/SendGridInboundProvider.ts +16 -22
- package/Server/Services/IncidentAlertService.ts +1811 -0
- package/Server/Services/IncidentEpisodeMemberService.ts +9 -0
- package/Server/Services/IncidentEpisodePublicNoteService.ts +66 -0
- package/Server/Services/IncidentEpisodeRoleMemberService.ts +27 -0
- package/Server/Services/IncidentEpisodeService.ts +14 -1
- package/Server/Services/IncidentGroupingEngineService.ts +11 -0
- package/Server/Services/IncidentMemberService.ts +21 -0
- package/Server/Services/IncidentPublicNoteService.ts +63 -0
- package/Server/Services/IncidentService.ts +330 -30
- package/Server/Services/IncidentStateTimelineService.ts +39 -0
- package/Server/Services/IncidentTemplateService.ts +83 -3
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/KubernetesClusterAiAccessService.ts +1943 -0
- package/Server/Services/KubernetesClusterService.ts +1570 -1
- package/Server/Services/LogAggregationService.ts +14 -6
- package/Server/Services/MonitorService.ts +651 -52
- package/Server/Services/MonitorStatusTimelineService.ts +639 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -0
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +3 -0
- package/Server/Services/OnCallNotificationAlertingService.ts +36 -12
- package/Server/Services/OnCallSetupReminderService.ts +1 -0
- package/Server/Services/ProbeService.ts +7 -1
- package/Server/Services/ProjectService.ts +11 -1
- package/Server/Services/RunbookCredentialService.ts +26 -1
- package/Server/Services/RunbookSecretService.ts +36 -0
- package/Server/Services/RunnerJobService.ts +941 -23
- package/Server/Services/RunnerService.ts +439 -6
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +66 -0
- package/Server/Services/ScheduledMaintenanceService.ts +124 -19
- package/Server/Services/ScheduledMaintenanceTemplateService.ts +82 -3
- package/Server/Services/ServiceLevelObjectiveService.ts +14 -1
- package/Server/Services/StatusPagePrivateUserService.ts +1 -0
- package/Server/Services/StatusPageService.ts +301 -74
- package/Server/Services/StatusPageSubscriberService.ts +4 -0
- package/Server/Services/TeamMemberService.ts +309 -7
- package/Server/Services/TraceAggregationService.ts +10 -6
- package/Server/Services/UserEmailService.ts +1 -0
- package/Server/Services/UserNotificationMethodAdminService.ts +1 -0
- package/Server/Services/UserNotificationRuleAdminService.ts +1 -0
- package/Server/Services/UserNotificationRuleService.ts +4 -0
- package/Server/Services/UserService.ts +122 -1
- package/Server/Services/WorkflowVariableService.ts +593 -8
- package/Server/Services/WorkspaceNotificationRuleService.ts +588 -8
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +170 -1
- package/Server/Types/Database/QueryHelper.ts +22 -0
- package/Server/Types/Markdown.ts +502 -27
- package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +182 -0
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +525 -0
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +854 -0
- package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +22 -2
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1916 -307
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1601 -74
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +139 -7
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +58 -1
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +54 -1
- package/Server/Utils/AI/Toolbox/AIActionTools.ts +14 -6
- package/Server/Utils/AI/Toolbox/ResourceTools.ts +4 -2
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +1341 -150
- package/Server/Utils/AutoRemediation/RemediationVerifier.ts +375 -4
- package/Server/Utils/Database/AffectedResourceRelations.ts +127 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +240 -1
- package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +1 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +5 -1
- package/Server/Utils/Monitor/MonitorIncident.ts +104 -9
- package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +124 -7
- package/Server/Utils/Monitor/ProbeMonitorsNotification.ts +407 -0
- package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +1 -0
- package/Server/Utils/PushNotificationUtil.ts +45 -0
- package/Server/Utils/Rules/OwnerRuleAssignment.ts +40 -3
- package/Server/Utils/Runbook/RunbookExecutePermission.ts +56 -6
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +24 -2
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +223 -33
- package/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.ts +559 -0
- package/Server/Utils/StartServer.ts +4 -3
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +528 -0
- package/Server/Utils/TwoFactorBackupCodeNotification.ts +1 -0
- package/Server/Utils/UserPermission/UserPermission.ts +16 -0
- package/Server/Utils/Workflow/OAuth2TokenClient.ts +704 -0
- package/Server/Utils/Workflow/WorkflowVariableOAuthToken.ts +318 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +47 -7
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +57 -16
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +193 -214
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +57 -13
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +54 -17
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +179 -161
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +281 -167
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.ts +114 -0
- package/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.ts +895 -0
- package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +7 -9
- package/Server/Utils/Workspace/Slack/Actions/Alert.ts +96 -227
- package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +93 -214
- package/Server/Utils/Workspace/Slack/Actions/Auth.ts +30 -3
- package/Server/Utils/Workspace/Slack/Actions/Authorization.ts +99 -0
- package/Server/Utils/Workspace/Slack/Actions/Incident.ts +117 -283
- package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +101 -196
- package/Server/Utils/Workspace/Slack/Actions/ReactionNote.ts +329 -0
- package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +111 -287
- package/Server/Utils/Workspace/Slack/Slack.ts +86 -17
- package/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
- package/Server/Utils/Workspace/Workspace.ts +171 -54
- package/Server/Utils/Workspace/WorkspaceActionAuthorization.ts +252 -0
- package/Server/Utils/Workspace/WorkspaceBase.ts +39 -0
- package/Server/Utils/Workspace/WorkspaceOAuthState.ts +280 -0
- package/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.ts +96 -0
- package/Server/Utils/Workspace/WorkspaceReactionNote.ts +620 -0
- package/Tests/App/Dashboard/AttributesJSONDashboardSurfaces.test.tsx +496 -0
- package/Tests/App/Dashboard/AutoRemediationRulesTableCommandRunners.test.tsx +152 -0
- package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +32 -0
- package/Tests/App/Dashboard/BulkIncidentLinkActions.test.tsx +869 -0
- package/Tests/App/Dashboard/CephMetricTooltips.test.tsx +1777 -0
- package/Tests/App/Dashboard/CloudFleetSummary.test.ts +8 -0
- package/Tests/App/Dashboard/CloudMetricTooltips.test.tsx +887 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +794 -0
- package/Tests/App/Dashboard/ContainerHostMetricTooltips.test.tsx +1309 -0
- package/Tests/App/Dashboard/CreateOAuthWorkflowVariableModal.test.tsx +840 -0
- package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +653 -0
- package/Tests/App/Dashboard/DatabaseMonitorStepForm.test.tsx +197 -0
- package/Tests/App/Dashboard/DatabaseMonitorView.test.tsx +376 -0
- package/Tests/App/Dashboard/DisabledWarningRobustness.test.tsx +262 -0
- package/Tests/App/Dashboard/DiscoveryScanLivePage.test.tsx +39 -0
- package/Tests/App/Dashboard/DockerSwarmClustersListTooltips.test.tsx +414 -0
- package/Tests/App/Dashboard/DockerSwarmMetricTooltips.test.ts +796 -0
- package/Tests/App/Dashboard/DockerSwarmOverviewTooltips.test.tsx +747 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +2080 -0
- package/Tests/App/Dashboard/EventNotesPages.test.tsx +486 -0
- package/Tests/App/Dashboard/EventNotesUtil.test.ts +933 -0
- package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +238 -0
- package/Tests/App/Dashboard/HostDetailViewTooltips.test.tsx +725 -0
- package/Tests/App/Dashboard/HostListColumnTooltips.test.tsx +472 -0
- package/Tests/App/Dashboard/HostMetricTooltips.test.ts +709 -0
- package/Tests/App/Dashboard/HostOverviewTooltips.test.tsx +698 -0
- package/Tests/App/Dashboard/HostTooltipHarness.ts +124 -0
- package/Tests/App/Dashboard/HostsListTooltips.test.tsx +395 -0
- package/Tests/App/Dashboard/IncidentAlertLinkDialog.test.tsx +691 -0
- package/Tests/App/Dashboard/IncidentAlertLinkHelpers.test.ts +458 -0
- package/Tests/App/Dashboard/IncidentAlertLinkPages.test.tsx +828 -0
- package/Tests/App/Dashboard/IncidentAlertLinkSideMenus.test.tsx +231 -0
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +665 -0
- package/Tests/App/Dashboard/IncidentCreateFromAlerts.test.tsx +689 -0
- package/Tests/App/Dashboard/IncidentEpisodeFeedPublicNoteNotifyDefault.test.tsx +494 -0
- package/Tests/App/Dashboard/IncidentEpisodeOverviewNotifyDefault.test.tsx +706 -0
- package/Tests/App/Dashboard/IncidentFeedPublicNoteNotifyDefault.test.tsx +472 -0
- package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +858 -0
- package/Tests/App/Dashboard/IncomingEmailAddressSettings.test.tsx +567 -0
- package/Tests/App/Dashboard/InvestigationClusterToolFormat.test.ts +241 -0
- package/Tests/App/Dashboard/InvestigationEvidenceKubectl.test.tsx +322 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +12 -0
- package/Tests/App/Dashboard/InvestigationPanelClusterAccess.test.tsx +476 -0
- package/Tests/App/Dashboard/InvestigationRunDetailsKubectl.test.tsx +383 -0
- package/Tests/App/Dashboard/IoTMetricTooltips.test.tsx +1123 -0
- package/Tests/App/Dashboard/KubectlActivitySummary.test.tsx +366 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +2886 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +2688 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +915 -0
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +837 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +699 -0
- package/Tests/App/Dashboard/KubernetesContainersTabTooltips.test.tsx +320 -0
- package/Tests/App/Dashboard/KubernetesCostTooltips.test.tsx +502 -0
- package/Tests/App/Dashboard/KubernetesInsightsTooltips.test.tsx +222 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricTooltips.test.ts +561 -0
- package/Tests/App/Dashboard/KubernetesResourcePagesTooltips.test.tsx +1168 -0
- package/Tests/App/Dashboard/LayerDateTimeFieldTimezoneName.test.tsx +66 -0
- package/Tests/App/Dashboard/LinkIncidentAlertModal.test.tsx +154 -0
- package/Tests/App/Dashboard/LogMonitorPreview.test.tsx +174 -0
- package/Tests/App/Dashboard/MetricDescriptionRules.ts +170 -0
- package/Tests/App/Dashboard/MetricDescriptionsCatalog.test.ts +206 -0
- package/Tests/App/Dashboard/MetricDescriptionsConsistency.test.ts +275 -0
- package/Tests/App/Dashboard/MonitorFeedRefresh.test.tsx +279 -0
- package/Tests/App/Dashboard/MonitorMetricQueryConfig.test.ts +244 -0
- package/Tests/App/Dashboard/MonitorOpenWorkCard.test.tsx +381 -0
- package/Tests/App/Dashboard/MonitorOpenWorkHook.test.tsx +784 -0
- package/Tests/App/Dashboard/MonitorOverviewData.test.tsx +2199 -0
- package/Tests/App/Dashboard/MonitorOverviewHero.test.tsx +1169 -0
- package/Tests/App/Dashboard/MonitorOverviewInput.test.ts +1127 -0
- package/Tests/App/Dashboard/MonitorOverviewPage.test.tsx +2136 -0
- package/Tests/App/Dashboard/MonitorOverviewSelectReadable.test.ts +368 -0
- package/Tests/App/Dashboard/MonitorOverviewStatBar.test.tsx +526 -0
- package/Tests/App/Dashboard/MonitorOverviewSupportingCards.test.tsx +542 -0
- package/Tests/App/Dashboard/MonitorOwnersCombine.test.ts +299 -0
- package/Tests/App/Dashboard/MonitorProbesCard.test.tsx +602 -0
- package/Tests/App/Dashboard/MonitorResponseTimeCard.test.tsx +386 -0
- package/Tests/App/Dashboard/MonitorSetupAndConnectionCards.test.tsx +612 -0
- package/Tests/App/Dashboard/MonitorStatusChangesCard.test.tsx +257 -0
- package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +360 -1
- package/Tests/App/Dashboard/MonitorUptimeHistoryCard.test.tsx +835 -0
- package/Tests/App/Dashboard/MonitorUptimeSummaryHook.test.tsx +971 -0
- package/Tests/App/Dashboard/MonitorViewLayout.test.tsx +580 -0
- package/Tests/App/Dashboard/NetworkDeviceTooltips.test.tsx +852 -0
- package/Tests/App/Dashboard/NetworkDiagnosticsTooltips.test.tsx +556 -0
- package/Tests/App/Dashboard/NetworkMetricTooltips.test.ts +1348 -0
- package/Tests/App/Dashboard/NetworkSiteTooltips.test.tsx +679 -0
- package/Tests/App/Dashboard/OnCallLayerRestrictionTimesPreserved.test.tsx +441 -0
- package/Tests/App/Dashboard/OnCallNavbar.test.tsx +214 -0
- package/Tests/App/Dashboard/OnCallSchedulePreviewCalendarDisplay.test.tsx +1389 -0
- package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +205 -1
- package/Tests/App/Dashboard/OnCallScheduleTimeInputsSettingsZone.test.tsx +198 -0
- package/Tests/App/Dashboard/ProxmoxMetricTooltips.test.ts +592 -0
- package/Tests/App/Dashboard/ProxmoxOverviewTooltips.test.tsx +1002 -0
- package/Tests/App/Dashboard/ProxmoxResourcePageTooltips.test.tsx +923 -0
- package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +524 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardApproval.test.tsx +702 -0
- package/Tests/App/Dashboard/RemoveUserFromProject.test.tsx +427 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +4 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +1066 -0
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +1195 -0
- package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +3 -1
- package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +166 -0
- package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +622 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceChangeStateNotifyDefault.test.tsx +1491 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceFeedPublicNoteNotifyDefault.test.tsx +489 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewNotifyDefault.test.tsx +909 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +14 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceWindowStatsTranslation.test.tsx +307 -0
- package/Tests/App/Dashboard/SecurityEventAttributeColumnPicker.test.tsx +542 -0
- package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +174 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +973 -212
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +491 -52
- package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +301 -0
- package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +158 -1
- package/Tests/App/Dashboard/SecurityEventProviderTile.test.tsx +869 -0
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +959 -94
- package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +2 -0
- package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +274 -1
- package/Tests/App/Dashboard/ServerlessMetricTooltips.test.tsx +474 -0
- package/Tests/App/Dashboard/ServiceMetricTooltips.test.tsx +772 -0
- package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +56 -1
- package/Tests/App/Dashboard/TeamMembershipRemoval.test.ts +900 -0
- package/Tests/App/Dashboard/TeamMembershipRemovalPages.test.tsx +460 -0
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +1207 -0
- package/Tests/App/Dashboard/TelemetryResourceTabTooltips.test.tsx +437 -0
- package/Tests/App/Dashboard/TimezoneElements.test.tsx +321 -0
- package/Tests/App/Dashboard/TimezoneSelectButton.test.tsx +634 -0
- package/Tests/App/Dashboard/TraceExplorer.test.tsx +260 -0
- package/Tests/App/Dashboard/TraceMonitorPreview.test.tsx +182 -0
- package/Tests/App/Dashboard/UserTimezoneInit.test.tsx +544 -0
- package/Tests/App/Dashboard/VMwareMetricTooltips.test.ts +801 -0
- package/Tests/App/Dashboard/VMwareOverviewTooltips.test.tsx +601 -0
- package/Tests/App/Dashboard/VMwareResourcePagesTooltips.test.tsx +646 -0
- package/Tests/App/Dashboard/VMwareTooltipHarness.ts +145 -0
- package/Tests/App/Dashboard/WorkflowVariableModals.test.tsx +1630 -0
- package/Tests/App/Dashboard/WorkflowVariableRoutes.test.tsx +603 -0
- package/Tests/App/Dashboard/WorkflowVariableTokenStatus.test.tsx +589 -0
- package/Tests/App/Dashboard/WorkflowVariableUtil.test.ts +1543 -0
- package/Tests/App/Dashboard/WorkflowVariableView.test.tsx +3227 -0
- package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +1277 -395
- package/Tests/App/StatusPage/StatusPageOverviewMonitorGroupUptime.test.tsx +714 -0
- package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.NewYork.test.tsx +1009 -0
- package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.test.tsx +1092 -0
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -0
- package/Tests/Models/DatabaseModels/IncidentAlertModel.test.ts +554 -0
- package/Tests/Models/DatabaseModels/KubernetesClusterAiAccessColumns.test.ts +520 -0
- package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +66 -2
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +117 -0
- package/Tests/Models/DatabaseModels/WorkspaceProjectAuthTokenAccessControl.test.ts +236 -0
- package/Tests/Models/IncidentEpisodePublicNoteNotifyColumnContract.test.ts +103 -0
- package/Tests/Models/IncidentPublicNoteNotifyColumnContract.test.ts +96 -0
- package/Tests/Models/ScheduledMaintenancePublicNoteNotifyColumnContract.test.ts +111 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +976 -0
- package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +836 -0
- package/Tests/Server/API/CommonAPIAssertCanCreateTable.test.ts +565 -0
- package/Tests/Server/API/CommonAPIAssertCanReadTable.test.ts +282 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +1744 -0
- package/Tests/Server/API/LogHistogramAPI.test.ts +371 -0
- package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +125 -30
- package/Tests/Server/API/MicrosoftTeamsOAuthState.test.ts +1121 -0
- package/Tests/Server/API/MonitorUptimeSummaryAPI.test.ts +685 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +602 -1
- package/Tests/Server/API/SlackChannelCacheAPI.test.ts +358 -0
- package/Tests/Server/API/SlackOAuthState.test.ts +688 -0
- package/Tests/Server/API/StatusPageOverviewMonitorGroupUptime.test.ts +527 -0
- package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +2 -0
- package/Tests/Server/API/StatusPageUptimeAPI.test.ts +427 -0
- package/Tests/Server/API/UserProjectsAPI.test.ts +4 -0
- package/Tests/Server/API/WorkflowVariableAPI.test.ts +404 -0
- package/Tests/Server/API/WorkspaceOAuthTestHelpers.ts +289 -0
- package/Tests/Server/API/WorkspaceSendTestNotificationRoutes.test.ts +2064 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +86 -0
- package/Tests/Server/Infrastructure/Postgres/AddIncidentAlertMigration.test.ts +341 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
- package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
- package/Tests/Server/Infrastructure/Postgres/AddSessionReplaySameOriginTracePropagationMigration.test.ts +247 -0
- package/Tests/Server/Middleware/BlockedUserMiddleware.test.ts +403 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +13 -0
- package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +2 -0
- package/Tests/Server/Middleware/RemovedProjectMemberAuthorization.test.ts +469 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +77 -1
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +211 -0
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +358 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +97 -0
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +2 -0
- package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +14 -2
- package/Tests/Server/Services/AccessTokenRemovedMemberAccess.test.ts +1044 -0
- package/Tests/Server/Services/AddOwnersIdempotency.test.ts +5 -0
- package/Tests/Server/Services/AlertEpisodeMemberCreatePermission.test.ts +139 -0
- package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +1361 -0
- package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +375 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +2384 -0
- package/Tests/Server/Services/FindOwnersProjectMembership.test.ts +267 -0
- package/Tests/Server/Services/InboundEmail/SendGridInboundProviderAddress.test.ts +90 -0
- package/Tests/Server/Services/IncidentAlertPostgres.test.ts +903 -0
- package/Tests/Server/Services/IncidentAlertRegistration.test.ts +96 -0
- package/Tests/Server/Services/IncidentAlertService.test.ts +2225 -0
- package/Tests/Server/Services/IncidentAlertStateCascade.test.ts +970 -0
- package/Tests/Server/Services/IncidentCreateFromAlerts.test.ts +1284 -0
- package/Tests/Server/Services/IncidentEpisodeMemberCreatePermission.test.ts +143 -0
- package/Tests/Server/Services/IncidentEpisodePublicNoteNotifyDefault.test.ts +805 -0
- package/Tests/Server/Services/IncidentPublicNoteNotifyDefault.test.ts +724 -0
- package/Tests/Server/Services/IncidentRoleProjectMembership.test.ts +233 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessAgentRunnerAssignments.test.ts +372 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessBindingGuard.test.ts +1876 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +620 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +421 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +3662 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessSubjectClusters.test.ts +405 -0
- package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +975 -0
- package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +497 -0
- package/Tests/Server/Services/KubernetesClusterServiceDeleteSettlesRemediation.test.ts +328 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +99 -0
- package/Tests/Server/Services/MonitorServiceIncomingEmailCustomAddress.test.ts +447 -0
- package/Tests/Server/Services/MonitorServiceProbeFlagSync.test.ts +1334 -0
- package/Tests/Server/Services/MonitorServiceProbeStatusGrouping.test.ts +1640 -0
- package/Tests/Server/Services/MonitorServiceRefreshCurrentStatus.test.ts +225 -0
- package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +8 -8
- package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +264 -1
- package/Tests/Server/Services/MonitorStatusTimelineMergedDowntime.test.ts +284 -0
- package/Tests/Server/Services/MonitorUptimeSummaryService.test.ts +1043 -0
- package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +5 -0
- package/Tests/Server/Services/OnCallUndeliverablePageOwnerMembership.test.ts +359 -0
- package/Tests/Server/Services/ProbeServiceDashboardLink.test.ts +191 -0
- package/Tests/Server/Services/ProjectOwnersProjectMembership.test.ts +221 -0
- package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +5 -0
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +9 -1
- package/Tests/Server/Services/RunnerJobEnqueueAiCommandAgentRunner.test.ts +230 -0
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +978 -0
- package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +866 -0
- package/Tests/Server/Services/RunnerJobSubmitResultRedaction.test.ts +322 -0
- package/Tests/Server/Services/RunnerJobSubmitResultUnstorableText.test.ts +357 -0
- package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +227 -0
- package/Tests/Server/Services/RunnerServiceAgentName.test.ts +581 -0
- package/Tests/Server/Services/ScheduledMaintenancePublicNoteNotifyDefault.test.ts +802 -0
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +53 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +5 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +12 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +1176 -1
- package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +9 -0
- package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +18 -0
- package/Tests/Server/Services/TeamMemberProjectMembership.test.ts +255 -0
- package/Tests/Server/Services/TeamMemberRemovalRevokesProjectAccess.test.ts +600 -0
- package/Tests/Server/Services/TeamMemberResourceCleanup.test.ts +731 -0
- package/Tests/Server/Services/TeamMemberWorkspaceAccountCleanup.test.ts +164 -0
- package/Tests/Server/Services/TraceAggregationService.test.ts +64 -0
- package/Tests/Server/Services/UserServiceBlockedUser.test.ts +293 -0
- package/Tests/Server/Services/WorkflowVariableOAuthHooks.test.ts +1016 -0
- package/Tests/Server/Services/WorkflowVariableOAuthPostgres.test.ts +543 -0
- package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +16 -5
- package/Tests/Server/Services/WorkspaceNotificationRuleNoteDelivery.test.ts +473 -0
- package/Tests/Server/Services/WorkspaceNotificationRuleSendTestNotification.test.ts +3690 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenMiscDataSecrets.test.ts +374 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenSlackChannelCache.test.ts +143 -0
- package/Tests/Server/Types/Database/Permissions/WorkflowVariableOAuthColumnPermission.test.ts +250 -0
- package/Tests/Server/Types/Database/QueryHelperJsonContains.test.ts +96 -0
- package/Tests/Server/Types/Markdown.test.ts +1033 -0
- package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +285 -0
- package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +159 -1
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +752 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +1840 -0
- package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +1067 -0
- package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +355 -0
- package/Tests/Server/Utils/AI/PostIncidentStatusUpdateTool.test.ts +496 -0
- package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +305 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +1365 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +639 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +1585 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +1065 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +2138 -1
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +1035 -0
- package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +517 -0
- package/Tests/Server/Utils/AI/ResourceTools.test.ts +27 -1
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1371 -24
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorRollbackRace.test.ts +962 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +518 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +939 -2
- package/Tests/Server/Utils/Database/ProjectScopedRelationReferences.test.ts +371 -0
- package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +64 -0
- package/Tests/Server/Utils/Monitor/DatabaseMetricObservationRemediation.test.ts +102 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +362 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +55 -0
- package/Tests/Server/Utils/Monitor/MonitorEmailAddressRedaction.test.ts +255 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +347 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +203 -0
- package/Tests/Server/Utils/Monitor/ProbeMonitorsNotification.test.ts +1147 -0
- package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +79 -0
- package/Tests/Server/Utils/ResourceFeed/ResourceFeedUtil.test.ts +21 -1
- package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +155 -1
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +103 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
- package/Tests/Server/Utils/Workflow/OAuth2TokenClient.test.ts +702 -0
- package/Tests/Server/Utils/Workflow/OAuth2TokenClientHttpIntegration.test.ts +507 -0
- package/Tests/Server/Utils/Workflow/WorkflowVariableOAuthToken.test.ts +831 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +63 -1
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsAppAccessToken.test.ts +256 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +60 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChatActionAuthorization.test.ts +442 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +44 -2
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +55 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsReactionNoteSync.test.ts +1522 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsServiceUrl.test.ts +130 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +4 -4
- package/Tests/Server/Utils/Workspace/Slack/SlackActionAuthorization.test.ts +691 -0
- package/Tests/Server/Utils/Workspace/Slack/SlackMessageByTimestamp.test.ts +253 -0
- package/Tests/Server/Utils/Workspace/Slack/SlackReactionNote.test.ts +574 -0
- package/Tests/Server/Utils/Workspace/WorkspaceActionAuthorization.test.ts +488 -0
- package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +861 -0
- package/Tests/Server/Utils/Workspace/WorkspaceMessageAuthor.test.ts +635 -0
- package/Tests/Server/Utils/Workspace/WorkspaceOAuthState.test.ts +548 -0
- package/Tests/Server/Utils/Workspace/WorkspaceReactionNote.test.ts +839 -0
- package/Tests/Server/Utils/Workspace/WorkspaceSendErrorMessage.test.ts +725 -0
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +283 -9
- package/Tests/Types/DateBrowserLocalWallClock.test.ts +280 -0
- package/Tests/Types/DateLocalShortMonthNameFallback.test.ts +389 -0
- package/Tests/Types/DateUserTimezone.test.ts +177 -4
- package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +306 -0
- package/Tests/Types/Kubernetes/KubernetesClusterAiAccess.test.ts +283 -0
- package/Tests/Types/Kubernetes/KubernetesRunnerPostureWriteScope.test.ts +68 -0
- package/Tests/Types/Monitor/DatabaseMonitor/SqlServerPlatform.test.ts +144 -0
- package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +160 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +53 -0
- package/Tests/Types/Rum/WebVitals.test.ts +173 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +91 -16
- package/Tests/Types/Runner/RunnerLiveStatus.test.ts +14 -3
- package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +15 -0
- package/Tests/Types/StatusPage/PublicNoteSubscriberNotificationDefault.test.ts +1868 -0
- package/Tests/Types/Timezone.test.ts +5 -0
- package/Tests/Types/TimezoneAlias.test.ts +1034 -0
- package/Tests/Types/Workflow/JiraTemplateFixtures.ts +833 -0
- package/Tests/Types/Workflow/JiraTemplateScripts.test.ts +5414 -0
- package/Tests/Types/Workflow/JiraTemplateSimulation.test.ts +4803 -0
- package/Tests/Types/Workflow/JiraTemplates.test.ts +5429 -0
- package/Tests/Types/Workflow/Templates.test.ts +79 -4
- package/Tests/Types/Workflow/WorkflowVariableOAuth.test.ts +520 -0
- package/Tests/Types/Workspace/WorkspaceNoteReaction.test.ts +177 -0
- package/Tests/UI/Components/AttributesJSON/AttributesJSONView.test.tsx +390 -0
- package/Tests/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.test.tsx +566 -0
- package/Tests/UI/Components/AttributesJSON/JSONHighlight.test.ts +131 -0
- package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +276 -1
- package/Tests/UI/Components/Calendar/Calendar.test.tsx +1611 -0
- package/Tests/UI/Components/Calendar/CalendarDayLayout.test.ts +1486 -0
- package/Tests/UI/Components/Calendar/CalendarDayLayoutDst.test.ts +632 -0
- package/Tests/UI/Components/Calendar/CalendarDst.test.tsx +388 -0
- package/Tests/UI/Components/Charts/ChartGroupPlotFloor.test.tsx +170 -0
- package/Tests/UI/Components/Charts/HistogramSelection.test.ts +282 -0
- package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +6 -0
- package/Tests/UI/Components/CustomTimeRangeModalTimezoneTranslation.test.tsx +285 -0
- package/Tests/UI/Components/Dropdown.test.tsx +999 -2
- package/Tests/UI/Components/FieldLabelInfoCardTooltip.test.tsx +413 -0
- package/Tests/UI/Components/Forms/FormFieldCheckboxTranslation.test.tsx +216 -0
- package/Tests/UI/Components/Forms/FormFieldTimezoneDescriptionTranslation.test.tsx +461 -0
- package/Tests/UI/Components/Forms/IncidentEpisodePublicNoteNotifyPayload.test.tsx +641 -0
- package/Tests/UI/Components/Forms/IncidentPublicNoteNotifyPayload.test.tsx +346 -0
- package/Tests/UI/Components/Forms/ScheduledMaintenancePublicNoteNotifyPayload.test.tsx +493 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +911 -2
- package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.NewYork.test.tsx +730 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.Tokyo.test.tsx +506 -0
- package/Tests/UI/Components/HistogramClickToZoom.test.tsx +619 -0
- package/Tests/UI/Components/InfoTooltip.test.tsx +242 -0
- package/Tests/UI/Components/LogDetailsPanelAttributesJSON.test.tsx +248 -0
- package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +47 -1
- package/Tests/UI/Components/LogsViewerSmallScreens.test.tsx +620 -0
- package/Tests/UI/Components/ModelPageModelChange.test.tsx +242 -3
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +207 -3
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDeleteVerb.test.tsx +367 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableCreateButtonTitle.test.tsx +275 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableDeleteConfirmation.test.tsx +521 -0
- package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +406 -2
- package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +169 -2
- package/Tests/UI/Components/TableHeaderTooltip.test.tsx +662 -0
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +266 -0
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerSmallScreens.test.tsx +439 -0
- package/Tests/UI/Components/TimezoneDropdownLegacyValues.test.tsx +704 -0
- package/Tests/UI/Components/UseHistogramZoom.test.tsx +89 -1
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +175 -0
- package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +5 -5
- package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +166 -9
- package/Tests/UI/Rum/RecordingHealthTooltips.test.tsx +447 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +164 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +6750 -0
- package/Tests/UI/Rum/ReplayRail.test.tsx +723 -12
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +86 -0
- package/Tests/UI/Rum/ReplayScreenshot.test.ts +2124 -0
- package/Tests/UI/Rum/ReplayScreenshotActions.test.tsx +3288 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +62 -0
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +983 -2
- package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +149 -0
- package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +107 -6
- package/Tests/UI/Rum/SessionReplayUsersTableTooltips.test.tsx +330 -0
- package/Tests/UI/Rum/UserFlowTooltips.test.tsx +886 -0
- package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +28 -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/PermissionGate.test.ts +76 -0
- package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +51 -0
- package/Tests/UI/Utils/Timezone.test.ts +747 -33
- package/Tests/UI/Workspace/SendTestNotificationButton.test.tsx +1854 -0
- package/Tests/UI/Workspace/WorkspaceCardsSendTestNotification.test.tsx +1864 -0
- package/Tests/Utils/AI/InvestigationReport.test.ts +435 -7
- package/Tests/Utils/AiRemediation/KubectlAllowlistValidatorParity.test.ts +1084 -0
- package/Tests/Utils/AiRemediation/KubectlOutputRedactor.test.ts +2531 -0
- package/Tests/Utils/AiRemediation/KubectlPolicy.test.ts +1895 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyAllowlist.test.ts +428 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +828 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyCustomResourceScope.test.ts +573 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyDenials.test.ts +1525 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyExposeOverrides.test.ts +275 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyNodePatch.test.ts +428 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyObjectWords.test.ts +195 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyPatchParents.test.ts +637 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyPodSecurityBaseline.test.ts +322 -0
- package/Tests/Utils/AiRemediation/KubectlPolicySafeWriteScope.test.ts +1026 -0
- package/Tests/Utils/AiRemediation/KubectlWaitBudget.test.ts +267 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScope.test.ts +390 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +777 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeClusterScoped.test.ts +613 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeEntryPoint.test.ts +576 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeKubectlSpellings.test.ts +752 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeObjectWords.test.ts +358 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeOverrides.test.ts +293 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeParityCases.ts +598 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopePolicyParity.test.ts +403 -0
- package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +154 -5
- package/Tests/Utils/Incident/IncidentFromAlerts.test.ts +696 -0
- package/Tests/Utils/Monitor/IncomingEmailMonitorAddress.test.ts +478 -0
- package/Tests/Utils/Monitor/MonitorCheckScheduleUtil.test.ts +646 -0
- package/Tests/Utils/Monitor/MonitorOverviewCriteriaUtil.test.ts +292 -0
- package/Tests/Utils/Monitor/MonitorOverviewFamily.test.ts +274 -0
- package/Tests/Utils/Monitor/MonitorOverviewPresentationUtil.test.ts +3100 -0
- package/Tests/Utils/Monitor/MonitorOverviewProbeUtil.test.ts +1412 -0
- package/Tests/Utils/Monitor/MonitorOverviewTargetUtil.test.ts +374 -0
- package/Tests/Utils/Monitor/MonitorStatusHistoryUtil.test.ts +332 -0
- package/Tests/Utils/Monitor/MonitorUptimeSummaryUtil.test.ts +946 -0
- package/Tests/Utils/Rum/SessionIdentity.test.ts +94 -0
- package/Tests/Utils/Rum/SessionReplayWafCompatibility.test.ts +840 -0
- package/Tests/Utils/Rum/SessionReplayWireEncoding.test.ts +208 -0
- package/Tests/Utils/Rum/SessionTraceState.test.ts +239 -0
- package/Tests/Utils/Rum/UserFlow.test.ts +1010 -0
- package/Tests/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.test.ts +217 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +1272 -1
- package/Tests/Utils/StatusPage/UptimeDailyAggregateTotals.test.ts +298 -0
- package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +649 -1
- package/Tests/Utils/StatusPage/UptimeDailyAggregateWindow.test.ts +314 -0
- package/Tests/Utils/Telemetry/AttributesJSON.test.ts +454 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +418 -2
- package/Tests/Utils/Uptime/DayUptimeGraphUtilMidnightDst.test.ts +160 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Auckland.test.ts +45 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Kolkata.test.ts +40 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.London.test.ts +147 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.NewYork.test.ts +48 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Tokyo.test.ts +39 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezoneCases.ts +1015 -0
- package/Tests/Utils/Uptime/UptimeUtil.test.ts +75 -0
- package/Tests/Utils/Uptime/UptimeUtilChronologicalOrder.test.ts +1279 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +203 -5
- package/Types/Date.ts +121 -7
- package/Types/Email/EmailMessage.ts +8 -0
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/Types/Exception/ExceptionMessages.ts +1 -0
- package/Types/Incident/IncidentAlertLink.ts +38 -0
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +488 -0
- package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +54 -0
- package/Types/Kubernetes/KubernetesClusterAiAccessToolNames.ts +17 -0
- package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +9 -0
- package/Types/Monitor/DatabaseMonitor/SqlServerPlatform.ts +117 -0
- package/Types/Monitor/KubernetesAlertTemplates.ts +6 -5
- package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
- package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
- package/Types/Permission.ts +48 -0
- package/Types/Rum/SessionReplay.ts +80 -8
- package/Types/Rum/SessionReplayApi.ts +44 -1
- package/Types/Rum/UserFlow.ts +187 -0
- package/Types/Rum/WebVitals.ts +52 -0
- package/Types/Runbook/RunbookStepType.ts +10 -0
- package/Types/Runbook/RunnerJobOrigin.ts +15 -0
- package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +6 -2
- package/Types/StatusPage/MergedDowntimeTotals.ts +17 -0
- package/Types/StatusPage/PublicNoteSubscriberNotificationDefault.ts +148 -0
- package/Types/StatusPage/UptimeDailyAggregate.ts +15 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +9 -0
- package/Types/Timezone.ts +5 -0
- package/Types/TimezoneAlias.ts +385 -0
- package/Types/Workflow/Templates.ts +3041 -2
- package/Types/Workflow/WorkflowVariableOAuth.ts +476 -0
- package/Types/Workspace/WorkspaceNoteReaction.ts +132 -0
- package/Typings/Index.d.ts +24 -0
- package/UI/Components/AttributesJSON/AttributesJSONPreferences.ts +175 -0
- package/UI/Components/AttributesJSON/AttributesJSONView.tsx +138 -0
- package/UI/Components/AttributesJSON/AttributesViewToggle.tsx +79 -0
- package/UI/Components/AttributesJSON/BracesGlyph.tsx +30 -0
- package/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.tsx +491 -0
- package/UI/Components/AttributesJSON/JSONHighlight.ts +77 -0
- package/UI/Components/Calendar/Calendar.css +83 -0
- package/UI/Components/Calendar/Calendar.tsx +136 -7
- package/UI/Components/Calendar/CalendarDayLayout.ts +236 -0
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -1
- package/UI/Components/Charts/Utils/HistogramSelection.ts +117 -0
- package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +269 -0
- package/UI/Components/Charts/Utils/useHistogramZoom.ts +26 -2
- package/UI/Components/Date/CustomTimeRangeModal.tsx +26 -3
- package/UI/Components/Detail/FieldLabel.tsx +8 -0
- package/UI/Components/Dropdown/Dropdown.tsx +58 -9
- package/UI/Components/Forms/Fields/FormField.tsx +49 -14
- package/UI/Components/Graphs/DayUptimeGraph.tsx +166 -25
- package/UI/Components/Graphs/UptimeBarTooltip.tsx +10 -2
- package/UI/Components/Graphs/UptimeDaySummary.tsx +8 -0
- package/UI/Components/InfoCard/InfoCard.tsx +47 -1
- package/UI/Components/LogsViewer/LogsViewer.tsx +52 -5
- package/UI/Components/LogsViewer/components/ColumnSelector.tsx +13 -2
- package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +13 -1
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +91 -76
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +15 -1
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +28 -106
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +54 -6
- package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +8 -2
- package/UI/Components/LogsViewer/useLogsHistogram.ts +39 -6
- package/UI/Components/ModelTable/BaseModelTable.tsx +161 -31
- package/UI/Components/ModelTable/Column.ts +5 -0
- package/UI/Components/MonitorGraphs/Uptime.tsx +12 -0
- package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +10 -2
- package/UI/Components/Page/ModelPage.tsx +59 -3
- package/UI/Components/Table/TableHeader.tsx +47 -2
- package/UI/Components/Table/Types/Column.ts +7 -0
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +69 -5
- package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +12 -1
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +15 -1
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +36 -104
- package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +9 -1
- package/UI/Components/Tooltip/InfoTooltip.tsx +78 -0
- package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +19 -1
- package/UI/Utils/PermissionGate.ts +11 -2
- package/UI/Utils/Timezone.ts +69 -28
- package/Utils/AI/InvestigationReport.ts +195 -25
- package/Utils/AiRemediation/KubectlOutputRedactor.ts +1724 -0
- package/Utils/AiRemediation/KubectlPolicy.ts +4136 -0
- package/Utils/AiRemediation/KubectlWaitBudget.ts +156 -0
- package/Utils/AiRemediation/KubectlWriteScope.ts +1964 -0
- package/Utils/Dashboard/Components/DashboardClockComponent.ts +7 -6
- package/Utils/Incident/IncidentFromAlerts.ts +516 -0
- package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
- package/Utils/Monitor/MonitorCheckScheduleUtil.ts +654 -0
- package/Utils/Monitor/MonitorOverviewCriteriaUtil.ts +188 -0
- package/Utils/Monitor/MonitorOverviewFamily.ts +263 -0
- package/Utils/Monitor/MonitorOverviewPresentationUtil.ts +1958 -0
- package/Utils/Monitor/MonitorOverviewProbeUtil.ts +979 -0
- package/Utils/Monitor/MonitorOverviewTargetUtil.ts +299 -0
- package/Utils/Monitor/MonitorStatusHistoryUtil.ts +201 -0
- package/Utils/Monitor/MonitorUptimeSummaryUtil.ts +638 -0
- package/Utils/Rum/SessionIdentity.ts +44 -0
- package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
- package/Utils/Rum/SessionTraceState.ts +212 -0
- package/Utils/Rum/UserFlow.ts +1527 -0
- package/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.ts +107 -0
- package/Utils/StatusPage/ResourceUptime.ts +99 -0
- package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +197 -0
- package/Utils/Telemetry/AttributesJSON.ts +367 -0
- package/Utils/Uptime/DayUptimeGraphUtil.ts +165 -0
- package/Utils/Uptime/UptimeUtil.ts +122 -38
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +8 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentAlert.js +553 -0
- package/build/dist/Models/DatabaseModels/IncidentAlert.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +8 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +10 -3
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +10 -3
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +434 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +49 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +62 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +53 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Runner.js +4 -4
- package/build/dist/Models/DatabaseModels/Runner.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +66 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +11 -3
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +487 -3
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +64 -16
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +205 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +115 -2
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +49 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +564 -0
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -0
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +465 -176
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorAPI.js +107 -1
- package/build/dist/Server/API/MonitorAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +5 -16
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +322 -78
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +123 -17
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +144 -5
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +1 -0
- package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -1
- package/build/dist/Server/API/WorkflowVariableAPI.js +109 -0
- package/build/dist/Server/API/WorkflowVariableAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js +54 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794800000000-AddSessionReplaySameOriginTracePropagation.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794900000000-AddIncidentAlert.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +21 -0
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +32 -2
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +39 -6
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AIAgentService.js +1 -0
- package/build/dist/Server/Services/AIAgentService.js.map +1 -1
- package/build/dist/Server/Services/AccessTokenService.js +87 -42
- package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeMemberService.js +4 -0
- package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeService.js +12 -1
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +110 -23
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +716 -30
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js +100 -0
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +1 -0
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js +11 -16
- package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js.map +1 -1
- package/build/dist/Server/Services/IncidentAlertService.js +1370 -0
- package/build/dist/Server/Services/IncidentAlertService.js.map +1 -0
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +4 -0
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +48 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js +21 -0
- package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +12 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentMemberService.js +14 -0
- package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +48 -0
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +263 -33
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +33 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +68 -4
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +1470 -0
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterService.js +1097 -0
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +14 -6
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +487 -48
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +540 -3
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +3 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js +29 -7
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -1
- package/build/dist/Server/Services/OnCallSetupReminderService.js +1 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +7 -1
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +11 -1
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RunbookCredentialService.js +24 -0
- package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -1
- package/build/dist/Server/Services/RunbookSecretService.js +44 -0
- package/build/dist/Server/Services/RunbookSecretService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +641 -21
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +344 -5
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +48 -0
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -20
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +67 -4
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +12 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +230 -32
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +270 -11
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +10 -6
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +1 -0
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +1 -0
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +4 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +107 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowVariableService.js +396 -7
- package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +463 -7
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +144 -2
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +20 -0
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +394 -27
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +123 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +344 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +587 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +18 -2
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1380 -239
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +1099 -68
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +88 -6
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +45 -11
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +41 -11
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +11 -4
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +4 -2
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +997 -141
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +299 -4
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +120 -0
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +160 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +1 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +59 -12
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +5 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +84 -22
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +70 -5
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js +249 -0
- package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -1
- package/build/dist/Server/Utils/PushNotificationUtil.js +27 -0
- package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
- package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +26 -3
- package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
- package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +38 -3
- package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +8 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +143 -23
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js +406 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +4 -3
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +382 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -0
- package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js +1 -0
- package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js.map +1 -1
- package/build/dist/Server/Utils/UserPermission/UserPermission.js +10 -0
- package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +447 -0
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -0
- package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js +202 -0
- package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +39 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +44 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +150 -191
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +44 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +39 -17
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +151 -145
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +221 -127
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js +91 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js +673 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +3 -9
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +65 -166
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +63 -157
- package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +23 -3
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js +92 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +81 -210
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +68 -138
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js +240 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +85 -212
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +60 -17
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
- package/build/dist/Server/Utils/Workspace/Workspace.js +134 -41
- package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js +197 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +32 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js +199 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js +91 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js +450 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +103 -5
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/Date.js +97 -7
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Exception/ExceptionMessages.js +1 -0
- package/build/dist/Types/Exception/ExceptionMessages.js.map +1 -1
- package/build/dist/Types/Incident/IncidentAlertLink.js +34 -0
- package/build/dist/Types/Incident/IncidentAlertLink.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +245 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +49 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js +16 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js.map +1 -0
- package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js +104 -0
- package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js.map +1 -0
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +6 -5
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +32 -0
- package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorUptimeSummary.js +52 -0
- package/build/dist/Types/Monitor/MonitorUptimeSummary.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +9 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +52 -2
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayApi.js +11 -0
- package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
- package/build/dist/Types/Rum/UserFlow.js +115 -0
- package/build/dist/Types/Rum/UserFlow.js.map +1 -0
- package/build/dist/Types/Rum/WebVitals.js +25 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/Types/Runbook/RunbookStepType.js +10 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/Types/Runbook/RunnerJobOrigin.js +14 -0
- package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -1
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +2 -2
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
- package/build/dist/Types/StatusPage/MergedDowntimeTotals.js +2 -0
- package/build/dist/Types/StatusPage/MergedDowntimeTotals.js.map +1 -0
- package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js +65 -0
- package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +8 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/Types/Timezone.js +5 -0
- package/build/dist/Types/Timezone.js.map +1 -1
- package/build/dist/Types/TimezoneAlias.js +340 -0
- package/build/dist/Types/TimezoneAlias.js.map +1 -0
- package/build/dist/Types/Workflow/Templates.js +2694 -2
- package/build/dist/Types/Workflow/Templates.js.map +1 -1
- package/build/dist/Types/Workflow/WorkflowVariableOAuth.js +314 -0
- package/build/dist/Types/Workflow/WorkflowVariableOAuth.js.map +1 -0
- package/build/dist/Types/Workspace/WorkspaceNoteReaction.js +98 -0
- package/build/dist/Types/Workspace/WorkspaceNoteReaction.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js +95 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js +44 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js +27 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js +9 -0
- package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js +260 -0
- package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js +56 -0
- package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js.map +1 -0
- package/build/dist/UI/Components/Calendar/Calendar.js +94 -7
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Calendar/CalendarDayLayout.js +136 -0
- package/build/dist/UI/Components/Calendar/CalendarDayLayout.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js +72 -0
- package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +178 -0
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js +13 -1
- package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js.map +1 -1
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +20 -4
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -1
- package/build/dist/UI/Components/Detail/FieldLabel.js +2 -0
- package/build/dist/UI/Components/Detail/FieldLabel.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -9
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +36 -13
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +117 -11
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -1
- package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
- package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +22 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +36 -8
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +10 -2
- package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +9 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +16 -15
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +14 -74
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +22 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +9 -2
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +16 -5
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +89 -22
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js +7 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +5 -1
- package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
- package/build/dist/UI/Components/Page/ModelPage.js +37 -3
- package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
- package/build/dist/UI/Components/Table/TableHeader.js +18 -2
- package/build/dist/UI/Components/Table/TableHeader.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +27 -6
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +7 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +17 -77
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +10 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js +28 -0
- package/build/dist/UI/Components/Tooltip/InfoTooltip.js.map +1 -0
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +15 -1
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/PermissionGate.js +4 -2
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/build/dist/UI/Utils/Timezone.js +52 -17
- package/build/dist/UI/Utils/Timezone.js.map +1 -1
- package/build/dist/Utils/AI/InvestigationReport.js +116 -21
- package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
- package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js +1209 -0
- package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js.map +1 -0
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js +2973 -0
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js +99 -0
- package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js.map +1 -0
- package/build/dist/Utils/AiRemediation/KubectlWriteScope.js +1429 -0
- package/build/dist/Utils/AiRemediation/KubectlWriteScope.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +7 -6
- package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -1
- package/build/dist/Utils/Incident/IncidentFromAlerts.js +306 -0
- package/build/dist/Utils/Incident/IncidentFromAlerts.js.map +1 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js +214 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js +471 -0
- package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js +107 -0
- package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewFamily.js +191 -0
- package/build/dist/Utils/Monitor/MonitorOverviewFamily.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js +1259 -0
- package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js +593 -0
- package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js +195 -0
- package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js +127 -0
- package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js +464 -0
- package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js.map +1 -0
- package/build/dist/Utils/Rum/SessionIdentity.js +31 -1
- package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
- package/build/dist/Utils/Rum/SessionReplayWireEncoding.js +128 -0
- package/build/dist/Utils/Rum/SessionReplayWireEncoding.js.map +1 -0
- package/build/dist/Utils/Rum/SessionTraceState.js +154 -0
- package/build/dist/Utils/Rum/SessionTraceState.js.map +1 -0
- package/build/dist/Utils/Rum/UserFlow.js +941 -0
- package/build/dist/Utils/Rum/UserFlow.js.map +1 -0
- package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js +82 -0
- package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceUptime.js +46 -0
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/AttributesJSON.js +245 -0
- package/build/dist/Utils/Telemetry/AttributesJSON.js.map +1 -0
- package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +103 -0
- package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -1
- package/build/dist/Utils/Uptime/UptimeUtil.js +91 -27
- package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
- package/jest.config.json +2 -1
- package/package.json +2 -1
- package/tsconfig.tests.json +36 -0
- package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +0 -337
- package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +0 -124
- package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +0 -47
- package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +0 -1
|
@@ -0,0 +1,1943 @@
|
|
|
1
|
+
import Alert from "../../Models/DatabaseModels/Alert";
|
|
2
|
+
import Incident from "../../Models/DatabaseModels/Incident";
|
|
3
|
+
import KubernetesCluster from "../../Models/DatabaseModels/KubernetesCluster";
|
|
4
|
+
import Monitor from "../../Models/DatabaseModels/Monitor";
|
|
5
|
+
import Project from "../../Models/DatabaseModels/Project";
|
|
6
|
+
import RunbookCredential from "../../Models/DatabaseModels/RunbookCredential";
|
|
7
|
+
import Runner, {
|
|
8
|
+
RunnerConnectionStatus,
|
|
9
|
+
} from "../../Models/DatabaseModels/Runner";
|
|
10
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
11
|
+
import ForbiddenException from "../../Types/Exception/ForbiddenException";
|
|
12
|
+
import TooManyRequestsException from "../../Types/Exception/TooManyRequestsException";
|
|
13
|
+
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
14
|
+
import OneUptimeDate from "../../Types/Date";
|
|
15
|
+
import { JSONObject } from "../../Types/JSON";
|
|
16
|
+
import ObjectID from "../../Types/ObjectID";
|
|
17
|
+
import Version from "../../Types/Version";
|
|
18
|
+
import MonitorStep from "../../Types/Monitor/MonitorStep";
|
|
19
|
+
import { getDeletedAgentRunnerRebindNote } from "../../Types/Kubernetes/KubernetesClusterAiAccessPermissions";
|
|
20
|
+
import RunbookCredentialType from "../../Types/Runbook/RunbookCredentialType";
|
|
21
|
+
import {
|
|
22
|
+
RUNNER_ALIVE_WINDOW_IN_MINUTES,
|
|
23
|
+
RunnerLiveStatus,
|
|
24
|
+
getRunnerLiveStatus,
|
|
25
|
+
} from "../../Types/Runner/RunnerLiveStatus";
|
|
26
|
+
import {
|
|
27
|
+
KUBERNETES_AGENT_RUNNER_NAME_PREFIX,
|
|
28
|
+
KubernetesAgentRegistrationRefusalReason,
|
|
29
|
+
KubernetesAgentRunnerBindingState,
|
|
30
|
+
KubernetesAiAccessGap,
|
|
31
|
+
KubernetesAiRemediationMode,
|
|
32
|
+
KubernetesClusterAiAccessStatus,
|
|
33
|
+
KubernetesRunnerPosture,
|
|
34
|
+
isInClusterPostureForCluster,
|
|
35
|
+
isSameKubernetesClusterIdentifier,
|
|
36
|
+
normalizeKubernetesClusterIdentifier,
|
|
37
|
+
parseKubernetesRunnerPosture,
|
|
38
|
+
} from "../../Types/Kubernetes/KubernetesClusterAiAccess";
|
|
39
|
+
import { KubernetesClusterFeedEventType } from "../../Models/DatabaseModels/KubernetesClusterFeed";
|
|
40
|
+
import { Blue500, Green500, Yellow500 } from "../../Types/BrandColors";
|
|
41
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
42
|
+
import AlertService from "./AlertService";
|
|
43
|
+
import IncidentService from "./IncidentService";
|
|
44
|
+
import KubernetesClusterFeedService from "./KubernetesClusterFeedService";
|
|
45
|
+
import KubernetesClusterService from "./KubernetesClusterService";
|
|
46
|
+
import LlmProviderService from "./LlmProviderService";
|
|
47
|
+
import MonitorService from "./MonitorService";
|
|
48
|
+
import ProjectService from "./ProjectService";
|
|
49
|
+
import RunbookCredentialService from "./RunbookCredentialService";
|
|
50
|
+
import RunbookSecretService from "./RunbookSecretService";
|
|
51
|
+
import RunnerService, {
|
|
52
|
+
Service as RunnerServiceClass,
|
|
53
|
+
getKubernetesAgentRunnerNameForCluster,
|
|
54
|
+
} from "./RunnerService";
|
|
55
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
56
|
+
import logger from "../Utils/Logger";
|
|
57
|
+
import crypto from "crypto";
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* The agent Runner's name for a cluster, and the Runner name column bound.
|
|
61
|
+
* Defined next to the Runner guards that key on it (RunnerService) and
|
|
62
|
+
* re-exported here, where registration and its callers have always found
|
|
63
|
+
* them.
|
|
64
|
+
*/
|
|
65
|
+
export {
|
|
66
|
+
MAX_KUBERNETES_AGENT_RUNNER_NAME_LENGTH,
|
|
67
|
+
getKubernetesAgentRunnerNameForCluster,
|
|
68
|
+
} from "./RunnerService";
|
|
69
|
+
|
|
70
|
+
/*
|
|
71
|
+
* OneUptime AI access to Kubernetes clusters.
|
|
72
|
+
*
|
|
73
|
+
* Answers, from CURRENT configuration, whether AI can reach a cluster with
|
|
74
|
+
* kubectl and what it may do there — and turns every reason it cannot into a
|
|
75
|
+
* row of a checklist with a next step. The same status feeds the cluster's
|
|
76
|
+
* AI page, the investigation panel ("we could not access the cluster, here
|
|
77
|
+
* is why") and the AI runs themselves (which only ever act on a cluster this
|
|
78
|
+
* module calls ready).
|
|
79
|
+
*
|
|
80
|
+
* It also binds the in-cluster Runner the kubernetes-agent chart installs:
|
|
81
|
+
* that Runner registers with the project's telemetry ingestion key and the
|
|
82
|
+
* cluster's name, and this module upserts its Runner row and points the
|
|
83
|
+
* cluster at it. One helm flag, no dashboard steps.
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
export interface KubernetesClusterAiAccessProjectGates {
|
|
87
|
+
isAiEnabled: boolean;
|
|
88
|
+
isAutoRemediationEnabled: boolean;
|
|
89
|
+
isAiCommandExecutionEnabled: boolean;
|
|
90
|
+
hasLlmProvider: boolean;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface RegisterKubernetesAgentRunnerResult {
|
|
94
|
+
runnerId: ObjectID;
|
|
95
|
+
runnerKey: string;
|
|
96
|
+
clusterId: ObjectID;
|
|
97
|
+
/*
|
|
98
|
+
* False when the cluster is bound to a DIFFERENT Runner that an operator
|
|
99
|
+
* chose in the dashboard, or when a cluster that had a Runner bound before
|
|
100
|
+
* has none now (an operator cleared it, or its Runner was deleted). The
|
|
101
|
+
* agent Runner row still exists and heartbeats, but the dashboard is the
|
|
102
|
+
* control plane: it never silently rebinds.
|
|
103
|
+
*/
|
|
104
|
+
isBoundToCluster: boolean;
|
|
105
|
+
isFirstBind: boolean;
|
|
106
|
+
bindingState: KubernetesAgentRunnerBindingState;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* Ceiling on agent Runner rows one project may hold, and on how many new
|
|
111
|
+
* ones may be minted per hour. Registration is authenticated by the
|
|
112
|
+
* project's telemetry ingestion key — a credential every collector and CI
|
|
113
|
+
* job holds — and every distinct clusterName creates a Runner row with the
|
|
114
|
+
* AI-commands capability on, so the rows must be bounded like any other
|
|
115
|
+
* thing an ingestion key can create. A legitimate fleet-wide install that
|
|
116
|
+
* trips the hourly brake is only delayed: the pod retries registration
|
|
117
|
+
* with backoff until it is admitted.
|
|
118
|
+
*/
|
|
119
|
+
export const MAX_KUBERNETES_AGENT_RUNNERS_PER_PROJECT: number = 250;
|
|
120
|
+
export const MAX_NEW_KUBERNETES_AGENT_RUNNERS_PER_PROJECT_PER_HOUR: number = 30;
|
|
121
|
+
|
|
122
|
+
// What the status computation reads off a cluster row.
|
|
123
|
+
export const CLUSTER_AI_ACCESS_SELECT: Record<string, boolean> = {
|
|
124
|
+
_id: true,
|
|
125
|
+
projectId: true,
|
|
126
|
+
name: true,
|
|
127
|
+
clusterIdentifier: true,
|
|
128
|
+
aiAccessRunnerId: true,
|
|
129
|
+
aiAccessCredentialId: true,
|
|
130
|
+
isAiInvestigationEnabled: true,
|
|
131
|
+
aiRemediationMode: true,
|
|
132
|
+
aiKubectlCommandAllowlist: true,
|
|
133
|
+
aiAccessLastVerifiedAt: true,
|
|
134
|
+
aiAccessLastError: true,
|
|
135
|
+
aiAccessConfiguredAt: true,
|
|
136
|
+
aiAccessRunnerBoundAt: true,
|
|
137
|
+
isArchived: true,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const MAX_CLUSTERS_PER_SUBJECT: number = 10;
|
|
141
|
+
|
|
142
|
+
/*
|
|
143
|
+
* What to do about an in-cluster Runner that reports read-only RBAC while
|
|
144
|
+
* remediation is on. It points at the AI page's write-access section, which
|
|
145
|
+
* carries the complete helm commands (a bare --set line would miss the
|
|
146
|
+
* chart index refresh and --reuse-values), and names the two values that
|
|
147
|
+
* bound what the write role reaches: the namespaces it is bound in and the
|
|
148
|
+
* node switch. Shown verbatim on incident pages too, so it names the page
|
|
149
|
+
* rather than saying "this page".
|
|
150
|
+
*/
|
|
151
|
+
export const REMEDIATION_WRITE_ACCESS_NEXT_STEP: string =
|
|
152
|
+
'Grant the in-cluster Runner write access with a helm upgrade that adds --set aiAccess.remediation.enabled=true; the complete commands are under "Let AI apply fixes (write access)" on the cluster\'s AI page. List the namespaces AI may fix in aiAccess.remediation.namespaces (without it the write role is bound cluster-wide), and add aiAccess.remediation.nodeOperations=false to keep fixes off nodes.';
|
|
153
|
+
|
|
154
|
+
/*
|
|
155
|
+
* Why deleting an in-cluster Runner is a two-step remedy (the helper lives
|
|
156
|
+
* with the permissions it names, so RunnerService can use it too).
|
|
157
|
+
*/
|
|
158
|
+
export { getDeletedAgentRunnerRebindNote };
|
|
159
|
+
|
|
160
|
+
/*
|
|
161
|
+
* Runner.name and KubernetesCluster.clusterIdentifier are both ShortText
|
|
162
|
+
* columns. A cluster name longer than the column can never become a cluster
|
|
163
|
+
* row, so registration refuses it up front with a message that says so.
|
|
164
|
+
*/
|
|
165
|
+
export const MAX_KUBERNETES_CLUSTER_IDENTIFIER_LENGTH: number =
|
|
166
|
+
ColumnLength.ShortText;
|
|
167
|
+
|
|
168
|
+
/*
|
|
169
|
+
* A 403 from POST /runner-ingest/register-kubernetes-agent that says WHICH
|
|
170
|
+
* refusal it is (KubernetesAgentRegistrationRefusalReason), so the Runner can
|
|
171
|
+
* tell a wait that clears on its own from one that needs an operator instead
|
|
172
|
+
* of reading every 403 as "the previous instance is still online". The
|
|
173
|
+
* ingress sends `reason` (and, for previous_instance_online,
|
|
174
|
+
* `retryAfterSeconds` plus a Retry-After header) in the JSON error body.
|
|
175
|
+
*/
|
|
176
|
+
export class KubernetesAgentRegistrationRefusedException extends ForbiddenException {
|
|
177
|
+
public readonly reason: KubernetesAgentRegistrationRefusalReason;
|
|
178
|
+
// Set only for a refusal that clears on its own: when to try again.
|
|
179
|
+
public readonly retryAfterSeconds: number | undefined;
|
|
180
|
+
|
|
181
|
+
public constructor(data: {
|
|
182
|
+
message: string;
|
|
183
|
+
reason: KubernetesAgentRegistrationRefusalReason;
|
|
184
|
+
retryAfterSeconds?: number | undefined;
|
|
185
|
+
}) {
|
|
186
|
+
super(data.message);
|
|
187
|
+
this.reason = data.reason;
|
|
188
|
+
this.retryAfterSeconds = data.retryAfterSeconds;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/*
|
|
193
|
+
* When a registration refused because the previous instance is online may
|
|
194
|
+
* try again: once that instance's last heartbeat is older than the alive
|
|
195
|
+
* window. Never less than a second, so a Runner never retries in a loop.
|
|
196
|
+
*/
|
|
197
|
+
export function getPreviousInstanceRetryAfterSeconds(data: {
|
|
198
|
+
lastAlive: Date | undefined;
|
|
199
|
+
now?: Date | undefined;
|
|
200
|
+
}): number {
|
|
201
|
+
if (!data.lastAlive) {
|
|
202
|
+
return 1;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const admittedAt: number =
|
|
206
|
+
data.lastAlive.getTime() + RUNNER_ALIVE_WINDOW_IN_MINUTES * 60 * 1000;
|
|
207
|
+
const now: number = (data.now || OneUptimeDate.getCurrentDate()).getTime();
|
|
208
|
+
|
|
209
|
+
return Math.max(1, Math.ceil((admittedAt - now) / 1000));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/*
|
|
213
|
+
* One thing an agent Runner row holds beyond what registration gave it, and
|
|
214
|
+
* what an operator does under Project Settings → Runners to remove it.
|
|
215
|
+
*/
|
|
216
|
+
export interface AgentRunnerHolding {
|
|
217
|
+
description: string;
|
|
218
|
+
remedy: string;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/*
|
|
222
|
+
* How a registration that re-keys an existing agent Runner row was admitted.
|
|
223
|
+
*
|
|
224
|
+
* continuity: it presented the row's current key — the same Runner.
|
|
225
|
+
* signed_off: no proof; the previous instance had signed off
|
|
226
|
+
* (/runner-ingest/disconnect), as on a pod restart.
|
|
227
|
+
* offline: no proof; the previous instance had stopped heartbeating.
|
|
228
|
+
* never_connected: no proof; the row had never heartbeated.
|
|
229
|
+
*
|
|
230
|
+
* Only "continuity" is proof. The other three are what a restarted pod
|
|
231
|
+
* looks like — and also what anyone holding the ingestion key looks like —
|
|
232
|
+
* so they are admitted only for a row that holds nothing but the agent
|
|
233
|
+
* defaults, and the feed says which one it was.
|
|
234
|
+
*/
|
|
235
|
+
type AgentRunnerReKeyAdmission =
|
|
236
|
+
| "continuity"
|
|
237
|
+
| "signed_off"
|
|
238
|
+
| "offline"
|
|
239
|
+
| "never_connected";
|
|
240
|
+
|
|
241
|
+
interface RunnerPresence {
|
|
242
|
+
liveStatus: RunnerLiveStatus;
|
|
243
|
+
// The Runner shut down cleanly after its last heartbeat.
|
|
244
|
+
isSignedOff: boolean;
|
|
245
|
+
isOnline: boolean;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
class KubernetesClusterAiAccessServiceClass {
|
|
249
|
+
/*
|
|
250
|
+
* ------------------------------------------------------------------
|
|
251
|
+
* Readiness
|
|
252
|
+
* ------------------------------------------------------------------
|
|
253
|
+
*/
|
|
254
|
+
|
|
255
|
+
@CaptureSpan()
|
|
256
|
+
public async getProjectGates(
|
|
257
|
+
projectId: ObjectID,
|
|
258
|
+
): Promise<KubernetesClusterAiAccessProjectGates> {
|
|
259
|
+
const project: Project | null = await ProjectService.findOneById({
|
|
260
|
+
id: projectId,
|
|
261
|
+
select: {
|
|
262
|
+
enableAi: true,
|
|
263
|
+
enableAutoRemediation: true,
|
|
264
|
+
enableAiCommandExecution: true,
|
|
265
|
+
},
|
|
266
|
+
props: { isRoot: true },
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
let hasLlmProvider: boolean = false;
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
hasLlmProvider =
|
|
273
|
+
(await LlmProviderService.getLLMProviderForProject(projectId)) !== null;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
logger.error(
|
|
276
|
+
`KubernetesClusterAiAccess: could not resolve the LLM provider for project ${projectId.toString()}: ${error}`,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
// Kill switches: a missing column counts as enabled (=== false idiom).
|
|
282
|
+
isAiEnabled: project?.enableAi !== false,
|
|
283
|
+
isAutoRemediationEnabled: project?.enableAutoRemediation !== false,
|
|
284
|
+
// Explicit opt-in (=== true idiom), same as the Bash/SSH lane.
|
|
285
|
+
isAiCommandExecutionEnabled: project?.enableAiCommandExecution === true,
|
|
286
|
+
hasLlmProvider,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
@CaptureSpan()
|
|
291
|
+
public async getStatusForCluster(data: {
|
|
292
|
+
clusterId: ObjectID;
|
|
293
|
+
projectId: ObjectID;
|
|
294
|
+
gates?: KubernetesClusterAiAccessProjectGates | undefined;
|
|
295
|
+
}): Promise<KubernetesClusterAiAccessStatus | null> {
|
|
296
|
+
const cluster: KubernetesCluster | null =
|
|
297
|
+
await KubernetesClusterService.findOneBy({
|
|
298
|
+
query: {
|
|
299
|
+
_id: data.clusterId.toString(),
|
|
300
|
+
projectId: data.projectId,
|
|
301
|
+
},
|
|
302
|
+
select: CLUSTER_AI_ACCESS_SELECT,
|
|
303
|
+
props: { isRoot: true },
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
if (!cluster) {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return this.getStatusForClusterModel({
|
|
311
|
+
cluster,
|
|
312
|
+
gates: data.gates || (await this.getProjectGates(data.projectId)),
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/*
|
|
317
|
+
* The readiness computation proper. Reads the bound Runner and credential
|
|
318
|
+
* as root (the cluster row is already tenant-scoped by the caller) and
|
|
319
|
+
* folds project gates in. Never throws: a lookup failure becomes a gap,
|
|
320
|
+
* because "we could not check" must not read as "ready".
|
|
321
|
+
*/
|
|
322
|
+
@CaptureSpan()
|
|
323
|
+
public async getStatusForClusterModel(data: {
|
|
324
|
+
cluster: KubernetesCluster;
|
|
325
|
+
gates: KubernetesClusterAiAccessProjectGates;
|
|
326
|
+
}): Promise<KubernetesClusterAiAccessStatus> {
|
|
327
|
+
const { cluster, gates } = data;
|
|
328
|
+
const gaps: Array<KubernetesAiAccessGap> = [];
|
|
329
|
+
|
|
330
|
+
const remediationMode: KubernetesAiRemediationMode =
|
|
331
|
+
this.normalizeRemediationMode(cluster.aiRemediationMode);
|
|
332
|
+
const isInvestigationEnabled: boolean =
|
|
333
|
+
cluster.isAiInvestigationEnabled === true;
|
|
334
|
+
|
|
335
|
+
let runnerSummary: KubernetesClusterAiAccessStatus["runner"] = null;
|
|
336
|
+
let accessMethod: KubernetesClusterAiAccessStatus["accessMethod"] = "none";
|
|
337
|
+
let credentialId: string | undefined = undefined;
|
|
338
|
+
let credentialName: string | undefined = undefined;
|
|
339
|
+
|
|
340
|
+
if (!cluster.aiAccessRunnerId) {
|
|
341
|
+
gaps.push(await this.getNoRunnerBoundGap(cluster));
|
|
342
|
+
} else {
|
|
343
|
+
const runner: Runner | null = await RunnerService.findOneBy({
|
|
344
|
+
query: {
|
|
345
|
+
_id: cluster.aiAccessRunnerId.toString(),
|
|
346
|
+
projectId: cluster.projectId!,
|
|
347
|
+
},
|
|
348
|
+
select: {
|
|
349
|
+
_id: true,
|
|
350
|
+
name: true,
|
|
351
|
+
lastAlive: true,
|
|
352
|
+
connectionStatus: true,
|
|
353
|
+
canRunAiCommands: true,
|
|
354
|
+
// What would stop an offline agent row from re-registering.
|
|
355
|
+
canRunRunbooks: true,
|
|
356
|
+
canRunCodeFixTasks: true,
|
|
357
|
+
hostInfo: true,
|
|
358
|
+
},
|
|
359
|
+
props: { isRoot: true },
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
if (!runner) {
|
|
363
|
+
/*
|
|
364
|
+
* Only a race reaches this: the binding's foreign key is ON DELETE
|
|
365
|
+
* SET NULL, so deleting the bound Runner clears aiAccessRunnerId in
|
|
366
|
+
* the same statement, and the next read takes the no_runner_bound
|
|
367
|
+
* branch above. This is the window between reading the cluster row
|
|
368
|
+
* and reading its Runner while a delete lands.
|
|
369
|
+
*/
|
|
370
|
+
gaps.push({
|
|
371
|
+
code: "runner_missing",
|
|
372
|
+
title: "The bound Runner was just deleted",
|
|
373
|
+
description:
|
|
374
|
+
"The Runner this cluster was bound to was deleted while its status was being read.",
|
|
375
|
+
nextStep:
|
|
376
|
+
"Reload the cluster's AI page, then bind another Runner there, or reinstall the in-cluster Runner with --set aiAccess.enabled=true.",
|
|
377
|
+
blocks: "both",
|
|
378
|
+
});
|
|
379
|
+
} else {
|
|
380
|
+
const posture: KubernetesRunnerPosture | undefined =
|
|
381
|
+
parseKubernetesRunnerPosture(runner.hostInfo);
|
|
382
|
+
const presence: RunnerPresence = this.getRunnerPresence(runner);
|
|
383
|
+
const isOnline: boolean = presence.isOnline;
|
|
384
|
+
// The one "is an agent row" rule: the name marker or an agent posture.
|
|
385
|
+
const isAgentRunnerRow: boolean =
|
|
386
|
+
RunnerServiceClass.isKubernetesAgentRunnerRow(runner);
|
|
387
|
+
const isAgentRunner: boolean =
|
|
388
|
+
isAgentRunnerRow || posture?.inCluster === true;
|
|
389
|
+
|
|
390
|
+
runnerSummary = {
|
|
391
|
+
id: runner.id!.toString(),
|
|
392
|
+
name: runner.name || "Runner",
|
|
393
|
+
isOnline,
|
|
394
|
+
lastAliveAt: runner.lastAlive
|
|
395
|
+
? OneUptimeDate.toString(runner.lastAlive)
|
|
396
|
+
: undefined,
|
|
397
|
+
canRunAiCommands: runner.canRunAiCommands === true,
|
|
398
|
+
posture,
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
if (!isOnline) {
|
|
402
|
+
/*
|
|
403
|
+
* An offline agent Runner of THIS cluster that holds more than the
|
|
404
|
+
* agent defaults will be refused on every re-registration its
|
|
405
|
+
* restarted pod attempts (it cannot present the key it held), so
|
|
406
|
+
* "it reconnects within a minute" would be false: say what blocks
|
|
407
|
+
* it instead.
|
|
408
|
+
*/
|
|
409
|
+
const holdingsGap: KubernetesAiAccessGap | null =
|
|
410
|
+
RunnerServiceClass.isKubernetesAgentRunnerOfCluster(
|
|
411
|
+
runner,
|
|
412
|
+
cluster.clusterIdentifier,
|
|
413
|
+
)
|
|
414
|
+
? await this.getAgentRunnerHoldingsGap({ runner, cluster })
|
|
415
|
+
: null;
|
|
416
|
+
|
|
417
|
+
gaps.push(
|
|
418
|
+
holdingsGap ||
|
|
419
|
+
this.getRunnerOfflineGap({ runner, presence, isAgentRunner }),
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (runner.canRunAiCommands !== true) {
|
|
424
|
+
gaps.push({
|
|
425
|
+
code: "runner_ai_commands_disabled",
|
|
426
|
+
title: "The Runner does not accept AI commands",
|
|
427
|
+
description: `"Runs AI Remediation Commands" is turned off on Runner "${runner.name}", so it will not be served kubectl work.`,
|
|
428
|
+
nextStep:
|
|
429
|
+
'Turn on "Runs AI Remediation Commands" on the Runner (Project Settings → Runners).',
|
|
430
|
+
blocks: "both",
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/*
|
|
435
|
+
* In-cluster access means "kubectl with the pod's own
|
|
436
|
+
* ServiceAccount", which reaches whatever cluster the pod lives
|
|
437
|
+
* in. So it is usable only when the Runner's posture names THIS
|
|
438
|
+
* cluster — a Runner that is in-cluster somewhere else (the
|
|
439
|
+
* dashboard lets an operator pick any Runner) would run every
|
|
440
|
+
* command for this cluster against the wrong one.
|
|
441
|
+
*/
|
|
442
|
+
const isInClusterForThisCluster: boolean = isInClusterPostureForCluster(
|
|
443
|
+
posture,
|
|
444
|
+
cluster.clusterIdentifier,
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
if (isInClusterForThisCluster) {
|
|
448
|
+
accessMethod = "in_cluster";
|
|
449
|
+
} else if (cluster.aiAccessCredentialId && isAgentRunnerRow) {
|
|
450
|
+
/*
|
|
451
|
+
* A kubernetes-agent Runner is never handed credential material:
|
|
452
|
+
* its row is minted and re-keyed with the project's telemetry
|
|
453
|
+
* ingestion key, so a credential it carried would be one leaked
|
|
454
|
+
* ingestion key away from anyone. The claim path refuses to
|
|
455
|
+
* resolve one for it (by the same name-or-posture rule);
|
|
456
|
+
* readiness says so instead of promising access that would fail
|
|
457
|
+
* on every command.
|
|
458
|
+
*/
|
|
459
|
+
gaps.push({
|
|
460
|
+
code: "credential_on_agent_runner",
|
|
461
|
+
title: "An in-cluster Runner cannot carry a credential",
|
|
462
|
+
description: `Runner "${runner.name}" is ${
|
|
463
|
+
posture?.clusterIdentifier?.trim()
|
|
464
|
+
? `the in-cluster Runner of cluster "${posture.clusterIdentifier.trim()}"`
|
|
465
|
+
: "a Kubernetes agent's in-cluster Runner"
|
|
466
|
+
}. It runs kubectl with its own ServiceAccount only and is never given a credential, so the Kubernetes credential selected for this cluster cannot be used through it.`,
|
|
467
|
+
nextStep:
|
|
468
|
+
"Create a Runner under Project Settings → Runners, assign the Kubernetes credential to it and select both on the cluster's AI page — or install the in-cluster Runner on THIS cluster with --set aiAccess.enabled=true, which needs no credential.",
|
|
469
|
+
blocks: "both",
|
|
470
|
+
});
|
|
471
|
+
} else if (cluster.aiAccessCredentialId) {
|
|
472
|
+
const credential: RunbookCredential | null =
|
|
473
|
+
await RunbookCredentialService.findOneBy({
|
|
474
|
+
query: {
|
|
475
|
+
_id: cluster.aiAccessCredentialId.toString(),
|
|
476
|
+
projectId: cluster.projectId!,
|
|
477
|
+
},
|
|
478
|
+
select: {
|
|
479
|
+
_id: true,
|
|
480
|
+
name: true,
|
|
481
|
+
credentialType: true,
|
|
482
|
+
runners: { _id: true },
|
|
483
|
+
},
|
|
484
|
+
props: { isRoot: true },
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
const isAssignedToRunner: boolean = Boolean(
|
|
488
|
+
credential?.runners?.some((assigned: Runner) => {
|
|
489
|
+
return assigned.id?.toString() === runner.id?.toString();
|
|
490
|
+
}),
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
if (
|
|
494
|
+
!credential ||
|
|
495
|
+
credential.credentialType !== RunbookCredentialType.Kubernetes ||
|
|
496
|
+
!isAssignedToRunner
|
|
497
|
+
) {
|
|
498
|
+
gaps.push({
|
|
499
|
+
code: "credential_missing",
|
|
500
|
+
title: "The Kubernetes credential is not usable by the Runner",
|
|
501
|
+
description: !credential
|
|
502
|
+
? "The credential bound to this cluster no longer exists."
|
|
503
|
+
: credential.credentialType !== RunbookCredentialType.Kubernetes
|
|
504
|
+
? `"${credential.name}" is not a Kubernetes credential.`
|
|
505
|
+
: `"${credential.name}" is not assigned to Runner "${runner.name}".`,
|
|
506
|
+
nextStep:
|
|
507
|
+
"Create a Kubernetes credential (API server URL + ServiceAccount token) under Project Settings → Runner Credentials, assign it to the Runner, and select it on the cluster's AI page.",
|
|
508
|
+
blocks: "both",
|
|
509
|
+
});
|
|
510
|
+
} else {
|
|
511
|
+
accessMethod = "credential";
|
|
512
|
+
credentialId = credential.id!.toString();
|
|
513
|
+
credentialName = credential.name;
|
|
514
|
+
}
|
|
515
|
+
} else if (posture?.inCluster) {
|
|
516
|
+
/*
|
|
517
|
+
* In-cluster, but for another cluster (or for no named cluster
|
|
518
|
+
* at all). Say so explicitly: the operator picked this Runner
|
|
519
|
+
* believing "in-cluster" meant "in this cluster".
|
|
520
|
+
*/
|
|
521
|
+
const reportedCluster: string | undefined =
|
|
522
|
+
posture.clusterIdentifier?.trim() || undefined;
|
|
523
|
+
|
|
524
|
+
gaps.push({
|
|
525
|
+
code: "runner_cluster_mismatch",
|
|
526
|
+
title: reportedCluster
|
|
527
|
+
? "The bound Runner is the in-cluster Runner of a different cluster"
|
|
528
|
+
: "The bound Runner did not report which cluster it runs in",
|
|
529
|
+
description: reportedCluster
|
|
530
|
+
? `Runner "${runner.name}" runs inside cluster "${reportedCluster}", not "${
|
|
531
|
+
cluster.clusterIdentifier || cluster.name || "this cluster"
|
|
532
|
+
}". Its ServiceAccount would run every kubectl command against "${reportedCluster}", so OneUptime AI will not use it for this cluster.`
|
|
533
|
+
: `Runner "${runner.name}" reports that it runs inside a Kubernetes cluster but not which one, so OneUptime AI cannot tell whether that is this cluster.`,
|
|
534
|
+
nextStep:
|
|
535
|
+
"Install the in-cluster Runner on THIS cluster with --set aiAccess.enabled=true and select it on the cluster's AI page as its Runner (a registering Runner never replaces the Runner a cluster is bound to), or bind a Runner that holds a Kubernetes credential for this cluster and select that credential on the cluster's AI page.",
|
|
536
|
+
blocks: "both",
|
|
537
|
+
});
|
|
538
|
+
} else {
|
|
539
|
+
gaps.push({
|
|
540
|
+
code: "credential_missing",
|
|
541
|
+
title: "The Runner has no credential for this cluster",
|
|
542
|
+
description: `Runner "${runner.name}" runs outside the cluster, so it needs a Kubernetes credential to reach the API server.`,
|
|
543
|
+
nextStep:
|
|
544
|
+
"Select a Kubernetes credential assigned to this Runner on the cluster's AI page — or install the in-cluster Runner with --set aiAccess.enabled=true, which needs no credential.",
|
|
545
|
+
blocks: "both",
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if (
|
|
550
|
+
remediationMode !== KubernetesAiRemediationMode.Disabled &&
|
|
551
|
+
posture?.inCluster &&
|
|
552
|
+
posture.allowWrites !== true
|
|
553
|
+
) {
|
|
554
|
+
gaps.push({
|
|
555
|
+
code: "remediation_write_access_missing",
|
|
556
|
+
title: "The in-cluster Runner is read-only",
|
|
557
|
+
description:
|
|
558
|
+
"The Kubernetes agent was installed without write access, so kubectl changes would be refused by the cluster.",
|
|
559
|
+
nextStep: REMEDIATION_WRITE_ACCESS_NEXT_STEP,
|
|
560
|
+
blocks: "remediation",
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
if (!isInvestigationEnabled) {
|
|
567
|
+
gaps.push({
|
|
568
|
+
code: "investigation_disabled",
|
|
569
|
+
title: "AI investigation is turned off for this cluster",
|
|
570
|
+
description:
|
|
571
|
+
"OneUptime AI will investigate incidents and alerts on this cluster with OneUptime data only — it will not run kubectl.",
|
|
572
|
+
nextStep:
|
|
573
|
+
'Turn on "Let AI investigate with kubectl" on the cluster\'s AI page.',
|
|
574
|
+
blocks: "investigation",
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (remediationMode === KubernetesAiRemediationMode.Disabled) {
|
|
579
|
+
gaps.push({
|
|
580
|
+
code: "remediation_disabled",
|
|
581
|
+
title: "AI remediation is turned off for this cluster",
|
|
582
|
+
description:
|
|
583
|
+
"OneUptime AI will diagnose but never propose or apply a fix on this cluster.",
|
|
584
|
+
nextStep:
|
|
585
|
+
'Set "AI remediation" to "Ask for approval", "Automatic" or "Bypass approval" on the cluster\'s AI page.',
|
|
586
|
+
blocks: "remediation",
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (!gates.isAiEnabled) {
|
|
591
|
+
gaps.push({
|
|
592
|
+
code: "project_ai_disabled",
|
|
593
|
+
title: "AI is disabled for this project",
|
|
594
|
+
description: "OneUptime AI is switched off at the project level.",
|
|
595
|
+
nextStep: "Enable AI under Project Settings → AI.",
|
|
596
|
+
blocks: "both",
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
if (!gates.hasLlmProvider) {
|
|
601
|
+
gaps.push({
|
|
602
|
+
code: "llm_provider_missing",
|
|
603
|
+
title: "No AI provider is configured",
|
|
604
|
+
description:
|
|
605
|
+
"OneUptime AI needs an LLM provider to investigate or remediate anything.",
|
|
606
|
+
nextStep:
|
|
607
|
+
"Add a provider under Project Settings → AI → LLM Providers (or use OneUptime AI credits).",
|
|
608
|
+
blocks: "both",
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (!gates.isAutoRemediationEnabled) {
|
|
613
|
+
gaps.push({
|
|
614
|
+
code: "project_auto_remediation_disabled",
|
|
615
|
+
title: "Auto-remediation is disabled for this project",
|
|
616
|
+
description:
|
|
617
|
+
"The project-level auto-remediation kill switch is off, so no AI fix can be proposed or run.",
|
|
618
|
+
nextStep: "Enable auto-remediation under Project Settings → AI.",
|
|
619
|
+
blocks: "remediation",
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (!gates.isAiCommandExecutionEnabled) {
|
|
624
|
+
gaps.push({
|
|
625
|
+
code: "project_ai_command_execution_disabled",
|
|
626
|
+
title: "AI command execution is not enabled for this project",
|
|
627
|
+
description:
|
|
628
|
+
"AI-composed commands (including kubectl fixes) never run in a project that has not opted in.",
|
|
629
|
+
nextStep:
|
|
630
|
+
'Turn on "Enable AI Command Execution" under Project Settings → AI.',
|
|
631
|
+
blocks: "remediation",
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const blocksInvestigation: boolean = gaps.some(
|
|
636
|
+
(gap: KubernetesAiAccessGap) => {
|
|
637
|
+
return gap.blocks === "investigation" || gap.blocks === "both";
|
|
638
|
+
},
|
|
639
|
+
);
|
|
640
|
+
const blocksRemediation: boolean = gaps.some(
|
|
641
|
+
(gap: KubernetesAiAccessGap) => {
|
|
642
|
+
return gap.blocks === "remediation" || gap.blocks === "both";
|
|
643
|
+
},
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
return {
|
|
647
|
+
clusterId: cluster.id!.toString(),
|
|
648
|
+
clusterName: cluster.name || cluster.clusterIdentifier || "cluster",
|
|
649
|
+
clusterIdentifier: cluster.clusterIdentifier,
|
|
650
|
+
runner: runnerSummary,
|
|
651
|
+
accessMethod,
|
|
652
|
+
credentialId,
|
|
653
|
+
credentialName,
|
|
654
|
+
kubectlAllowlist: this.normalizeAllowlist(
|
|
655
|
+
cluster.aiKubectlCommandAllowlist,
|
|
656
|
+
),
|
|
657
|
+
isInvestigationEnabled,
|
|
658
|
+
isInvestigationReady: isInvestigationEnabled && !blocksInvestigation,
|
|
659
|
+
remediationMode,
|
|
660
|
+
isRemediationReady:
|
|
661
|
+
remediationMode !== KubernetesAiRemediationMode.Disabled &&
|
|
662
|
+
!blocksRemediation,
|
|
663
|
+
gaps,
|
|
664
|
+
lastVerifiedAt: cluster.aiAccessLastVerifiedAt
|
|
665
|
+
? OneUptimeDate.toString(cluster.aiAccessLastVerifiedAt)
|
|
666
|
+
: undefined,
|
|
667
|
+
lastError: cluster.aiAccessLastError || undefined,
|
|
668
|
+
evaluatedAt: OneUptimeDate.toString(OneUptimeDate.getCurrentDate()),
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/*
|
|
673
|
+
* Is this Runner online right now? One rule for readiness and for
|
|
674
|
+
* registration: it heartbeated within the alive window (RunnerLiveStatus)
|
|
675
|
+
* AND it has not signed off since. A Runner signs off on a clean shutdown
|
|
676
|
+
* (/runner-ingest/disconnect), which is what a helm uninstall or turning
|
|
677
|
+
* aiAccess off does to the agent's Runner pod — it must read offline at
|
|
678
|
+
* once, not stay "Connected" until its last heartbeat ages out while
|
|
679
|
+
* investigations queue kubectl it will never claim. A freshly created
|
|
680
|
+
* row is Disconnected with no heartbeat yet: that is "never connected",
|
|
681
|
+
* not a sign-off.
|
|
682
|
+
*/
|
|
683
|
+
private getRunnerPresence(runner: Runner): RunnerPresence {
|
|
684
|
+
const liveStatus: RunnerLiveStatus = getRunnerLiveStatus(runner.lastAlive);
|
|
685
|
+
|
|
686
|
+
const isSignedOff: boolean =
|
|
687
|
+
runner.connectionStatus === RunnerConnectionStatus.Disconnected &&
|
|
688
|
+
liveStatus !== RunnerLiveStatus.NeverConnected;
|
|
689
|
+
|
|
690
|
+
return {
|
|
691
|
+
liveStatus,
|
|
692
|
+
isSignedOff,
|
|
693
|
+
isOnline: liveStatus === RunnerLiveStatus.Connected && !isSignedOff,
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
private getRunnerOfflineGap(data: {
|
|
698
|
+
runner: Runner;
|
|
699
|
+
presence: RunnerPresence;
|
|
700
|
+
// The Runner is a Kubernetes agent's in-cluster Runner.
|
|
701
|
+
isAgentRunner: boolean;
|
|
702
|
+
}): KubernetesAiAccessGap {
|
|
703
|
+
const { runner, presence, isAgentRunner } = data;
|
|
704
|
+
const lastAliveText: string = runner.lastAlive
|
|
705
|
+
? OneUptimeDate.getDateAsFormattedString(runner.lastAlive)
|
|
706
|
+
: "an unknown time";
|
|
707
|
+
|
|
708
|
+
if (presence.isSignedOff) {
|
|
709
|
+
return {
|
|
710
|
+
code: "runner_offline",
|
|
711
|
+
title: isAgentRunner
|
|
712
|
+
? "The in-cluster Runner signed off"
|
|
713
|
+
: "The Runner signed off",
|
|
714
|
+
description: isAgentRunner
|
|
715
|
+
? `Runner "${runner.name}" shut down cleanly after its last heartbeat at ${lastAliveText}: the Kubernetes agent was uninstalled or upgraded with aiAccess turned off, or its Runner pod is being replaced.`
|
|
716
|
+
: `Runner "${runner.name}" shut down cleanly after its last heartbeat at ${lastAliveText}: its container was stopped.`,
|
|
717
|
+
nextStep: isAgentRunner
|
|
718
|
+
? "If the agent was uninstalled or aiAccess was turned off on purpose, clear the Runner on the cluster's AI page. Otherwise upgrade the agent with --set aiAccess.enabled=true and the Runner reconnects within a minute."
|
|
719
|
+
: "Start the Runner container again, or bind another Runner on the cluster's AI page.",
|
|
720
|
+
blocks: "both",
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const isNeverConnected: boolean =
|
|
725
|
+
presence.liveStatus === RunnerLiveStatus.NeverConnected;
|
|
726
|
+
|
|
727
|
+
return {
|
|
728
|
+
code: "runner_offline",
|
|
729
|
+
title: isNeverConnected
|
|
730
|
+
? "The Runner has never connected"
|
|
731
|
+
: "The Runner is offline",
|
|
732
|
+
description: isNeverConnected
|
|
733
|
+
? `Runner "${runner.name}" exists but has not reported in yet.`
|
|
734
|
+
: `Runner "${runner.name}" last reported in at ${lastAliveText}.`,
|
|
735
|
+
nextStep: isAgentRunner
|
|
736
|
+
? "Check the in-cluster Runner pod: kubectl get pods -n <agent-namespace> -l component=ai-runner, then its logs. It must be able to reach your OneUptime URL."
|
|
737
|
+
: "Start the Runner container and make sure it can reach your OneUptime URL.",
|
|
738
|
+
blocks: "both",
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/*
|
|
743
|
+
* The gap for an offline agent Runner of this cluster that holds more than
|
|
744
|
+
* the agent defaults, or null when it holds nothing extra. Such a row is
|
|
745
|
+
* refused on every registration that cannot present its current key — and
|
|
746
|
+
* a restarted pod never can, it keeps the key in memory only — so the
|
|
747
|
+
* Runner will not come back until an operator removes what it holds.
|
|
748
|
+
* Never throws: a failed lookup falls back to the ordinary offline gap.
|
|
749
|
+
*/
|
|
750
|
+
private async getAgentRunnerHoldingsGap(data: {
|
|
751
|
+
runner: Runner;
|
|
752
|
+
cluster: KubernetesCluster;
|
|
753
|
+
}): Promise<KubernetesAiAccessGap | null> {
|
|
754
|
+
let holdings: Array<AgentRunnerHolding> = [];
|
|
755
|
+
|
|
756
|
+
try {
|
|
757
|
+
holdings = await this.getRunnerHoldingsBeyondDefaults({
|
|
758
|
+
runner: data.runner,
|
|
759
|
+
clusterId: data.cluster.id!,
|
|
760
|
+
projectId: data.cluster.projectId!,
|
|
761
|
+
});
|
|
762
|
+
} catch (error) {
|
|
763
|
+
logger.error(
|
|
764
|
+
`KubernetesClusterAiAccess: could not read what the agent Runner of cluster ${data.cluster.id?.toString()} holds: ${error}`,
|
|
765
|
+
);
|
|
766
|
+
return null;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
if (holdings.length === 0) {
|
|
770
|
+
return null;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return {
|
|
774
|
+
code: "runner_offline",
|
|
775
|
+
title: "The in-cluster Runner cannot re-register until it holds less",
|
|
776
|
+
description: `Runner "${data.runner.name}" is offline, and it holds more than an in-cluster Runner's defaults (${this.describeHoldings(
|
|
777
|
+
holdings,
|
|
778
|
+
)}). A restarted in-cluster Runner cannot prove it is the same Runner, so every registration it attempts is refused and it will not reconnect on its own.`,
|
|
779
|
+
/*
|
|
780
|
+
* Removing what it holds comes first: the Runner row survives, so the
|
|
781
|
+
* cluster stays bound to it and it reconnects on its next retry.
|
|
782
|
+
* Deleting it works too, but only with the second step — the fresh
|
|
783
|
+
* Runner is not bound to this cluster until someone selects it.
|
|
784
|
+
*/
|
|
785
|
+
nextStep: `Under Project Settings → Runners, on Runner "${data.runner.name}": ${this.describeHoldingRemedies(
|
|
786
|
+
holdings,
|
|
787
|
+
)}. The in-cluster Runner then reconnects on its next retry, still bound to this cluster. Or delete the Runner and, once the in-cluster Runner registers a fresh one (within a minute), select it on the cluster's AI page as its Runner. ${getDeletedAgentRunnerRebindNote()}`,
|
|
788
|
+
blocks: "both",
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
private describeHoldings(holdings: Array<AgentRunnerHolding>): string {
|
|
793
|
+
return holdings
|
|
794
|
+
.map((holding: AgentRunnerHolding) => {
|
|
795
|
+
return holding.description;
|
|
796
|
+
})
|
|
797
|
+
.join("; ");
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
private describeHoldingRemedies(holdings: Array<AgentRunnerHolding>): string {
|
|
801
|
+
return holdings
|
|
802
|
+
.map((holding: AgentRunnerHolding) => {
|
|
803
|
+
return holding.remedy;
|
|
804
|
+
})
|
|
805
|
+
.join("; ");
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/*
|
|
809
|
+
* The gap for a cluster with no Runner bound. What to do depends on
|
|
810
|
+
* whether this cluster's in-cluster Runner is already registered: a
|
|
811
|
+
* registering Runner never binds a cluster whose AI access was configured
|
|
812
|
+
* before (an operator cleared the binding, or the Runner it was bound to
|
|
813
|
+
* was deleted), so re-running the helm upgrade would change nothing —
|
|
814
|
+
* selecting the Runner on the AI page is the step that works. Never
|
|
815
|
+
* throws: a failed lookup falls back to the install instruction.
|
|
816
|
+
*/
|
|
817
|
+
private async getNoRunnerBoundGap(
|
|
818
|
+
cluster: KubernetesCluster,
|
|
819
|
+
): Promise<KubernetesAiAccessGap> {
|
|
820
|
+
const agentRunner: Runner | null =
|
|
821
|
+
await this.findAgentRunnerForCluster(cluster);
|
|
822
|
+
|
|
823
|
+
if (agentRunner) {
|
|
824
|
+
const isOnline: boolean = this.getRunnerPresence(agentRunner).isOnline;
|
|
825
|
+
|
|
826
|
+
return {
|
|
827
|
+
code: "no_runner_bound",
|
|
828
|
+
title: "The in-cluster Runner is installed but not selected",
|
|
829
|
+
description: `Runner "${agentRunner.name}", this cluster's in-cluster Runner, is registered${
|
|
830
|
+
isOnline ? " and online" : " but not online right now"
|
|
831
|
+
}, but no Runner is bound to this cluster, so OneUptime AI does not use it. A registering Runner never re-binds a cluster that had a Runner bound before (or already ran kubectl) — its binding was cleared by an operator, or the Runner it was bound to was deleted.`,
|
|
832
|
+
nextStep: `Select the kubernetes-agent Runner "${agentRunner.name}" on the cluster's AI page as its Runner (leave the credential empty). No helm change is needed.`,
|
|
833
|
+
blocks: "both",
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
return {
|
|
838
|
+
code: "no_runner_bound",
|
|
839
|
+
title: "No Runner can reach this cluster",
|
|
840
|
+
description:
|
|
841
|
+
"OneUptime AI runs kubectl through a Runner. None is bound to this cluster yet.",
|
|
842
|
+
nextStep:
|
|
843
|
+
"Upgrade the Kubernetes agent with --set aiAccess.enabled=true to install an in-cluster Runner (one command), or bind an existing Runner and a Kubernetes credential on the cluster's AI page.",
|
|
844
|
+
blocks: "both",
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/*
|
|
849
|
+
* This cluster's agent Runner row, by its (bounded) name, matched
|
|
850
|
+
* case-insensitively like registration matches it. A row whose posture
|
|
851
|
+
* names a DIFFERENT cluster is not this cluster's agent (a shortened
|
|
852
|
+
* name's hash collision, or a posture rewritten on heartbeat).
|
|
853
|
+
*/
|
|
854
|
+
private async findAgentRunnerForCluster(
|
|
855
|
+
cluster: KubernetesCluster,
|
|
856
|
+
): Promise<Runner | null> {
|
|
857
|
+
const clusterIdentifier: string = (cluster.clusterIdentifier || "").trim();
|
|
858
|
+
|
|
859
|
+
if (!clusterIdentifier || !cluster.projectId) {
|
|
860
|
+
return null;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
try {
|
|
864
|
+
const runner: Runner | null = await RunnerService.findOneBy({
|
|
865
|
+
query: {
|
|
866
|
+
projectId: cluster.projectId,
|
|
867
|
+
name: QueryHelper.findWithSameText(
|
|
868
|
+
getKubernetesAgentRunnerNameForCluster(clusterIdentifier),
|
|
869
|
+
),
|
|
870
|
+
},
|
|
871
|
+
select: {
|
|
872
|
+
_id: true,
|
|
873
|
+
name: true,
|
|
874
|
+
lastAlive: true,
|
|
875
|
+
connectionStatus: true,
|
|
876
|
+
hostInfo: true,
|
|
877
|
+
},
|
|
878
|
+
props: { isRoot: true },
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
if (
|
|
882
|
+
!runner ||
|
|
883
|
+
this.postureNamesAnotherCluster({ runner, clusterIdentifier })
|
|
884
|
+
) {
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
return runner;
|
|
889
|
+
} catch (error) {
|
|
890
|
+
logger.error(
|
|
891
|
+
`KubernetesClusterAiAccess: could not look up the agent Runner of cluster ${cluster.id?.toString()}: ${error}`,
|
|
892
|
+
);
|
|
893
|
+
return null;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// The Runner's reported posture names a cluster, and it is not this one.
|
|
898
|
+
private postureNamesAnotherCluster(data: {
|
|
899
|
+
runner: Runner;
|
|
900
|
+
clusterIdentifier: string;
|
|
901
|
+
}): boolean {
|
|
902
|
+
const reported: string =
|
|
903
|
+
parseKubernetesRunnerPosture(
|
|
904
|
+
data.runner.hostInfo,
|
|
905
|
+
)?.clusterIdentifier?.trim() || "";
|
|
906
|
+
|
|
907
|
+
return (
|
|
908
|
+
reported.length > 0 &&
|
|
909
|
+
!isSameKubernetesClusterIdentifier(reported, data.clusterIdentifier)
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/*
|
|
914
|
+
* ------------------------------------------------------------------
|
|
915
|
+
* Subject → clusters
|
|
916
|
+
* ------------------------------------------------------------------
|
|
917
|
+
*/
|
|
918
|
+
|
|
919
|
+
/*
|
|
920
|
+
* The clusters an incident or alert is about: the ones linked on the
|
|
921
|
+
* subject (SeriesResourceLinker attaches them from the monitor's series
|
|
922
|
+
* labels at creation), falling back to the cluster a Kubernetes monitor
|
|
923
|
+
* step names when the linker had nothing to work with (a manually
|
|
924
|
+
* declared incident, or one whose linking found nothing).
|
|
925
|
+
*
|
|
926
|
+
* The fallback matches identifiers case-insensitively, like
|
|
927
|
+
* MonitorResourceContext and SeriesResourceLinker do for the same input:
|
|
928
|
+
* a step identifier is typed by a person (or Terraform) and may differ in
|
|
929
|
+
* case from the ingest-stamped cluster row, which keeps the casing it was
|
|
930
|
+
* first seen with. The (projectId, clusterIdentifier) uniqueness guard is
|
|
931
|
+
* itself case-insensitive, so this can never match extra rows.
|
|
932
|
+
*/
|
|
933
|
+
@CaptureSpan()
|
|
934
|
+
public async getClustersForSubject(data: {
|
|
935
|
+
projectId: ObjectID;
|
|
936
|
+
incidentId?: ObjectID | undefined;
|
|
937
|
+
alertId?: ObjectID | undefined;
|
|
938
|
+
}): Promise<Array<KubernetesCluster>> {
|
|
939
|
+
const clusterIds: Set<string> = new Set<string>();
|
|
940
|
+
const monitorIds: Array<ObjectID> = [];
|
|
941
|
+
|
|
942
|
+
if (data.alertId) {
|
|
943
|
+
const alert: Alert | null = await AlertService.findOneBy({
|
|
944
|
+
query: { _id: data.alertId.toString(), projectId: data.projectId },
|
|
945
|
+
select: {
|
|
946
|
+
_id: true,
|
|
947
|
+
monitorId: true,
|
|
948
|
+
kubernetesClusters: { _id: true },
|
|
949
|
+
},
|
|
950
|
+
props: { isRoot: true },
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
for (const cluster of alert?.kubernetesClusters || []) {
|
|
954
|
+
if (cluster.id) {
|
|
955
|
+
clusterIds.add(cluster.id.toString());
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (alert?.monitorId) {
|
|
960
|
+
monitorIds.push(alert.monitorId);
|
|
961
|
+
}
|
|
962
|
+
} else if (data.incidentId) {
|
|
963
|
+
const incident: Incident | null = await IncidentService.findOneBy({
|
|
964
|
+
query: { _id: data.incidentId.toString(), projectId: data.projectId },
|
|
965
|
+
select: {
|
|
966
|
+
_id: true,
|
|
967
|
+
monitors: { _id: true },
|
|
968
|
+
kubernetesClusters: { _id: true },
|
|
969
|
+
},
|
|
970
|
+
props: { isRoot: true },
|
|
971
|
+
});
|
|
972
|
+
|
|
973
|
+
for (const cluster of incident?.kubernetesClusters || []) {
|
|
974
|
+
if (cluster.id) {
|
|
975
|
+
clusterIds.add(cluster.id.toString());
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
for (const monitor of incident?.monitors || []) {
|
|
980
|
+
if (monitor.id) {
|
|
981
|
+
monitorIds.push(monitor.id);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
const clusterIdentifiers: Set<string> = new Set<string>();
|
|
987
|
+
|
|
988
|
+
if (clusterIds.size === 0 && monitorIds.length > 0) {
|
|
989
|
+
const monitors: Array<Monitor> = await MonitorService.findBy({
|
|
990
|
+
query: {
|
|
991
|
+
_id: QueryHelper.any(monitorIds.slice(0, MAX_CLUSTERS_PER_SUBJECT)),
|
|
992
|
+
projectId: data.projectId,
|
|
993
|
+
},
|
|
994
|
+
select: { _id: true, monitorSteps: true },
|
|
995
|
+
limit: MAX_CLUSTERS_PER_SUBJECT,
|
|
996
|
+
skip: 0,
|
|
997
|
+
props: { isRoot: true },
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
for (const monitor of monitors) {
|
|
1001
|
+
const steps: Array<MonitorStep> =
|
|
1002
|
+
monitor.monitorSteps?.data?.monitorStepsInstanceArray || [];
|
|
1003
|
+
for (const step of steps) {
|
|
1004
|
+
const identifier: string = normalizeKubernetesClusterIdentifier(
|
|
1005
|
+
step.data?.kubernetesMonitor?.clusterIdentifier,
|
|
1006
|
+
);
|
|
1007
|
+
if (identifier) {
|
|
1008
|
+
clusterIdentifiers.add(identifier);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
if (clusterIds.size === 0 && clusterIdentifiers.size === 0) {
|
|
1015
|
+
return [];
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
const clusters: Array<KubernetesCluster> =
|
|
1019
|
+
await KubernetesClusterService.findBy({
|
|
1020
|
+
query: {
|
|
1021
|
+
projectId: data.projectId,
|
|
1022
|
+
isArchived: false,
|
|
1023
|
+
...(clusterIds.size > 0
|
|
1024
|
+
? {
|
|
1025
|
+
_id: QueryHelper.any(
|
|
1026
|
+
Array.from(clusterIds)
|
|
1027
|
+
.slice(0, MAX_CLUSTERS_PER_SUBJECT)
|
|
1028
|
+
.map((id: string) => {
|
|
1029
|
+
return new ObjectID(id);
|
|
1030
|
+
}),
|
|
1031
|
+
),
|
|
1032
|
+
}
|
|
1033
|
+
: {
|
|
1034
|
+
clusterIdentifier: QueryHelper.findWithSameTextAnyOf(
|
|
1035
|
+
Array.from(clusterIdentifiers).slice(
|
|
1036
|
+
0,
|
|
1037
|
+
MAX_CLUSTERS_PER_SUBJECT,
|
|
1038
|
+
),
|
|
1039
|
+
),
|
|
1040
|
+
}),
|
|
1041
|
+
},
|
|
1042
|
+
select: CLUSTER_AI_ACCESS_SELECT,
|
|
1043
|
+
limit: MAX_CLUSTERS_PER_SUBJECT,
|
|
1044
|
+
skip: 0,
|
|
1045
|
+
props: { isRoot: true },
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
return clusters;
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
@CaptureSpan()
|
|
1052
|
+
public async getStatusesForSubject(data: {
|
|
1053
|
+
projectId: ObjectID;
|
|
1054
|
+
incidentId?: ObjectID | undefined;
|
|
1055
|
+
alertId?: ObjectID | undefined;
|
|
1056
|
+
}): Promise<Array<KubernetesClusterAiAccessStatus>> {
|
|
1057
|
+
const clusters: Array<KubernetesCluster> =
|
|
1058
|
+
await this.getClustersForSubject(data);
|
|
1059
|
+
|
|
1060
|
+
if (clusters.length === 0) {
|
|
1061
|
+
return [];
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
const gates: KubernetesClusterAiAccessProjectGates =
|
|
1065
|
+
await this.getProjectGates(data.projectId);
|
|
1066
|
+
|
|
1067
|
+
const statuses: Array<KubernetesClusterAiAccessStatus> = [];
|
|
1068
|
+
|
|
1069
|
+
for (const cluster of clusters) {
|
|
1070
|
+
statuses.push(await this.getStatusForClusterModel({ cluster, gates }));
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
return statuses;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
/*
|
|
1077
|
+
* ------------------------------------------------------------------
|
|
1078
|
+
* In-cluster Runner registration
|
|
1079
|
+
* ------------------------------------------------------------------
|
|
1080
|
+
*/
|
|
1081
|
+
|
|
1082
|
+
/*
|
|
1083
|
+
* Called by the Runner the kubernetes-agent chart deploys. Authenticated
|
|
1084
|
+
* by the project's telemetry ingestion key (the same key the agent ships
|
|
1085
|
+
* telemetry with), so installing the chart with one extra flag is the
|
|
1086
|
+
* whole setup. Idempotent per (project, cluster): a restarted pod gets a
|
|
1087
|
+
* fresh key for the same Runner row and the same cluster binding.
|
|
1088
|
+
*
|
|
1089
|
+
* The ingestion key is a credential every collector, CI job and sidecar
|
|
1090
|
+
* in the project holds, so what it can do here is deliberately narrow:
|
|
1091
|
+
*
|
|
1092
|
+
* - It can mint an agent Runner row for a cluster (bounded per project,
|
|
1093
|
+
* see MAX_KUBERNETES_AGENT_RUNNERS_PER_PROJECT).
|
|
1094
|
+
* - It can re-key that row only while the row is OFFLINE (no heartbeat
|
|
1095
|
+
* for RUNNER_ALIVE_WINDOW_IN_MINUTES, or signed off) or when the
|
|
1096
|
+
* request presents the row's current key. A Runner the dashboard shows
|
|
1097
|
+
* as connected can therefore not be taken over by anyone but itself:
|
|
1098
|
+
* whoever holds a leaked ingestion key cannot lock the live pod out and
|
|
1099
|
+
* be handed the identity every kubectl job for that cluster is targeted
|
|
1100
|
+
* at.
|
|
1101
|
+
*
|
|
1102
|
+
* The trade-off is availability after a pod restart: the new pod holds
|
|
1103
|
+
* no key, so it is refused until the previous instance's last heartbeat
|
|
1104
|
+
* ages out of the alive window (it retries with backoff and then
|
|
1105
|
+
* succeeds). A pod that shuts down cleanly avoids the wait by calling
|
|
1106
|
+
* /runner-ingest/disconnect on the way out, which marks the row offline
|
|
1107
|
+
* at once.
|
|
1108
|
+
* - Without that proof it can re-key only a row that holds NOTHING beyond
|
|
1109
|
+
* the agent defaults. An offline window is predictable (every helm
|
|
1110
|
+
* upgrade opens one), so a row an operator gave more — a credential or
|
|
1111
|
+
* secret assigned to it, runbooks or code fixes turned on, or another
|
|
1112
|
+
* cluster bound to it — is refused: re-keying it would hand all of that
|
|
1113
|
+
* to whoever holds the ingestion key. The operator removes the extras
|
|
1114
|
+
* (the Runner then comes back still bound), or deletes the Runner, lets
|
|
1115
|
+
* the agent register a fresh one and selects it on the cluster's AI
|
|
1116
|
+
* page — a deleted Runner's cluster is left unbound, see below.
|
|
1117
|
+
* - It finds the row it may re-key by this cluster's agent Runner NAME
|
|
1118
|
+
* only — never by the posture a Runner reports about itself — and only
|
|
1119
|
+
* binds a cluster that never had a Runner bound: a cluster whose
|
|
1120
|
+
* binding was cleared (or whose Runner was deleted) stays unbound, and
|
|
1121
|
+
* an operator's AI settings are never overwritten, only the chart's
|
|
1122
|
+
* defaults fill in a cluster nobody configured.
|
|
1123
|
+
* - Every refusal is a KubernetesAgentRegistrationRefusedException whose
|
|
1124
|
+
* reason tells the Runner whether waiting helps.
|
|
1125
|
+
*
|
|
1126
|
+
* Every bind and key rotation is written to the cluster's feed — saying
|
|
1127
|
+
* whether the rotation was proven (the current key was presented) or not
|
|
1128
|
+
* — so an operator can see when the identity behind AI access changed.
|
|
1129
|
+
*/
|
|
1130
|
+
@CaptureSpan()
|
|
1131
|
+
public async registerKubernetesAgentRunner(data: {
|
|
1132
|
+
projectId: ObjectID;
|
|
1133
|
+
clusterIdentifier: string;
|
|
1134
|
+
agentVersion?: string | undefined;
|
|
1135
|
+
posture: KubernetesRunnerPosture;
|
|
1136
|
+
/*
|
|
1137
|
+
* The key the Runner currently holds, when it still has one. Proves
|
|
1138
|
+
* continuity, so a live Runner may rotate its own key without waiting
|
|
1139
|
+
* to be considered offline first.
|
|
1140
|
+
*/
|
|
1141
|
+
previousRunnerKey?: string | undefined;
|
|
1142
|
+
}): Promise<RegisterKubernetesAgentRunnerResult> {
|
|
1143
|
+
const clusterIdentifier: string = (data.clusterIdentifier || "").trim();
|
|
1144
|
+
|
|
1145
|
+
if (!clusterIdentifier) {
|
|
1146
|
+
throw new BadDataException("clusterName is required.");
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
/*
|
|
1150
|
+
* A name longer than the cluster identifier column can never become a
|
|
1151
|
+
* cluster row, so say so before anything is written. Every shorter
|
|
1152
|
+
* name works: the Runner row's name is bounded separately.
|
|
1153
|
+
*/
|
|
1154
|
+
if (clusterIdentifier.length > MAX_KUBERNETES_CLUSTER_IDENTIFIER_LENGTH) {
|
|
1155
|
+
throw new BadDataException(
|
|
1156
|
+
`clusterName is ${clusterIdentifier.length} characters long; OneUptime accepts Kubernetes cluster names of up to ${MAX_KUBERNETES_CLUSTER_IDENTIFIER_LENGTH} characters. Set a shorter clusterName on the Kubernetes agent chart.`,
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
const agentRunnerName: string =
|
|
1161
|
+
getKubernetesAgentRunnerNameForCluster(clusterIdentifier);
|
|
1162
|
+
|
|
1163
|
+
const created: KubernetesCluster =
|
|
1164
|
+
await KubernetesClusterService.findOrCreateByClusterIdentifier({
|
|
1165
|
+
projectId: data.projectId,
|
|
1166
|
+
clusterIdentifier,
|
|
1167
|
+
});
|
|
1168
|
+
|
|
1169
|
+
const cluster: KubernetesCluster | null =
|
|
1170
|
+
await KubernetesClusterService.findOneBy({
|
|
1171
|
+
query: { _id: created.id!.toString(), projectId: data.projectId },
|
|
1172
|
+
select: CLUSTER_AI_ACCESS_SELECT,
|
|
1173
|
+
props: { isRoot: true },
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
if (!cluster) {
|
|
1177
|
+
throw new BadDataException("Cluster could not be resolved.");
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/*
|
|
1181
|
+
* What the row stores: the body's posture, with the cluster and
|
|
1182
|
+
* in-cluster set by the service. A re-key may fill in the write scope
|
|
1183
|
+
* an older agent image left out of its body (keepStoredWriteScope).
|
|
1184
|
+
*/
|
|
1185
|
+
let posture: KubernetesRunnerPosture = {
|
|
1186
|
+
...data.posture,
|
|
1187
|
+
clusterIdentifier,
|
|
1188
|
+
inCluster: true,
|
|
1189
|
+
};
|
|
1190
|
+
|
|
1191
|
+
// What a re-key decision reads off an existing agent Runner row.
|
|
1192
|
+
const runnerSelect: Record<string, boolean> = {
|
|
1193
|
+
_id: true,
|
|
1194
|
+
name: true,
|
|
1195
|
+
hostInfo: true,
|
|
1196
|
+
lastAlive: true,
|
|
1197
|
+
connectionStatus: true,
|
|
1198
|
+
key: true,
|
|
1199
|
+
canRunRunbooks: true,
|
|
1200
|
+
canRunCodeFixTasks: true,
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
/*
|
|
1204
|
+
* The agent Runner row for this cluster, bound or not — found by its
|
|
1205
|
+
* NAME, never by the posture a Runner reports about itself. The name is
|
|
1206
|
+
* the server-owned marker: only registration writes it, and
|
|
1207
|
+
* RunnerService refuses to let anyone else rename a row into or out of
|
|
1208
|
+
* it, whereas a posture is whatever the Runner last heartbeated. So a
|
|
1209
|
+
* bound Runner that merely CLAIMS to be this cluster's in-cluster Runner
|
|
1210
|
+
* (a renamed agent row, another cluster's pod, anything) is never
|
|
1211
|
+
* re-keyed here. Matched case-insensitively, like the cluster row
|
|
1212
|
+
* itself, so a chart that registers "Prod-US" finds the row a "prod-us"
|
|
1213
|
+
* registration made.
|
|
1214
|
+
*/
|
|
1215
|
+
let runner: Runner | null = await RunnerService.findOneBy({
|
|
1216
|
+
query: {
|
|
1217
|
+
projectId: data.projectId,
|
|
1218
|
+
name: QueryHelper.findWithSameText(agentRunnerName),
|
|
1219
|
+
},
|
|
1220
|
+
select: runnerSelect,
|
|
1221
|
+
props: { isRoot: true },
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
/*
|
|
1225
|
+
* The name is this cluster's, but the row says it is another cluster's
|
|
1226
|
+
* agent (a shortened name whose hash collided, or a posture rewritten on
|
|
1227
|
+
* heartbeat). Re-keying it would move another cluster's identity here,
|
|
1228
|
+
* and creating a second row would trip the unique name — so refuse, and
|
|
1229
|
+
* say which row is in the way. The instruction comes first: the Runner
|
|
1230
|
+
* logs only the start of a long reason.
|
|
1231
|
+
*/
|
|
1232
|
+
if (
|
|
1233
|
+
runner &&
|
|
1234
|
+
this.postureNamesAnotherCluster({ runner, clusterIdentifier })
|
|
1235
|
+
) {
|
|
1236
|
+
logger.warn(
|
|
1237
|
+
`KubernetesClusterAiAccess: refused to register an agent Runner for cluster "${clusterIdentifier}" in project ${data.projectId.toString()}: Runner "${runner.name}" (${runner.id?.toString()}) reports that it belongs to a different cluster.`,
|
|
1238
|
+
);
|
|
1239
|
+
|
|
1240
|
+
throw new KubernetesAgentRegistrationRefusedException({
|
|
1241
|
+
reason: "runner_belongs_to_another_cluster",
|
|
1242
|
+
message: `Delete Runner "${runner.name}" under Project Settings → Runners (an in-cluster Runner cannot be renamed) and, once the agent registers a fresh one on its next retry, select it on the AI page of cluster "${clusterIdentifier}" as its Runner. That Runner already exists in this project but reports that it is the in-cluster Runner of a different cluster, so it was not reused for cluster "${clusterIdentifier}". ${getDeletedAgentRunnerRebindNote()}`,
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
const agentRunnerExistedBefore: boolean = Boolean(runner);
|
|
1247
|
+
const runnerKey: string = ObjectID.generate().toString();
|
|
1248
|
+
let reKeyAdmission: AgentRunnerReKeyAdmission | undefined = undefined;
|
|
1249
|
+
|
|
1250
|
+
if (runner) {
|
|
1251
|
+
reKeyAdmission = await this.assertRunnerMayBeReKeyed({
|
|
1252
|
+
runner,
|
|
1253
|
+
clusterIdentifier,
|
|
1254
|
+
clusterId: cluster.id!,
|
|
1255
|
+
projectId: data.projectId,
|
|
1256
|
+
previousRunnerKey: data.previousRunnerKey,
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
posture = this.keepStoredWriteScope({
|
|
1260
|
+
reported: posture,
|
|
1261
|
+
stored: parseKubernetesRunnerPosture(runner.hostInfo),
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
await RunnerService.updateOneById({
|
|
1265
|
+
id: runner.id!,
|
|
1266
|
+
data: {
|
|
1267
|
+
key: runnerKey,
|
|
1268
|
+
hostInfo: { kubernetes: posture as unknown as JSONObject },
|
|
1269
|
+
lastAlive: OneUptimeDate.getCurrentDate(),
|
|
1270
|
+
connectionStatus: RunnerConnectionStatus.Connected,
|
|
1271
|
+
...(data.agentVersion
|
|
1272
|
+
? { agentVersion: new Version(data.agentVersion) }
|
|
1273
|
+
: {}),
|
|
1274
|
+
} as never,
|
|
1275
|
+
props: { isRoot: true },
|
|
1276
|
+
});
|
|
1277
|
+
} else {
|
|
1278
|
+
await this.assertAgentRunnerMayBeCreated({
|
|
1279
|
+
projectId: data.projectId,
|
|
1280
|
+
clusterIdentifier,
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
const newRunner: Runner = new Runner();
|
|
1284
|
+
newRunner.projectId = data.projectId;
|
|
1285
|
+
newRunner.name = agentRunnerName;
|
|
1286
|
+
newRunner.description = `In-cluster Runner installed by the OneUptime Kubernetes agent chart (aiAccess.enabled=true). Gives OneUptime AI kubectl access to cluster "${clusterIdentifier}". It never runs runbooks or code fixes.`;
|
|
1287
|
+
newRunner.key = runnerKey;
|
|
1288
|
+
newRunner.canRunRunbooks = false;
|
|
1289
|
+
newRunner.canRunCodeFixTasks = false;
|
|
1290
|
+
newRunner.canRunAiCommands = true;
|
|
1291
|
+
newRunner.hostInfo = { kubernetes: posture as unknown as JSONObject };
|
|
1292
|
+
newRunner.lastAlive = OneUptimeDate.getCurrentDate();
|
|
1293
|
+
newRunner.connectionStatus = RunnerConnectionStatus.Connected;
|
|
1294
|
+
if (data.agentVersion) {
|
|
1295
|
+
newRunner.agentVersion = new Version(data.agentVersion);
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
runner = await RunnerService.create({
|
|
1299
|
+
data: newRunner,
|
|
1300
|
+
props: { isRoot: true },
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
/*
|
|
1305
|
+
* Binding. The dashboard is the control plane:
|
|
1306
|
+
*
|
|
1307
|
+
* - A cluster already bound to this cluster's agent row stays so; only
|
|
1308
|
+
* the key (and posture) rotated.
|
|
1309
|
+
* - A cluster bound to any other Runner is left alone. A bound id whose
|
|
1310
|
+
* Runner row is gone is only the race with a Runner delete (the
|
|
1311
|
+
* foreign key is about to clear it) and is treated like the unbound
|
|
1312
|
+
* case below: never re-bound.
|
|
1313
|
+
* - A cluster with no Runner bound:
|
|
1314
|
+
* - that had a Runner bound before (aiAccessRunnerBoundAt, stamped
|
|
1315
|
+
* on every bind and never cleared) or has run kubectl (a recorded
|
|
1316
|
+
* verification or error) stays unbound and keeps its switches.
|
|
1317
|
+
* That covers both ways a binding goes away: an operator cleared
|
|
1318
|
+
* it on the AI page, or the bound Runner was DELETED — the
|
|
1319
|
+
* binding's foreign key is ON DELETE SET NULL, so Postgres clears
|
|
1320
|
+
* aiAccessRunnerId in the same statement. Deleting the Runner is
|
|
1321
|
+
* how an operator revokes or resets it, so it must not come back
|
|
1322
|
+
* bound a few heartbeats later. The AI page tells the operator to
|
|
1323
|
+
* select the Runner if they want it back.
|
|
1324
|
+
* - that never had a Runner bound but was configured on the AI page
|
|
1325
|
+
* (aiAccessConfiguredAt: an operator chose a mode, the switches
|
|
1326
|
+
* or an allowlist BEFORE installing the chart) is bound to this
|
|
1327
|
+
* agent Runner with every setting left exactly as the operator
|
|
1328
|
+
* chose it — the one-flag install still just works.
|
|
1329
|
+
* - that nobody ever configured takes the chart's intent:
|
|
1330
|
+
* investigation on, remediation "ask for approval" when the chart
|
|
1331
|
+
* also granted write RBAC (a mode already on the row is kept).
|
|
1332
|
+
*/
|
|
1333
|
+
let bindingState: KubernetesAgentRunnerBindingState;
|
|
1334
|
+
let appliedRemediationMode: KubernetesAiRemediationMode | undefined =
|
|
1335
|
+
undefined;
|
|
1336
|
+
|
|
1337
|
+
const hadRunnerBound: boolean = Boolean(cluster.aiAccessRunnerBoundAt);
|
|
1338
|
+
const hasCommandHistory: boolean =
|
|
1339
|
+
Boolean(cluster.aiAccessLastVerifiedAt) ||
|
|
1340
|
+
Boolean(cluster.aiAccessLastError);
|
|
1341
|
+
|
|
1342
|
+
if (cluster.aiAccessRunnerId) {
|
|
1343
|
+
/*
|
|
1344
|
+
* A row this registration just created cannot already be bound, even
|
|
1345
|
+
* if the ids happened to compare equal.
|
|
1346
|
+
*/
|
|
1347
|
+
const isBoundToThisRow: boolean =
|
|
1348
|
+
agentRunnerExistedBefore &&
|
|
1349
|
+
cluster.aiAccessRunnerId.toString() === runner.id!.toString();
|
|
1350
|
+
|
|
1351
|
+
if (isBoundToThisRow) {
|
|
1352
|
+
bindingState = "already_bound";
|
|
1353
|
+
} else {
|
|
1354
|
+
const boundRunner: Runner | null = await RunnerService.findOneBy({
|
|
1355
|
+
query: {
|
|
1356
|
+
_id: cluster.aiAccessRunnerId.toString(),
|
|
1357
|
+
projectId: data.projectId,
|
|
1358
|
+
},
|
|
1359
|
+
select: { _id: true },
|
|
1360
|
+
props: { isRoot: true },
|
|
1361
|
+
});
|
|
1362
|
+
|
|
1363
|
+
bindingState = boundRunner
|
|
1364
|
+
? "bound_to_other_runner"
|
|
1365
|
+
: "left_unbound_by_operator";
|
|
1366
|
+
}
|
|
1367
|
+
} else if (hadRunnerBound || hasCommandHistory) {
|
|
1368
|
+
bindingState = "left_unbound_by_operator";
|
|
1369
|
+
} else if (cluster.aiAccessConfiguredAt) {
|
|
1370
|
+
bindingState = "bound_keeping_operator_settings";
|
|
1371
|
+
|
|
1372
|
+
// Only the binding: not one of the operator's switches moves.
|
|
1373
|
+
await KubernetesClusterService.updateOneById({
|
|
1374
|
+
id: cluster.id!,
|
|
1375
|
+
data: {
|
|
1376
|
+
aiAccessRunnerId: runner.id!,
|
|
1377
|
+
aiAccessRunnerBoundAt: OneUptimeDate.getCurrentDate(),
|
|
1378
|
+
} as never,
|
|
1379
|
+
props: { isRoot: true },
|
|
1380
|
+
});
|
|
1381
|
+
} else {
|
|
1382
|
+
bindingState = "first_bind";
|
|
1383
|
+
|
|
1384
|
+
appliedRemediationMode = this.getFirstBindRemediationMode({
|
|
1385
|
+
currentMode: cluster.aiRemediationMode,
|
|
1386
|
+
allowWrites: posture.allowWrites === true,
|
|
1387
|
+
});
|
|
1388
|
+
|
|
1389
|
+
await KubernetesClusterService.updateOneById({
|
|
1390
|
+
id: cluster.id!,
|
|
1391
|
+
data: {
|
|
1392
|
+
aiAccessRunnerId: runner.id!,
|
|
1393
|
+
aiAccessCredentialId: null,
|
|
1394
|
+
isAiInvestigationEnabled: true,
|
|
1395
|
+
aiRemediationMode: appliedRemediationMode,
|
|
1396
|
+
aiAccessConfiguredAt: OneUptimeDate.getCurrentDate(),
|
|
1397
|
+
aiAccessRunnerBoundAt: OneUptimeDate.getCurrentDate(),
|
|
1398
|
+
} as never,
|
|
1399
|
+
props: { isRoot: true },
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
if (bindingState === "left_unbound_by_operator") {
|
|
1404
|
+
logger.info(
|
|
1405
|
+
`KubernetesClusterAiAccess: cluster "${clusterIdentifier}" in project ${data.projectId.toString()} had a Runner bound before (or has run kubectl) but has none bound now — the binding was cleared, or its Runner was deleted; the in-cluster Runner registered without re-binding it.`,
|
|
1406
|
+
);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
const isBoundToCluster: boolean =
|
|
1410
|
+
bindingState === "first_bind" ||
|
|
1411
|
+
bindingState === "bound_keeping_operator_settings" ||
|
|
1412
|
+
bindingState === "already_bound";
|
|
1413
|
+
|
|
1414
|
+
await this.writeRegistrationFeedItem({
|
|
1415
|
+
cluster,
|
|
1416
|
+
runner,
|
|
1417
|
+
posture,
|
|
1418
|
+
bindingState,
|
|
1419
|
+
agentRunnerExistedBefore,
|
|
1420
|
+
appliedRemediationMode,
|
|
1421
|
+
reKeyAdmission,
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
return {
|
|
1425
|
+
runnerId: runner.id!,
|
|
1426
|
+
runnerKey,
|
|
1427
|
+
clusterId: cluster.id!,
|
|
1428
|
+
isBoundToCluster,
|
|
1429
|
+
isFirstBind: bindingState === "first_bind",
|
|
1430
|
+
bindingState,
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
/*
|
|
1435
|
+
* The posture a re-keyed agent Runner row stores. An agent image older
|
|
1436
|
+
* than the write scope's registration fields sends neither
|
|
1437
|
+
* writeNamespaces nor podNamespace when it registers (its heartbeat
|
|
1438
|
+
* does). Storing that body as it is would clear the scope the row
|
|
1439
|
+
* already holds, and every server-side check — the enqueue chokepoint,
|
|
1440
|
+
* the remediation toolkit, the cluster's AI page — would read the Runner
|
|
1441
|
+
* as cluster-wide with no namespace of its own until its first
|
|
1442
|
+
* heartbeat. So a field the body leaves out keeps the value the row last
|
|
1443
|
+
* reported; a field it sends wins, an explicit empty list (cluster-wide)
|
|
1444
|
+
* included. Keeping a stored value can only make the server refuse more
|
|
1445
|
+
* than the Runner would, never less, until the heartbeat corrects it.
|
|
1446
|
+
*/
|
|
1447
|
+
public keepStoredWriteScope(data: {
|
|
1448
|
+
reported: KubernetesRunnerPosture;
|
|
1449
|
+
stored: KubernetesRunnerPosture | undefined;
|
|
1450
|
+
}): KubernetesRunnerPosture {
|
|
1451
|
+
const { reported, stored } = data;
|
|
1452
|
+
const posture: KubernetesRunnerPosture = { ...reported };
|
|
1453
|
+
|
|
1454
|
+
if (
|
|
1455
|
+
reported.writeNamespaces === undefined &&
|
|
1456
|
+
stored?.writeNamespaces !== undefined
|
|
1457
|
+
) {
|
|
1458
|
+
posture.writeNamespaces = [...stored.writeNamespaces];
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
if (reported.podNamespace === undefined && stored?.podNamespace) {
|
|
1462
|
+
posture.podNamespace = stored.podNamespace;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
return posture;
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
/*
|
|
1469
|
+
* The remediation mode a first bind leaves on the cluster. The chart's
|
|
1470
|
+
* intent fills in only what the operator has not set: a mode still at
|
|
1471
|
+
* its default (Disabled) becomes "ask for approval" when the chart
|
|
1472
|
+
* granted write RBAC, and stays Disabled otherwise. A mode an operator
|
|
1473
|
+
* already chose on the AI page — even before any Runner was bound — is
|
|
1474
|
+
* kept exactly as chosen: a pod registering never downgrades or upgrades
|
|
1475
|
+
* a dashboard decision. (A write mode on a read-only Runner is not a
|
|
1476
|
+
* hazard; it reads as a remediation_write_access_missing gap and nothing
|
|
1477
|
+
* runs.)
|
|
1478
|
+
*/
|
|
1479
|
+
public getFirstBindRemediationMode(data: {
|
|
1480
|
+
currentMode: unknown;
|
|
1481
|
+
allowWrites: boolean;
|
|
1482
|
+
}): KubernetesAiRemediationMode {
|
|
1483
|
+
const currentMode: KubernetesAiRemediationMode =
|
|
1484
|
+
this.normalizeRemediationMode(data.currentMode);
|
|
1485
|
+
|
|
1486
|
+
if (currentMode !== KubernetesAiRemediationMode.Disabled) {
|
|
1487
|
+
return currentMode;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
return data.allowWrites
|
|
1491
|
+
? KubernetesAiRemediationMode.RequireApproval
|
|
1492
|
+
: KubernetesAiRemediationMode.Disabled;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
/*
|
|
1496
|
+
* A registration may replace the key of an existing agent Runner row only
|
|
1497
|
+
* when it can be the same Runner coming back:
|
|
1498
|
+
*
|
|
1499
|
+
* - it presents the row's current key (proof of continuity) — always
|
|
1500
|
+
* admitted; or
|
|
1501
|
+
* - the row is offline or signed off (what a restarted pod looks like)
|
|
1502
|
+
* AND holds nothing beyond the agent defaults. Anyone holding the
|
|
1503
|
+
* ingestion key looks exactly like a restarted pod, and the offline
|
|
1504
|
+
* window is predictable (every helm upgrade opens one), so a row an
|
|
1505
|
+
* operator gave more — credentials or secrets assigned to it, runbooks
|
|
1506
|
+
* or code fixes turned on, another cluster bound to it — must not be
|
|
1507
|
+
* handed to a registration that cannot prove it is the same Runner.
|
|
1508
|
+
*
|
|
1509
|
+
* Refused otherwise — WITHOUT rotating anything — so an ingestion key
|
|
1510
|
+
* alone can never evict a live Runner or inherit what an operator
|
|
1511
|
+
* entrusted to one. Nothing about the current key is disclosed on
|
|
1512
|
+
* refusal. Returns how the re-key was admitted, for the feed.
|
|
1513
|
+
*/
|
|
1514
|
+
private async assertRunnerMayBeReKeyed(data: {
|
|
1515
|
+
runner: Runner;
|
|
1516
|
+
clusterIdentifier: string;
|
|
1517
|
+
clusterId: ObjectID;
|
|
1518
|
+
projectId: ObjectID;
|
|
1519
|
+
previousRunnerKey?: string | undefined;
|
|
1520
|
+
}): Promise<AgentRunnerReKeyAdmission> {
|
|
1521
|
+
const provesContinuity: boolean = Boolean(
|
|
1522
|
+
data.previousRunnerKey &&
|
|
1523
|
+
data.runner.key &&
|
|
1524
|
+
this.isSameSecret(data.previousRunnerKey, data.runner.key),
|
|
1525
|
+
);
|
|
1526
|
+
|
|
1527
|
+
if (provesContinuity) {
|
|
1528
|
+
return "continuity";
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
/*
|
|
1532
|
+
* connectionStatus is written by the Runner's own sign-off
|
|
1533
|
+
* (/runner-ingest/disconnect): a pod that shut down cleanly is offline
|
|
1534
|
+
* the moment it says so, whatever its last heartbeat's age.
|
|
1535
|
+
*/
|
|
1536
|
+
const presence: RunnerPresence = this.getRunnerPresence(data.runner);
|
|
1537
|
+
|
|
1538
|
+
if (presence.isOnline) {
|
|
1539
|
+
logger.warn(
|
|
1540
|
+
`KubernetesClusterAiAccess: refused to re-key Runner "${
|
|
1541
|
+
data.runner.name
|
|
1542
|
+
}" (${data.runner.id?.toString()}) for cluster "${
|
|
1543
|
+
data.clusterIdentifier
|
|
1544
|
+
}" in project ${data.projectId.toString()}: the Runner is online and the registration did not present its current key.`,
|
|
1545
|
+
);
|
|
1546
|
+
|
|
1547
|
+
/*
|
|
1548
|
+
* The one refusal that clears on its own: the previous instance's
|
|
1549
|
+
* last heartbeat ages out of the alive window. Say when, so the
|
|
1550
|
+
* Runner retries then instead of guessing.
|
|
1551
|
+
*/
|
|
1552
|
+
throw new KubernetesAgentRegistrationRefusedException({
|
|
1553
|
+
reason: "previous_instance_online",
|
|
1554
|
+
retryAfterSeconds: getPreviousInstanceRetryAfterSeconds({
|
|
1555
|
+
lastAlive: data.runner.lastAlive,
|
|
1556
|
+
}),
|
|
1557
|
+
message: `Runner "${data.runner.name}" for cluster "${data.clusterIdentifier}" is online, so this registration was refused and its key was not changed. A registration replaces a Runner only once it has been offline for ${RUNNER_ALIVE_WINDOW_IN_MINUTES} minutes, or when the request presents that Runner's current key as previousRunnerKey. If the Runner pod just restarted, keep retrying: it is admitted as soon as the previous instance's last heartbeat is older than ${RUNNER_ALIVE_WINDOW_IN_MINUTES} minutes (a pod that shuts down cleanly can call /runner-ingest/disconnect to skip the wait).`,
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
const holdings: Array<AgentRunnerHolding> =
|
|
1562
|
+
await this.getRunnerHoldingsBeyondDefaults({
|
|
1563
|
+
runner: data.runner,
|
|
1564
|
+
clusterId: data.clusterId,
|
|
1565
|
+
projectId: data.projectId,
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1568
|
+
if (holdings.length > 0) {
|
|
1569
|
+
logger.warn(
|
|
1570
|
+
`KubernetesClusterAiAccess: refused to re-key Runner "${
|
|
1571
|
+
data.runner.name
|
|
1572
|
+
}" (${data.runner.id?.toString()}) for cluster "${
|
|
1573
|
+
data.clusterIdentifier
|
|
1574
|
+
}" in project ${data.projectId.toString()} without proof of continuity: it holds more than an in-cluster Runner's defaults (${this.describeHoldings(
|
|
1575
|
+
holdings,
|
|
1576
|
+
)}).`,
|
|
1577
|
+
);
|
|
1578
|
+
|
|
1579
|
+
/*
|
|
1580
|
+
* The instruction leads: the Runner logs only the start of a long
|
|
1581
|
+
* reason, and this refusal never clears until someone acts on it.
|
|
1582
|
+
*/
|
|
1583
|
+
throw new KubernetesAgentRegistrationRefusedException({
|
|
1584
|
+
reason: "runner_holds_more_than_defaults",
|
|
1585
|
+
message: `Under Project Settings → Runners, on Runner "${data.runner.name}": ${this.describeHoldingRemedies(
|
|
1586
|
+
holdings,
|
|
1587
|
+
)} — or delete the Runner and, once the agent registers a fresh one on its next retry, select it on the cluster's AI page as its Runner. It is offline, but it holds more than an in-cluster Runner's defaults (${this.describeHoldings(
|
|
1588
|
+
holdings,
|
|
1589
|
+
)}), so a registration for cluster "${data.clusterIdentifier}" that does not present its current key may not take it over, and its key was not changed. ${getDeletedAgentRunnerRebindNote()}`,
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
if (presence.isSignedOff) {
|
|
1594
|
+
return "signed_off";
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
return presence.liveStatus === RunnerLiveStatus.NeverConnected
|
|
1598
|
+
? "never_connected"
|
|
1599
|
+
: "offline";
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
/*
|
|
1603
|
+
* What an agent Runner row holds beyond what registration gave it, one
|
|
1604
|
+
* entry per kind with what removes it. Registration creates the row with
|
|
1605
|
+
* kubectl access only (no runbooks, no code fixes, nothing assigned) bound
|
|
1606
|
+
* to its own cluster; anything else was entrusted to the row by an
|
|
1607
|
+
* operator. "Runs Runbooks" is read the way the claim path reads it: on
|
|
1608
|
+
* unless explicitly false.
|
|
1609
|
+
*/
|
|
1610
|
+
private async getRunnerHoldingsBeyondDefaults(data: {
|
|
1611
|
+
runner: Runner;
|
|
1612
|
+
clusterId: ObjectID;
|
|
1613
|
+
projectId: ObjectID;
|
|
1614
|
+
}): Promise<Array<AgentRunnerHolding>> {
|
|
1615
|
+
const holdings: Array<AgentRunnerHolding> = [];
|
|
1616
|
+
|
|
1617
|
+
if (data.runner.canRunRunbooks !== false) {
|
|
1618
|
+
holdings.push({
|
|
1619
|
+
description: '"Runs Runbooks" is on',
|
|
1620
|
+
remedy: 'turn off "Runs Runbooks"',
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
if (data.runner.canRunCodeFixTasks === true) {
|
|
1625
|
+
holdings.push({
|
|
1626
|
+
description: '"Runs AI Code Fixes" is on',
|
|
1627
|
+
remedy: 'turn off "Runs AI Code Fixes"',
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
const assignedCredentials: number = (
|
|
1632
|
+
await RunbookCredentialService.countBy({
|
|
1633
|
+
query: {
|
|
1634
|
+
projectId: data.projectId,
|
|
1635
|
+
runners: QueryHelper.inRelationArray([data.runner.id!]),
|
|
1636
|
+
},
|
|
1637
|
+
props: { isRoot: true },
|
|
1638
|
+
})
|
|
1639
|
+
).toNumber();
|
|
1640
|
+
|
|
1641
|
+
if (assignedCredentials > 0) {
|
|
1642
|
+
holdings.push({
|
|
1643
|
+
description: `${assignedCredentials} Runner credential(s) assigned`,
|
|
1644
|
+
remedy: `unassign its ${assignedCredentials} Runner credential(s)`,
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
const assignedSecrets: number = (
|
|
1649
|
+
await RunbookSecretService.countBy({
|
|
1650
|
+
query: {
|
|
1651
|
+
projectId: data.projectId,
|
|
1652
|
+
runners: QueryHelper.inRelationArray([data.runner.id!]),
|
|
1653
|
+
},
|
|
1654
|
+
props: { isRoot: true },
|
|
1655
|
+
})
|
|
1656
|
+
).toNumber();
|
|
1657
|
+
|
|
1658
|
+
if (assignedSecrets > 0) {
|
|
1659
|
+
holdings.push({
|
|
1660
|
+
description: `${assignedSecrets} runbook secret(s) assigned`,
|
|
1661
|
+
remedy: `unassign its ${assignedSecrets} runbook secret(s)`,
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
const otherBoundClusters: number = (
|
|
1666
|
+
await KubernetesClusterService.countBy({
|
|
1667
|
+
query: {
|
|
1668
|
+
projectId: data.projectId,
|
|
1669
|
+
aiAccessRunnerId: data.runner.id!,
|
|
1670
|
+
_id: QueryHelper.notEquals(data.clusterId),
|
|
1671
|
+
},
|
|
1672
|
+
props: { isRoot: true },
|
|
1673
|
+
})
|
|
1674
|
+
).toNumber();
|
|
1675
|
+
|
|
1676
|
+
if (otherBoundClusters > 0) {
|
|
1677
|
+
holdings.push({
|
|
1678
|
+
description: `the AI access Runner of ${otherBoundClusters} other cluster(s)`,
|
|
1679
|
+
remedy: `select another Runner on the AI page of the ${otherBoundClusters} other cluster(s) bound to it`,
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
return holdings;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
/*
|
|
1687
|
+
* Bounds what an ingestion key can mint. Counted on the Runner rows
|
|
1688
|
+
* themselves (name prefix) so every registration path shares one brake.
|
|
1689
|
+
*/
|
|
1690
|
+
private async assertAgentRunnerMayBeCreated(data: {
|
|
1691
|
+
projectId: ObjectID;
|
|
1692
|
+
clusterIdentifier: string;
|
|
1693
|
+
}): Promise<void> {
|
|
1694
|
+
const agentRunnerNameFilter: unknown = QueryHelper.startsWith(
|
|
1695
|
+
`${KUBERNETES_AGENT_RUNNER_NAME_PREFIX}/`,
|
|
1696
|
+
);
|
|
1697
|
+
|
|
1698
|
+
const totalAgentRunners: number = (
|
|
1699
|
+
await RunnerService.countBy({
|
|
1700
|
+
query: {
|
|
1701
|
+
projectId: data.projectId,
|
|
1702
|
+
name: agentRunnerNameFilter as never,
|
|
1703
|
+
},
|
|
1704
|
+
props: { isRoot: true },
|
|
1705
|
+
})
|
|
1706
|
+
).toNumber();
|
|
1707
|
+
|
|
1708
|
+
if (totalAgentRunners >= MAX_KUBERNETES_AGENT_RUNNERS_PER_PROJECT) {
|
|
1709
|
+
logger.warn(
|
|
1710
|
+
`KubernetesClusterAiAccess: refused to create an agent Runner for cluster "${data.clusterIdentifier}" in project ${data.projectId.toString()}: the project already has ${totalAgentRunners} agent Runners (limit ${MAX_KUBERNETES_AGENT_RUNNERS_PER_PROJECT}).`,
|
|
1711
|
+
);
|
|
1712
|
+
|
|
1713
|
+
throw new TooManyRequestsException(
|
|
1714
|
+
`This project already has ${totalAgentRunners} in-cluster Runners, which is its limit (${MAX_KUBERNETES_AGENT_RUNNERS_PER_PROJECT}). Delete the Runners of clusters that no longer exist under Project Settings → Runners, then retry.`,
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
const createdInLastHour: number = (
|
|
1719
|
+
await RunnerService.countBy({
|
|
1720
|
+
query: {
|
|
1721
|
+
projectId: data.projectId,
|
|
1722
|
+
name: agentRunnerNameFilter as never,
|
|
1723
|
+
createdAt: QueryHelper.greaterThan(OneUptimeDate.getSomeHoursAgo(1)),
|
|
1724
|
+
},
|
|
1725
|
+
props: { isRoot: true },
|
|
1726
|
+
})
|
|
1727
|
+
).toNumber();
|
|
1728
|
+
|
|
1729
|
+
if (
|
|
1730
|
+
createdInLastHour >= MAX_NEW_KUBERNETES_AGENT_RUNNERS_PER_PROJECT_PER_HOUR
|
|
1731
|
+
) {
|
|
1732
|
+
logger.warn(
|
|
1733
|
+
`KubernetesClusterAiAccess: refused to create an agent Runner for cluster "${data.clusterIdentifier}" in project ${data.projectId.toString()}: ${createdInLastHour} agent Runners were created in the last hour (limit ${MAX_NEW_KUBERNETES_AGENT_RUNNERS_PER_PROJECT_PER_HOUR}).`,
|
|
1734
|
+
);
|
|
1735
|
+
|
|
1736
|
+
throw new TooManyRequestsException(
|
|
1737
|
+
`${createdInLastHour} in-cluster Runners were registered in this project in the last hour, which is its limit (${MAX_NEW_KUBERNETES_AGENT_RUNNERS_PER_PROJECT_PER_HOUR}). The Runner will be admitted when it retries later.`,
|
|
1738
|
+
);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
|
|
1742
|
+
// Best-effort: the feed must never fail the registration it describes.
|
|
1743
|
+
private async writeRegistrationFeedItem(data: {
|
|
1744
|
+
cluster: KubernetesCluster;
|
|
1745
|
+
runner: Runner;
|
|
1746
|
+
posture: KubernetesRunnerPosture;
|
|
1747
|
+
bindingState: KubernetesAgentRunnerBindingState;
|
|
1748
|
+
agentRunnerExistedBefore: boolean;
|
|
1749
|
+
// The mode a first bind left on the cluster; undefined otherwise.
|
|
1750
|
+
appliedRemediationMode?: KubernetesAiRemediationMode | undefined;
|
|
1751
|
+
// How an existing row's re-key was admitted; undefined for a new row.
|
|
1752
|
+
reKeyAdmission?: AgentRunnerReKeyAdmission | undefined;
|
|
1753
|
+
}): Promise<void> {
|
|
1754
|
+
try {
|
|
1755
|
+
const runnerName: string = data.runner.name || "kubernetes-agent";
|
|
1756
|
+
const writes: string = data.posture.allowWrites
|
|
1757
|
+
? "writes allowed"
|
|
1758
|
+
: "read-only";
|
|
1759
|
+
const registered: string = this.describeRegistration(
|
|
1760
|
+
data.agentRunnerExistedBefore ? data.reKeyAdmission : undefined,
|
|
1761
|
+
);
|
|
1762
|
+
|
|
1763
|
+
let feedInfoInMarkdown: string;
|
|
1764
|
+
let displayColor: typeof Blue500 = Blue500;
|
|
1765
|
+
|
|
1766
|
+
if (data.bindingState === "first_bind") {
|
|
1767
|
+
displayColor = Green500;
|
|
1768
|
+
feedInfoInMarkdown = `🤖 The in-cluster Runner **${runnerName}** registered from the Kubernetes agent chart (${writes}) and was bound as this cluster's AI access Runner. AI investigation is on; AI remediation is ${this.describeRemediationMode(
|
|
1769
|
+
data.appliedRemediationMode,
|
|
1770
|
+
)}.`;
|
|
1771
|
+
} else if (data.bindingState === "bound_keeping_operator_settings") {
|
|
1772
|
+
displayColor = Green500;
|
|
1773
|
+
feedInfoInMarkdown = `🤖 The in-cluster Runner **${runnerName}** ${registered} from the Kubernetes agent chart (${writes}) and was bound as this cluster's AI access Runner. AI access had already been configured on the cluster's AI page, so no AI setting was changed: AI investigation with kubectl is ${
|
|
1774
|
+
data.cluster.isAiInvestigationEnabled === true ? "on" : "off"
|
|
1775
|
+
} and AI remediation is ${this.describeRemediationMode(
|
|
1776
|
+
data.cluster.aiRemediationMode,
|
|
1777
|
+
)}, as an operator chose.`;
|
|
1778
|
+
} else if (data.bindingState === "left_unbound_by_operator") {
|
|
1779
|
+
feedInfoInMarkdown = `🤖 The in-cluster Runner **${runnerName}** ${registered} (${writes}), but no Runner is bound to this cluster although one was before (or AI already ran kubectl here) — the binding was cleared by an operator, or the Runner it was bound to was deleted — so it was left unbound and no AI switch was changed. Select **${runnerName}** on the cluster's AI page to use it.`;
|
|
1780
|
+
} else if (data.bindingState === "bound_to_other_runner") {
|
|
1781
|
+
feedInfoInMarkdown = `🤖 The in-cluster Runner **${runnerName}** ${registered} (${writes}), but this cluster is bound to a different Runner in the dashboard, so it was not used.`;
|
|
1782
|
+
} else if (data.reKeyAdmission === "continuity") {
|
|
1783
|
+
feedInfoInMarkdown = `🔑 The in-cluster Runner **${runnerName}** re-registered and its key was rotated (${writes}). It presented its current key, so this is the same Runner.`;
|
|
1784
|
+
} else {
|
|
1785
|
+
/*
|
|
1786
|
+
* A re-key without proof is what a restarted pod looks like — and
|
|
1787
|
+
* also what anyone holding the telemetry ingestion key looks like.
|
|
1788
|
+
* Said plainly, so an unexpected one stands out from a restart.
|
|
1789
|
+
*/
|
|
1790
|
+
displayColor = Yellow500;
|
|
1791
|
+
feedInfoInMarkdown = `🔑 The in-cluster Runner **${runnerName}** ${registered} (${writes}). That is expected when the agent's Runner pod restarts or is upgraded. If it did not, someone holding this project's telemetry ingestion key registered in its place: disable that key and delete this Runner so the agent registers afresh.`;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
await KubernetesClusterFeedService.createKubernetesClusterFeedItem({
|
|
1795
|
+
kubernetesClusterId: data.cluster.id!,
|
|
1796
|
+
projectId: data.cluster.projectId!,
|
|
1797
|
+
kubernetesClusterFeedEventType:
|
|
1798
|
+
KubernetesClusterFeedEventType.KubernetesClusterUpdated,
|
|
1799
|
+
displayColor,
|
|
1800
|
+
feedInfoInMarkdown,
|
|
1801
|
+
moreInformationInMarkdown: [
|
|
1802
|
+
`**Runner**: ${runnerName} (${data.runner.id?.toString() || "?"})`,
|
|
1803
|
+
`**Cluster identifier**: \`${data.posture.clusterIdentifier || ""}\``,
|
|
1804
|
+
`**kubectl**: ${data.posture.kubectlVersion || "not detected"}`,
|
|
1805
|
+
`**Agent chart**: ${data.posture.agentChartVersion || "unknown"}`,
|
|
1806
|
+
].join("\n\n"),
|
|
1807
|
+
});
|
|
1808
|
+
} catch (error) {
|
|
1809
|
+
logger.error(
|
|
1810
|
+
`KubernetesClusterAiAccess: could not write the registration feed item for cluster ${data.cluster.id?.toString()}: ${error}`,
|
|
1811
|
+
);
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
/*
|
|
1816
|
+
* How the Runner came to hold its new key, in the feed's words: a new
|
|
1817
|
+
* row, a re-key the Runner proved (it presented its current key), or a
|
|
1818
|
+
* re-key without that proof — which names why it was admitted.
|
|
1819
|
+
*/
|
|
1820
|
+
private describeRegistration(
|
|
1821
|
+
reKeyAdmission: AgentRunnerReKeyAdmission | undefined,
|
|
1822
|
+
): string {
|
|
1823
|
+
switch (reKeyAdmission) {
|
|
1824
|
+
case undefined:
|
|
1825
|
+
return "registered";
|
|
1826
|
+
case "continuity":
|
|
1827
|
+
return "re-registered (its key was rotated; it presented its current key)";
|
|
1828
|
+
case "signed_off":
|
|
1829
|
+
return "re-registered and its key was rotated WITHOUT proof of continuity (it did not present its previous key; admitted because the previous instance had signed off)";
|
|
1830
|
+
case "never_connected":
|
|
1831
|
+
return "re-registered and its key was rotated WITHOUT proof of continuity (it did not present its previous key; admitted because the Runner had never connected)";
|
|
1832
|
+
default:
|
|
1833
|
+
return "re-registered and its key was rotated WITHOUT proof of continuity (it did not present its previous key; admitted because the previous instance had stopped heartbeating)";
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
// The AI page's label for a mode, quoted the way the page shows it.
|
|
1838
|
+
private describeRemediationMode(mode: unknown): string {
|
|
1839
|
+
switch (this.normalizeRemediationMode(mode)) {
|
|
1840
|
+
case KubernetesAiRemediationMode.RequireApproval:
|
|
1841
|
+
return '"Ask for approval"';
|
|
1842
|
+
case KubernetesAiRemediationMode.Automatic:
|
|
1843
|
+
return '"Automatic"';
|
|
1844
|
+
case KubernetesAiRemediationMode.BypassApproval:
|
|
1845
|
+
return '"Bypass approval"';
|
|
1846
|
+
default:
|
|
1847
|
+
return "Disabled";
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
// Constant-time comparison so a refusal cannot leak the key byte by byte.
|
|
1852
|
+
private isSameSecret(presented: string, current: string): boolean {
|
|
1853
|
+
const presentedBuffer: Buffer = Buffer.from(presented, "utf8");
|
|
1854
|
+
const currentBuffer: Buffer = Buffer.from(current, "utf8");
|
|
1855
|
+
|
|
1856
|
+
if (presentedBuffer.length !== currentBuffer.length) {
|
|
1857
|
+
return false;
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
return crypto.timingSafeEqual(presentedBuffer, currentBuffer);
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
/*
|
|
1864
|
+
* ------------------------------------------------------------------
|
|
1865
|
+
* Bookkeeping
|
|
1866
|
+
* ------------------------------------------------------------------
|
|
1867
|
+
*/
|
|
1868
|
+
|
|
1869
|
+
// Best-effort: the AI page shows "last verified" / "last error" from this.
|
|
1870
|
+
@CaptureSpan()
|
|
1871
|
+
public async recordCommandOutcome(data: {
|
|
1872
|
+
clusterId: ObjectID;
|
|
1873
|
+
succeeded: boolean;
|
|
1874
|
+
errorMessage?: string | undefined;
|
|
1875
|
+
}): Promise<void> {
|
|
1876
|
+
try {
|
|
1877
|
+
await KubernetesClusterService.updateOneById({
|
|
1878
|
+
id: data.clusterId,
|
|
1879
|
+
data: (data.succeeded
|
|
1880
|
+
? {
|
|
1881
|
+
aiAccessLastVerifiedAt: OneUptimeDate.getCurrentDate(),
|
|
1882
|
+
aiAccessLastError: null,
|
|
1883
|
+
}
|
|
1884
|
+
: {
|
|
1885
|
+
aiAccessLastError: (data.errorMessage || "Command failed.").slice(
|
|
1886
|
+
0,
|
|
1887
|
+
2000,
|
|
1888
|
+
),
|
|
1889
|
+
}) as never,
|
|
1890
|
+
props: { isRoot: true },
|
|
1891
|
+
});
|
|
1892
|
+
} catch (error) {
|
|
1893
|
+
logger.error(
|
|
1894
|
+
`KubernetesClusterAiAccess: could not record command outcome for cluster ${data.clusterId.toString()}: ${error}`,
|
|
1895
|
+
);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
public normalizeRemediationMode(value: unknown): KubernetesAiRemediationMode {
|
|
1900
|
+
if (
|
|
1901
|
+
value === KubernetesAiRemediationMode.RequireApproval ||
|
|
1902
|
+
value === KubernetesAiRemediationMode.Automatic ||
|
|
1903
|
+
value === KubernetesAiRemediationMode.BypassApproval
|
|
1904
|
+
) {
|
|
1905
|
+
return value;
|
|
1906
|
+
}
|
|
1907
|
+
return KubernetesAiRemediationMode.Disabled;
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
/*
|
|
1911
|
+
* The allowlist column is jsonb; the dashboard may save it as an array or
|
|
1912
|
+
* as a JSON string. Anything unusable normalizes to empty — nothing
|
|
1913
|
+
* risky auto-executes.
|
|
1914
|
+
*/
|
|
1915
|
+
public normalizeAllowlist(value: unknown): Array<string> {
|
|
1916
|
+
let raw: unknown = value;
|
|
1917
|
+
|
|
1918
|
+
if (typeof raw === "string") {
|
|
1919
|
+
try {
|
|
1920
|
+
raw = JSON.parse(raw);
|
|
1921
|
+
} catch {
|
|
1922
|
+
raw = [value];
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
if (!Array.isArray(raw)) {
|
|
1927
|
+
return [];
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
return raw
|
|
1931
|
+
.filter((pattern: unknown) => {
|
|
1932
|
+
return typeof pattern === "string" && pattern.trim().length > 0;
|
|
1933
|
+
})
|
|
1934
|
+
.map((pattern: string) => {
|
|
1935
|
+
return pattern.trim();
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
const KubernetesClusterAiAccessService: KubernetesClusterAiAccessServiceClass =
|
|
1941
|
+
new KubernetesClusterAiAccessServiceClass();
|
|
1942
|
+
|
|
1943
|
+
export default KubernetesClusterAiAccessService;
|