@oneuptime/common 14.0.8 → 14.0.10
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/AutoRemediationSuggestion.ts +60 -0
- package/Models/DatabaseModels/CephCluster.ts +213 -0
- package/Models/DatabaseModels/DatabaseServer.ts +213 -0
- package/Models/DatabaseModels/DockerHost.ts +213 -0
- package/Models/DatabaseModels/DockerSwarmCluster.ts +213 -0
- package/Models/DatabaseModels/GlobalConfig.ts +1 -1
- package/Models/DatabaseModels/Host.ts +213 -0
- package/Models/DatabaseModels/Index.ts +4 -0
- package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
- package/Models/DatabaseModels/PodmanHost.ts +213 -0
- package/Models/DatabaseModels/Project.ts +55 -5
- package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
- package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
- package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
- package/Models/DatabaseModels/RunnerJob.ts +219 -0
- package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
- package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
- package/Server/API/AIChatAPI.ts +6 -3
- package/Server/API/AIInvestigationAPI.ts +17 -10
- package/Server/API/AutoRemediationAPI.ts +598 -25
- package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
- package/Server/API/MicrosoftTeamsAPI.ts +93 -14
- package/Server/API/ProxmoxResourceAPI.ts +244 -14
- package/Server/API/ResourceAiAccessAPI.ts +1669 -0
- package/Server/Infrastructure/GlobalCache.ts +32 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Infrastructure/Semaphore.ts +22 -0
- package/Server/Middleware/TelemetryIngest.ts +51 -18
- package/Server/Services/AIService.ts +70 -1
- package/Server/Services/AlertService.ts +352 -11
- package/Server/Services/AlertSeverityService.ts +65 -15
- package/Server/Services/AnalyticsDatabaseService.ts +45 -1
- package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
- package/Server/Services/AutoRemediationRuleService.ts +1 -1
- package/Server/Services/CephClusterService.ts +109 -1
- package/Server/Services/DatabaseServerService.ts +116 -1
- package/Server/Services/DockerHostService.ts +109 -1
- package/Server/Services/DockerSwarmClusterService.ts +113 -1
- package/Server/Services/HostService.ts +126 -4
- package/Server/Services/IncidentService.ts +280 -104
- package/Server/Services/IncidentSeverityService.ts +66 -15
- package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
- package/Server/Services/KubernetesAiAgentService.ts +1634 -0
- package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
- package/Server/Services/KubernetesClusterService.ts +129 -21
- package/Server/Services/MetricRecordingRuleService.ts +47 -0
- package/Server/Services/PodmanHostService.ts +109 -1
- package/Server/Services/ProjectService.ts +32 -0
- package/Server/Services/ProxmoxClusterService.ts +110 -1
- package/Server/Services/ProxmoxResourceService.ts +322 -5
- package/Server/Services/ResourceAiAccessService.ts +1439 -0
- package/Server/Services/ResourceAiAgentJobService.ts +202 -0
- package/Server/Services/ResourceAiAgentService.ts +2432 -0
- package/Server/Services/RunnerJobService.ts +852 -108
- package/Server/Services/RunnerService.ts +1 -1
- package/Server/Services/ScheduledMaintenanceService.ts +737 -1
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
- package/Server/Services/TeamComplianceSettingService.ts +1125 -9
- package/Server/Services/TelemetryUsageBillingService.ts +28 -0
- package/Server/Services/TraceRecordingRuleService.ts +33 -0
- package/Server/Services/VMwareVCenterService.ts +110 -1
- package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1475 -51
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1000 -43
- package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
- package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +577 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
- package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
- package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +873 -0
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +120 -7
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
- package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
- package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +402 -14
- package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
- package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
- package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
- package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
- package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +298 -224
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +35 -15
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +1073 -213
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
- package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
- package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
- package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
- package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
- package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
- package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
- package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
- package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
- package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
- package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
- package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
- package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
- package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
- package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
- package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +184 -19
- package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
- package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
- package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
- package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
- package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
- package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
- package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
- package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
- package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
- package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
- package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
- package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
- package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
- package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
- package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
- package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
- package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
- package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
- package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
- package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
- package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
- package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
- package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
- package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
- package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
- package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
- package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
- package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
- package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
- package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
- package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
- package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
- package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
- package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
- package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
- package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
- package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
- package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
- package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
- package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
- package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
- package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
- package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
- package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
- package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
- package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
- package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
- package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
- package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
- package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
- package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
- package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
- package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
- package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
- package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
- package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
- package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
- package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
- package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
- package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
- package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
- package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
- package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
- package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
- package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
- package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
- package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
- package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
- package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
- package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
- package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
- package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
- package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
- package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
- package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
- package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
- package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +1721 -0
- package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1002 -0
- package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
- package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
- package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
- package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
- package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
- package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
- package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
- package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
- package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
- package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
- package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
- package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
- package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
- package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
- package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
- package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
- package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
- package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
- package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
- package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
- package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
- package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
- package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
- package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
- package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
- package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
- package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
- package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
- package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
- package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
- package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
- package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
- package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
- package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
- package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
- package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
- package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
- package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
- package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
- package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
- package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +119 -4
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +196 -2
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +123 -2
- package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
- package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
- package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
- package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
- package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
- package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
- package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
- package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
- package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
- package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
- package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
- package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
- package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
- package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
- package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
- package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
- package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
- package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
- package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
- package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
- package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
- package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
- package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
- package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
- package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
- package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
- package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
- package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
- package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
- package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
- package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +153 -15
- package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
- package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
- package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
- package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
- package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
- package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
- package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
- package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
- package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
- package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
- package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
- package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
- package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
- package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +453 -0
- package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
- package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
- package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1249 -0
- package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
- package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
- package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +722 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
- package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +830 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
- package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
- package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
- package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
- package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +75 -3
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
- package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
- package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
- package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
- package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
- package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
- package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +517 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
- package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
- package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
- package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
- package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
- package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
- package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
- package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
- package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
- package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
- package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
- package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
- package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
- package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
- package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
- package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
- package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
- package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
- package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
- package/Tests/UI/Components/Input.test.tsx +80 -1
- package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
- package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
- package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
- package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
- package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
- package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
- package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
- package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
- package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
- package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
- package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
- package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
- package/Tests/UI/Components/Toggle.test.tsx +66 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
- package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
- package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
- package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
- package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
- package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
- package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
- package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
- package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
- package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
- package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
- package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
- package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
- package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
- package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
- package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
- package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
- package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
- package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
- package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
- package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
- package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
- package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
- package/Types/AI/AIRunType.ts +9 -4
- package/Types/AI/InvestigationNotStartedReason.ts +6 -0
- package/Types/AI/ResourceAiAccessApi.ts +140 -0
- package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +197 -18
- package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
- package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
- package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
- package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
- package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
- package/Types/Monitor/RumAlertTemplates.ts +351 -4
- package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
- package/Types/ResourceAiAgent/ResourceAiAccess.ts +574 -0
- package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
- package/Types/Runbook/RunbookStepType.ts +29 -0
- package/Types/Team/ComplianceNotificationChannel.ts +22 -0
- package/Types/Team/ComplianceRule.ts +434 -0
- package/Types/Team/ComplianceRuleType.ts +21 -0
- package/Types/Team/TeamComplianceStatus.ts +81 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +40 -0
- package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
- package/UI/Components/ActionButton/RowActions.tsx +238 -0
- package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
- package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
- package/UI/Components/Charts/Line/LineChart.tsx +32 -2
- package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
- package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
- package/UI/Components/Charts/Utils/XAxis.ts +75 -0
- package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
- package/UI/Components/Forms/Fields/FormField.tsx +10 -0
- package/UI/Components/Input/Input.tsx +90 -57
- package/UI/Components/List/ListRow.tsx +8 -74
- package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
- package/UI/Components/OrderedStatesList/Item.tsx +7 -72
- package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
- package/UI/Components/Table/TableBody.tsx +8 -2
- package/UI/Components/Table/TableRow.tsx +20 -140
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
- package/UI/Components/TelemetryViewer/types.ts +2 -0
- package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
- package/UI/Components/Toggle/Toggle.tsx +26 -0
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Styles/Theme.css +14 -2
- package/Utils/AI/InvestigationReport.ts +121 -15
- package/Utils/AiRemediation/CommandPolicy.ts +1 -1
- package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
- package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
- package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
- package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
- package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
- package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
- package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
- package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
- package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
- package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
- package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
- package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
- package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
- package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
- package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +219 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +219 -0
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Host.js +219 -0
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/PodmanHost.js +219 -0
- package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +56 -5
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
- package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js +219 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js +219 -0
- package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +6 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +374 -24
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
- package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
- package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
- package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +41 -19
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +59 -1
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +248 -13
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +51 -15
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +104 -0
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseServerService.js +99 -0
- package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +104 -0
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +114 -2
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +199 -72
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
- package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +104 -0
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +22 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
- package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
- package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
- package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/RunnerJobService.js +522 -97
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -1
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
- package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
- package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
- package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1056 -43
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +683 -42
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +330 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +600 -0
- package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +89 -9
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +55 -4
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +294 -15
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +183 -209
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +19 -15
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +768 -152
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
- package/build/dist/Types/AI/AIRunType.js +9 -4
- package/build/dist/Types/AI/AIRunType.js.map +1 -1
- package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
- package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
- package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +126 -45
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
- package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
- package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +275 -0
- package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
- package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRule.js +304 -0
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
- package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
- package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
- package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +36 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
- package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +69 -54
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/List/ListRow.js +4 -39
- package/build/dist/UI/Components/List/ListRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
- package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +6 -2
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +12 -75
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/Utils/AI/InvestigationReport.js +71 -11
- package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
- package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
- package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
- package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
- package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
- package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
- package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
- package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import Alert from "../../Models/DatabaseModels/Alert";
|
|
2
2
|
import Incident from "../../Models/DatabaseModels/Incident";
|
|
3
|
+
import KubernetesAiAgent from "../../Models/DatabaseModels/KubernetesAiAgent";
|
|
3
4
|
import KubernetesCluster from "../../Models/DatabaseModels/KubernetesCluster";
|
|
5
|
+
import LlmProvider from "../../Models/DatabaseModels/LlmProvider";
|
|
4
6
|
import Monitor from "../../Models/DatabaseModels/Monitor";
|
|
5
7
|
import Project from "../../Models/DatabaseModels/Project";
|
|
6
8
|
import RunbookCredential from "../../Models/DatabaseModels/RunbookCredential";
|
|
@@ -24,23 +26,33 @@ import {
|
|
|
24
26
|
getRunnerLiveStatus,
|
|
25
27
|
} from "../../Types/Runner/RunnerLiveStatus";
|
|
26
28
|
import {
|
|
29
|
+
KUBECTL_ALLOW_WRITES_ENV,
|
|
30
|
+
KUBECTL_WRITE_NAMESPACES_ENV,
|
|
27
31
|
KUBERNETES_AGENT_RUNNER_NAME_PREFIX,
|
|
32
|
+
KUBERNETES_AI_AGENT_COMPONENT,
|
|
33
|
+
KUBERNETES_AI_AGENT_DISPLAY_NAME,
|
|
34
|
+
KubernetesAgentPosture,
|
|
28
35
|
KubernetesAgentRegistrationRefusalReason,
|
|
29
36
|
KubernetesAgentRunnerBindingState,
|
|
30
37
|
KubernetesAiAccessGap,
|
|
38
|
+
KubernetesAiAccessRunnerSummary,
|
|
39
|
+
KubernetesAiAutomaticInvestigationSettings,
|
|
31
40
|
KubernetesAiRemediationMode,
|
|
32
41
|
KubernetesClusterAiAccessStatus,
|
|
33
42
|
KubernetesRunnerPosture,
|
|
34
43
|
isInClusterPostureForCluster,
|
|
35
44
|
isSameKubernetesClusterIdentifier,
|
|
36
45
|
normalizeKubernetesClusterIdentifier,
|
|
46
|
+
parseKubernetesAgentPosture,
|
|
37
47
|
parseKubernetesRunnerPosture,
|
|
38
48
|
} from "../../Types/Kubernetes/KubernetesClusterAiAccess";
|
|
39
49
|
import { KubernetesClusterFeedEventType } from "../../Models/DatabaseModels/KubernetesClusterFeed";
|
|
40
50
|
import { Blue500, Green500, Yellow500 } from "../../Types/BrandColors";
|
|
41
51
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
52
|
+
import AIService from "./AIService";
|
|
42
53
|
import AlertService from "./AlertService";
|
|
43
54
|
import IncidentService from "./IncidentService";
|
|
55
|
+
import KubernetesAiAgentService from "./KubernetesAiAgentService";
|
|
44
56
|
import KubernetesClusterFeedService from "./KubernetesClusterFeedService";
|
|
45
57
|
import KubernetesClusterService from "./KubernetesClusterService";
|
|
46
58
|
import LlmProviderService from "./LlmProviderService";
|
|
@@ -73,14 +85,22 @@ export {
|
|
|
73
85
|
* Answers, from CURRENT configuration, whether AI can reach a cluster with
|
|
74
86
|
* kubectl and what it may do there — and turns every reason it cannot into a
|
|
75
87
|
* 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,
|
|
77
|
-
* is why") and the AI runs themselves (which only ever act on a cluster
|
|
78
|
-
* module calls ready).
|
|
88
|
+
* AI agent page, the investigation panel ("we could not access the cluster,
|
|
89
|
+
* here is why") and the AI runs themselves (which only ever act on a cluster
|
|
90
|
+
* this module calls ready).
|
|
79
91
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
92
|
+
* AI reaches a cluster through ONE access target, resolved from liveness by
|
|
93
|
+
* resolveKubernetesAiAccessTarget: the cluster's Kubernetes AI agent (its
|
|
94
|
+
* own identity, a KubernetesAiAgent row), the chart's previous in-cluster
|
|
95
|
+
* Runner, or a Runner an operator bound together with a Kubernetes
|
|
96
|
+
* credential. The status, the enqueue chokepoint and the access test all
|
|
97
|
+
* ask the same function, so they never disagree about which one it is.
|
|
98
|
+
*
|
|
99
|
+
* It also still registers the previous in-cluster Runner older charts
|
|
100
|
+
* install: that Runner registers with the project's telemetry ingestion key
|
|
101
|
+
* and the cluster's name, and this module upserts its Runner row and points
|
|
102
|
+
* the cluster at it — unless the cluster's Kubernetes AI agent is online,
|
|
103
|
+
* which replaces it.
|
|
84
104
|
*/
|
|
85
105
|
|
|
86
106
|
export interface KubernetesClusterAiAccessProjectGates {
|
|
@@ -88,6 +108,20 @@ export interface KubernetesClusterAiAccessProjectGates {
|
|
|
88
108
|
isAutoRemediationEnabled: boolean;
|
|
89
109
|
isAiCommandExecutionEnabled: boolean;
|
|
90
110
|
hasLlmProvider: boolean;
|
|
111
|
+
/*
|
|
112
|
+
* Why no AI run can start for lack of AI credits (AIService.
|
|
113
|
+
* getAiBalanceBlocker), or null/absent when nothing blocks. getProjectGates
|
|
114
|
+
* always sets it; callers that build gates by hand may leave it out.
|
|
115
|
+
*/
|
|
116
|
+
aiBalanceBlocker?: string | null | undefined;
|
|
117
|
+
/*
|
|
118
|
+
* The project's automatic-investigation opt-ins, shown on the AI agent
|
|
119
|
+
* page as a line of their own (never a gap). getProjectGates always sets
|
|
120
|
+
* them; absent reads as both off.
|
|
121
|
+
*/
|
|
122
|
+
automaticInvestigation?:
|
|
123
|
+
| KubernetesAiAutomaticInvestigationSettings
|
|
124
|
+
| undefined;
|
|
91
125
|
}
|
|
92
126
|
|
|
93
127
|
export interface RegisterKubernetesAgentRunnerResult {
|
|
@@ -140,16 +174,214 @@ export const CLUSTER_AI_ACCESS_SELECT: Record<string, boolean> = {
|
|
|
140
174
|
const MAX_CLUSTERS_PER_SUBJECT: number = 10;
|
|
141
175
|
|
|
142
176
|
/*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
177
|
+
* Where every next step sends an operator: the cluster's AI agent page
|
|
178
|
+
* (AI → Agent in the cluster's side menu). Shown verbatim on incident pages
|
|
179
|
+
* and in the investigation panel too, so it always names the page rather
|
|
180
|
+
* than saying "this page".
|
|
181
|
+
*/
|
|
182
|
+
export const CLUSTER_AI_AGENT_PAGE: string =
|
|
183
|
+
"the cluster's AI agent page (AI → Agent)";
|
|
184
|
+
|
|
185
|
+
/*
|
|
186
|
+
* The one command that installs the Kubernetes AI agent (or turns it back
|
|
187
|
+
* on), exactly as the dashboard, the docs and the chart's notes print it.
|
|
188
|
+
* --reuse-values keeps everything else the release already has.
|
|
150
189
|
*/
|
|
151
|
-
export const
|
|
152
|
-
|
|
190
|
+
export const AI_AGENT_INSTALL_COMMAND: string = [
|
|
191
|
+
"helm repo update",
|
|
192
|
+
"helm upgrade kubernetes-agent oneuptime/kubernetes-agent \\",
|
|
193
|
+
" --namespace oneuptime-agent --reuse-values \\",
|
|
194
|
+
" --set aiAgent.enabled=true",
|
|
195
|
+
].join("\n");
|
|
196
|
+
|
|
197
|
+
/*
|
|
198
|
+
* How to read the agent pod's logs. The namespace is the one the agent
|
|
199
|
+
* reported (posture.podNamespace) when it is known, a placeholder otherwise.
|
|
200
|
+
*/
|
|
201
|
+
export function getAiAgentLogsCommand(namespace?: string | undefined): string {
|
|
202
|
+
return `kubectl logs -n ${
|
|
203
|
+
namespace?.trim() || "<namespace>"
|
|
204
|
+
} -l component=${KUBERNETES_AI_AGENT_COMPONENT} --tail=100`;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/*
|
|
208
|
+
* The next step for a cluster no access target can reach: install the
|
|
209
|
+
* agent, with the command verbatim, or check its pod if that was done.
|
|
210
|
+
*/
|
|
211
|
+
export const AI_AGENT_NOT_CONNECTED_NEXT_STEP: string = `Install the Kubernetes AI agent (use your own release name and namespace if they differ):\n\n${AI_AGENT_INSTALL_COMMAND}\n\nAlready installed? Check its logs: ${getAiAgentLogsCommand()}`;
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* Who runs kubectl for a cluster, as the chart-value wording tells them
|
|
215
|
+
* apart: the Kubernetes AI agent (aiAgent.* values), the previous in-cluster
|
|
216
|
+
* Runner an older chart installed (its values are aiAccess.*, and the fix is
|
|
217
|
+
* upgrading the chart to the AI agent), or a Runner an operator runs with a
|
|
218
|
+
* Kubernetes credential (its own ONEUPTIME_KUBECTL_* environment).
|
|
219
|
+
*/
|
|
220
|
+
export type KubernetesAiAccessExecutorKind =
|
|
221
|
+
| "ai_agent"
|
|
222
|
+
| "legacy_runner"
|
|
223
|
+
| "credential_runner";
|
|
224
|
+
|
|
225
|
+
/*
|
|
226
|
+
* What to do about a read-only executor while remediation is on. For the
|
|
227
|
+
* agent it names the value that grants writes and the two that bound them
|
|
228
|
+
* (the namespaces the write role is bound in and the node switch); the
|
|
229
|
+
* complete command is on the AI agent page (a bare --set line would miss
|
|
230
|
+
* the chart index refresh and --reuse-values).
|
|
231
|
+
*/
|
|
232
|
+
export const REMEDIATION_WRITE_ACCESS_NEXT_STEP: string = `Upgrade the Kubernetes agent chart with --set aiAgent.remediation.enabled=true; the complete command is on ${CLUSTER_AI_AGENT_PAGE}. List the namespaces AI may fix in aiAgent.remediation.namespaces (without it the write role is cluster-wide), and add aiAgent.remediation.nodeOperations=false to keep fixes off nodes.`;
|
|
233
|
+
|
|
234
|
+
// The same for the previous in-cluster Runner: the AI agent replaces it.
|
|
235
|
+
export const LEGACY_RUNNER_REMEDIATION_WRITE_ACCESS_NEXT_STEP: string = `Upgrade the Kubernetes agent chart: the Kubernetes AI agent replaces this Runner and your settings carry over. Add --set aiAgent.remediation.enabled=true to the upgrade to let it apply fixes; the complete command is on ${CLUSTER_AI_AGENT_PAGE}.`;
|
|
236
|
+
|
|
237
|
+
// The same for a Runner no chart configures: its own environment.
|
|
238
|
+
export const CREDENTIAL_RUNNER_REMEDIATION_WRITE_ACCESS_NEXT_STEP: string = `Set ${KUBECTL_ALLOW_WRITES_ENV}=true on the Runner's host and restart it (${KUBECTL_WRITE_NAMESPACES_ENV} limits the namespaces it may change).`;
|
|
239
|
+
|
|
240
|
+
export function getRemediationWriteAccessNextStep(
|
|
241
|
+
kind: KubernetesAiAccessExecutorKind,
|
|
242
|
+
): string {
|
|
243
|
+
switch (kind) {
|
|
244
|
+
case "ai_agent":
|
|
245
|
+
return REMEDIATION_WRITE_ACCESS_NEXT_STEP;
|
|
246
|
+
case "legacy_runner":
|
|
247
|
+
return LEGACY_RUNNER_REMEDIATION_WRITE_ACCESS_NEXT_STEP;
|
|
248
|
+
default:
|
|
249
|
+
return CREDENTIAL_RUNNER_REMEDIATION_WRITE_ACCESS_NEXT_STEP;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/*
|
|
254
|
+
* The one access target AI reaches a cluster through right now (see
|
|
255
|
+
* resolveKubernetesAiAccessTarget), with the row it was resolved from.
|
|
256
|
+
*
|
|
257
|
+
* advanced_runner: a Runner an operator bound that is not a kubernetes-agent
|
|
258
|
+
* row — reached with the cluster's Kubernetes credential.
|
|
259
|
+
* ai_agent: the cluster's Kubernetes AI agent (online or not).
|
|
260
|
+
* legacy_runner: the cluster's previous in-cluster Runner, bound to it.
|
|
261
|
+
* none: nothing can reach the cluster.
|
|
262
|
+
*/
|
|
263
|
+
export type KubernetesAiAccessTarget =
|
|
264
|
+
| { type: "advanced_runner"; runner: Runner; isOnline: boolean }
|
|
265
|
+
| { type: "ai_agent"; agent: KubernetesAiAgent; isOnline: boolean }
|
|
266
|
+
| { type: "legacy_runner"; runner: Runner; isOnline: boolean }
|
|
267
|
+
| { type: "none" };
|
|
268
|
+
|
|
269
|
+
// The id jobs and plans name the target by (runnerId), or null for none.
|
|
270
|
+
export function getKubernetesAiAccessTargetId(
|
|
271
|
+
target: KubernetesAiAccessTarget,
|
|
272
|
+
): string | null {
|
|
273
|
+
switch (target.type) {
|
|
274
|
+
case "ai_agent":
|
|
275
|
+
return target.agent.id?.toString() || null;
|
|
276
|
+
case "advanced_runner":
|
|
277
|
+
case "legacy_runner":
|
|
278
|
+
return target.runner.id?.toString() || null;
|
|
279
|
+
default:
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/*
|
|
285
|
+
* THE rule for which access target AI uses for a cluster. Liveness based,
|
|
286
|
+
* so a helm upgrade to the AI agent switches over the moment the agent is
|
|
287
|
+
* online, and a rollback that stops the agent falls back on its own to the
|
|
288
|
+
* previous in-cluster Runner the cluster is still bound to — no dashboard
|
|
289
|
+
* step either way:
|
|
290
|
+
*
|
|
291
|
+
* 1. a bound Runner that is not a kubernetes-agent row: the advanced Runner
|
|
292
|
+
* an operator chose (with its Kubernetes credential). An explicit
|
|
293
|
+
* operator choice wins over everything the chart installs.
|
|
294
|
+
* 2. the cluster's AI agent, when online.
|
|
295
|
+
* 3. the bound Runner, when it is THIS cluster's previous in-cluster Runner
|
|
296
|
+
* and online (the agent is not, or was never installed).
|
|
297
|
+
* 4. the cluster's AI agent, offline (its offline gap says what to check).
|
|
298
|
+
* 5. the bound previous in-cluster Runner, offline.
|
|
299
|
+
* 6. nothing.
|
|
300
|
+
*
|
|
301
|
+
* A bound Runner that is another cluster's kubernetes-agent row is none of
|
|
302
|
+
* these: its ServiceAccount can never reach this cluster. Pure: liveness is
|
|
303
|
+
* decided by the caller (isRunnerOnline, KubernetesAiAgentService.isOnline).
|
|
304
|
+
*/
|
|
305
|
+
export function resolveKubernetesAiAccessTarget(data: {
|
|
306
|
+
clusterIdentifier: string | undefined;
|
|
307
|
+
aiAccessRunnerId: ObjectID | string | null | undefined;
|
|
308
|
+
// The Runner row aiAccessRunnerId names, when it still exists.
|
|
309
|
+
boundRunner: Runner | null | undefined;
|
|
310
|
+
isBoundRunnerOnline: boolean;
|
|
311
|
+
// The cluster's KubernetesAiAgent row, if one ever registered.
|
|
312
|
+
agent: KubernetesAiAgent | null | undefined;
|
|
313
|
+
isAgentOnline: boolean;
|
|
314
|
+
}): KubernetesAiAccessTarget {
|
|
315
|
+
/*
|
|
316
|
+
* Only the row the binding names: a row with another id (a stale read)
|
|
317
|
+
* is not the bound Runner.
|
|
318
|
+
*/
|
|
319
|
+
const boundRunner: Runner | null =
|
|
320
|
+
data.aiAccessRunnerId &&
|
|
321
|
+
data.boundRunner &&
|
|
322
|
+
(!data.boundRunner.id ||
|
|
323
|
+
data.boundRunner.id.toString() === data.aiAccessRunnerId.toString())
|
|
324
|
+
? data.boundRunner
|
|
325
|
+
: null;
|
|
326
|
+
|
|
327
|
+
if (
|
|
328
|
+
boundRunner &&
|
|
329
|
+
!RunnerServiceClass.isKubernetesAgentRunnerRow(boundRunner)
|
|
330
|
+
) {
|
|
331
|
+
return {
|
|
332
|
+
type: "advanced_runner",
|
|
333
|
+
runner: boundRunner,
|
|
334
|
+
isOnline: data.isBoundRunnerOnline,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const legacyRunner: Runner | null =
|
|
339
|
+
boundRunner &&
|
|
340
|
+
RunnerServiceClass.isKubernetesAgentRunnerOfCluster(
|
|
341
|
+
boundRunner,
|
|
342
|
+
data.clusterIdentifier,
|
|
343
|
+
)
|
|
344
|
+
? boundRunner
|
|
345
|
+
: null;
|
|
346
|
+
|
|
347
|
+
if (data.agent && data.isAgentOnline) {
|
|
348
|
+
return { type: "ai_agent", agent: data.agent, isOnline: true };
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (legacyRunner && data.isBoundRunnerOnline) {
|
|
352
|
+
return { type: "legacy_runner", runner: legacyRunner, isOnline: true };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (data.agent) {
|
|
356
|
+
return { type: "ai_agent", agent: data.agent, isOnline: false };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (legacyRunner) {
|
|
360
|
+
return { type: "legacy_runner", runner: legacyRunner, isOnline: false };
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return { type: "none" };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// What the access-target resolution read, for callers that need the rows.
|
|
367
|
+
export interface LoadedKubernetesAiAccessTarget {
|
|
368
|
+
target: KubernetesAiAccessTarget;
|
|
369
|
+
// The Runner row aiAccessRunnerId names; null when unset or deleted.
|
|
370
|
+
boundRunner: Runner | null;
|
|
371
|
+
agent: KubernetesAiAgent | null;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// What target resolution and the status read off a bound Runner row.
|
|
375
|
+
const BOUND_RUNNER_SELECT: Record<string, boolean> = {
|
|
376
|
+
_id: true,
|
|
377
|
+
name: true,
|
|
378
|
+
lastAlive: true,
|
|
379
|
+
connectionStatus: true,
|
|
380
|
+
canRunAiCommands: true,
|
|
381
|
+
canRunRunbooks: true,
|
|
382
|
+
canRunCodeFixTasks: true,
|
|
383
|
+
hostInfo: true,
|
|
384
|
+
};
|
|
153
385
|
|
|
154
386
|
/*
|
|
155
387
|
* Why deleting an in-cluster Runner is a two-step remedy (the helper lives
|
|
@@ -189,6 +421,21 @@ export class KubernetesAgentRegistrationRefusedException extends ForbiddenExcept
|
|
|
189
421
|
}
|
|
190
422
|
}
|
|
191
423
|
|
|
424
|
+
/*
|
|
425
|
+
* How long a previous in-cluster Runner refused because the cluster's
|
|
426
|
+
* Kubernetes AI agent is online waits before it asks again. The refusal
|
|
427
|
+
* lasts only while the agent is online, so the Runner keeps asking — slowly
|
|
428
|
+
* — and is admitted within a minute of the agent stopping (a helm rollback).
|
|
429
|
+
*/
|
|
430
|
+
export const SUPERSEDED_BY_AI_AGENT_RETRY_AFTER_SECONDS: number = 60;
|
|
431
|
+
|
|
432
|
+
// Why the previous in-cluster Runner was refused: the AI agent replaces it.
|
|
433
|
+
export function getSupersededByAiAgentMessage(
|
|
434
|
+
clusterIdentifier: string,
|
|
435
|
+
): string {
|
|
436
|
+
return `The Kubernetes AI agent of cluster "${clusterIdentifier}" is online and replaces this in-cluster Runner, so the Runner was not registered. Nothing needs to be done: upgrading the Kubernetes agent chart removes the Runner, and it is admitted again only if the AI agent stops (for example after a helm rollback).`;
|
|
437
|
+
}
|
|
438
|
+
|
|
192
439
|
/*
|
|
193
440
|
* When a registration refused because the previous instance is online may
|
|
194
441
|
* try again: once that instance's last heartbeat is older than the alive
|
|
@@ -245,6 +492,23 @@ interface RunnerPresence {
|
|
|
245
492
|
isOnline: boolean;
|
|
246
493
|
}
|
|
247
494
|
|
|
495
|
+
/*
|
|
496
|
+
* How the resolved access target reaches the cluster, for the status: the
|
|
497
|
+
* target's summary (runner), how kubectl authenticates, the credential when
|
|
498
|
+
* it is one, and every gap that stops the target itself.
|
|
499
|
+
*/
|
|
500
|
+
interface ResolvedTargetAccess {
|
|
501
|
+
runner: KubernetesAiAccessRunnerSummary | null;
|
|
502
|
+
accessMethod: KubernetesClusterAiAccessStatus["accessMethod"];
|
|
503
|
+
credentialId?: string | undefined;
|
|
504
|
+
credentialName?: string | undefined;
|
|
505
|
+
gaps: Array<KubernetesAiAccessGap>;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// What to do about an empty AI balance (ai_balance_insufficient).
|
|
509
|
+
export const AI_BALANCE_INSUFFICIENT_NEXT_STEP: string =
|
|
510
|
+
"Add AI credits under Project Settings → AI Credits (or enable auto-recharge).";
|
|
511
|
+
|
|
248
512
|
class KubernetesClusterAiAccessServiceClass {
|
|
249
513
|
/*
|
|
250
514
|
* ------------------------------------------------------------------
|
|
@@ -262,28 +526,58 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
262
526
|
enableAi: true,
|
|
263
527
|
enableAutoRemediation: true,
|
|
264
528
|
enableAiCommandExecution: true,
|
|
529
|
+
enableAutomaticIncidentInvestigation: true,
|
|
530
|
+
enableAutomaticAlertInvestigation: true,
|
|
265
531
|
},
|
|
266
532
|
props: { isRoot: true },
|
|
267
533
|
});
|
|
268
534
|
|
|
269
|
-
|
|
535
|
+
// undefined: the lookup failed, so neither it nor the balance is known.
|
|
536
|
+
let llmProvider: LlmProvider | null | undefined = undefined;
|
|
270
537
|
|
|
271
538
|
try {
|
|
272
|
-
|
|
273
|
-
|
|
539
|
+
llmProvider =
|
|
540
|
+
await LlmProviderService.getLLMProviderForProject(projectId);
|
|
274
541
|
} catch (error) {
|
|
275
542
|
logger.error(
|
|
276
543
|
`KubernetesClusterAiAccess: could not resolve the LLM provider for project ${projectId.toString()}: ${error}`,
|
|
277
544
|
);
|
|
278
545
|
}
|
|
279
546
|
|
|
547
|
+
/*
|
|
548
|
+
* An empty balance is a precondition like the provider: when it cannot
|
|
549
|
+
* be checked, the status says nothing about it rather than claiming a
|
|
550
|
+
* blocker that may not exist (the AI call itself still refuses). The
|
|
551
|
+
* provider resolved above is handed over, so it is looked up once.
|
|
552
|
+
*/
|
|
553
|
+
let aiBalanceBlocker: string | null = null;
|
|
554
|
+
|
|
555
|
+
if (llmProvider !== undefined) {
|
|
556
|
+
try {
|
|
557
|
+
aiBalanceBlocker = await AIService.getAiBalanceBlocker({
|
|
558
|
+
projectId,
|
|
559
|
+
llmProvider,
|
|
560
|
+
});
|
|
561
|
+
} catch (error) {
|
|
562
|
+
logger.error(
|
|
563
|
+
`KubernetesClusterAiAccess: could not check the AI balance of project ${projectId.toString()}: ${error}`,
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
280
568
|
return {
|
|
281
569
|
// Kill switches: a missing column counts as enabled (=== false idiom).
|
|
282
570
|
isAiEnabled: project?.enableAi !== false,
|
|
283
571
|
isAutoRemediationEnabled: project?.enableAutoRemediation !== false,
|
|
284
572
|
// Explicit opt-in (=== true idiom), same as the Bash/SSH lane.
|
|
285
573
|
isAiCommandExecutionEnabled: project?.enableAiCommandExecution === true,
|
|
286
|
-
|
|
574
|
+
// A failed lookup reads as none, like no provider at all.
|
|
575
|
+
hasLlmProvider: Boolean(llmProvider),
|
|
576
|
+
aiBalanceBlocker,
|
|
577
|
+
automaticInvestigation: {
|
|
578
|
+
incidents: project?.enableAutomaticIncidentInvestigation === true,
|
|
579
|
+
alerts: project?.enableAutomaticAlertInvestigation === true,
|
|
580
|
+
},
|
|
287
581
|
};
|
|
288
582
|
}
|
|
289
583
|
|
|
@@ -314,254 +608,221 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
314
608
|
}
|
|
315
609
|
|
|
316
610
|
/*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
|
|
611
|
+
* ------------------------------------------------------------------
|
|
612
|
+
* The access target
|
|
613
|
+
* ------------------------------------------------------------------
|
|
614
|
+
*/
|
|
615
|
+
|
|
616
|
+
/*
|
|
617
|
+
* Is this Runner online right now? One rule for readiness, target
|
|
618
|
+
* resolution and registration: it heartbeated within the alive window
|
|
619
|
+
* (RunnerLiveStatus) AND it has not signed off since. A Runner signs off
|
|
620
|
+
* on a clean shutdown (/runner-ingest/disconnect), which is what a helm
|
|
621
|
+
* upgrade or uninstall does to the chart's in-cluster Runner pod — it
|
|
622
|
+
* must read offline at once, not stay "Connected" until its last
|
|
623
|
+
* heartbeat ages out while investigations queue kubectl it will never
|
|
624
|
+
* claim.
|
|
625
|
+
*/
|
|
626
|
+
public isRunnerOnline(runner: Runner): boolean {
|
|
627
|
+
return this.getRunnerPresence(runner).isOnline;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/*
|
|
631
|
+
* resolveKubernetesAiAccessTarget with each row's liveness read the one
|
|
632
|
+
* way this module reads it.
|
|
633
|
+
*/
|
|
634
|
+
public resolveAccessTarget(
|
|
635
|
+
cluster: Pick<KubernetesCluster, "aiAccessRunnerId" | "clusterIdentifier">,
|
|
636
|
+
data: {
|
|
637
|
+
boundRunner: Runner | null | undefined;
|
|
638
|
+
agentRow: KubernetesAiAgent | null | undefined;
|
|
639
|
+
},
|
|
640
|
+
): KubernetesAiAccessTarget {
|
|
641
|
+
return resolveKubernetesAiAccessTarget({
|
|
642
|
+
clusterIdentifier: cluster.clusterIdentifier,
|
|
643
|
+
aiAccessRunnerId: cluster.aiAccessRunnerId,
|
|
644
|
+
boundRunner: data.boundRunner,
|
|
645
|
+
isBoundRunnerOnline: data.boundRunner
|
|
646
|
+
? this.isRunnerOnline(data.boundRunner)
|
|
647
|
+
: false,
|
|
648
|
+
agent: data.agentRow,
|
|
649
|
+
isAgentOnline: data.agentRow
|
|
650
|
+
? KubernetesAiAgentService.isOnline(data.agentRow)
|
|
651
|
+
: false,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/*
|
|
656
|
+
* Read what the resolution needs — the bound Runner row and the cluster's
|
|
657
|
+
* agent row (unless the caller already has it) — and resolve. Lookup
|
|
658
|
+
* failures propagate: the enqueue chokepoint must fail closed on a target
|
|
659
|
+
* it could not check.
|
|
321
660
|
*/
|
|
322
661
|
@CaptureSpan()
|
|
323
|
-
public async
|
|
662
|
+
public async loadAccessTarget(data: {
|
|
324
663
|
cluster: KubernetesCluster;
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const
|
|
664
|
+
// The cluster's agent row when already read (a batch); undefined reads it.
|
|
665
|
+
agentRow?: KubernetesAiAgent | null | undefined;
|
|
666
|
+
}): Promise<LoadedKubernetesAiAccessTarget> {
|
|
667
|
+
const { cluster } = data;
|
|
329
668
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
669
|
+
if (!cluster.id || !cluster.projectId) {
|
|
670
|
+
throw new BadDataException(
|
|
671
|
+
"The cluster's id and project are needed to resolve its AI access target.",
|
|
672
|
+
);
|
|
673
|
+
}
|
|
334
674
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
675
|
+
const agent: KubernetesAiAgent | null =
|
|
676
|
+
data.agentRow !== undefined
|
|
677
|
+
? data.agentRow
|
|
678
|
+
: await KubernetesAiAgentService.findForCluster({
|
|
679
|
+
projectId: cluster.projectId,
|
|
680
|
+
kubernetesClusterId: cluster.id,
|
|
681
|
+
});
|
|
339
682
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
683
|
+
const boundRunner: Runner | null = cluster.aiAccessRunnerId
|
|
684
|
+
? await RunnerService.findOneBy({
|
|
685
|
+
query: {
|
|
686
|
+
_id: cluster.aiAccessRunnerId.toString(),
|
|
687
|
+
projectId: cluster.projectId,
|
|
688
|
+
},
|
|
689
|
+
select: BOUND_RUNNER_SELECT,
|
|
690
|
+
props: { isRoot: true },
|
|
691
|
+
})
|
|
692
|
+
: null;
|
|
693
|
+
|
|
694
|
+
return {
|
|
695
|
+
target: this.resolveAccessTarget(cluster, {
|
|
696
|
+
boundRunner,
|
|
697
|
+
agentRow: agent,
|
|
698
|
+
}),
|
|
699
|
+
boundRunner,
|
|
700
|
+
agent,
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/*
|
|
705
|
+
* This cluster's previous in-cluster Runner — the kubernetes-agent Runner
|
|
706
|
+
* row an older chart registered — or null. Found by its (bounded) name,
|
|
707
|
+
* matched case-insensitively like registration matches it; a row whose
|
|
708
|
+
* posture names a DIFFERENT cluster is not this cluster's (a shortened
|
|
709
|
+
* name's hash collision). Failing that, the cluster's bound Runner when it
|
|
710
|
+
* is this cluster's agent by posture.
|
|
711
|
+
*
|
|
712
|
+
* A failed lookup propagates rather than reading as none: the AI agent's
|
|
713
|
+
* registration asks this to keep a live Runner's cluster, and "could not
|
|
714
|
+
* tell" must refuse (the agent retries) — never admit an agent that then
|
|
715
|
+
* outranks a Runner that is still working. A caller that may treat "could
|
|
716
|
+
* not tell" as "nothing to do" catches it itself.
|
|
717
|
+
*/
|
|
718
|
+
@CaptureSpan()
|
|
719
|
+
public async getLegacyAgentRunnerForCluster(data: {
|
|
720
|
+
projectId: ObjectID;
|
|
721
|
+
kubernetesClusterId: ObjectID;
|
|
722
|
+
clusterIdentifier: string | undefined;
|
|
723
|
+
}): Promise<Runner | null> {
|
|
724
|
+
const clusterIdentifier: string = (data.clusterIdentifier || "").trim();
|
|
725
|
+
|
|
726
|
+
if (!clusterIdentifier) {
|
|
727
|
+
return null;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const byName: Runner | null = await RunnerService.findOneBy({
|
|
731
|
+
query: {
|
|
732
|
+
projectId: data.projectId,
|
|
733
|
+
name: QueryHelper.findWithSameText(
|
|
734
|
+
getKubernetesAgentRunnerNameForCluster(clusterIdentifier),
|
|
735
|
+
),
|
|
736
|
+
},
|
|
737
|
+
select: BOUND_RUNNER_SELECT,
|
|
738
|
+
props: { isRoot: true },
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
if (byName) {
|
|
742
|
+
return this.postureNamesAnotherCluster({
|
|
743
|
+
runner: byName,
|
|
744
|
+
clusterIdentifier,
|
|
745
|
+
})
|
|
746
|
+
? null
|
|
747
|
+
: byName;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const cluster: KubernetesCluster | null =
|
|
751
|
+
await KubernetesClusterService.findOneBy({
|
|
344
752
|
query: {
|
|
345
|
-
_id:
|
|
346
|
-
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,
|
|
753
|
+
_id: data.kubernetesClusterId.toString(),
|
|
754
|
+
projectId: data.projectId,
|
|
358
755
|
},
|
|
756
|
+
select: { _id: true, aiAccessRunnerId: true },
|
|
359
757
|
props: { isRoot: true },
|
|
360
758
|
});
|
|
361
759
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
};
|
|
760
|
+
if (!cluster?.aiAccessRunnerId) {
|
|
761
|
+
return null;
|
|
762
|
+
}
|
|
400
763
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
}
|
|
764
|
+
const bound: Runner | null = await RunnerService.findOneBy({
|
|
765
|
+
query: {
|
|
766
|
+
_id: cluster.aiAccessRunnerId.toString(),
|
|
767
|
+
projectId: data.projectId,
|
|
768
|
+
},
|
|
769
|
+
select: BOUND_RUNNER_SELECT,
|
|
770
|
+
props: { isRoot: true },
|
|
771
|
+
});
|
|
422
772
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
});
|
|
432
|
-
}
|
|
773
|
+
return bound &&
|
|
774
|
+
RunnerServiceClass.isKubernetesAgentRunnerOfCluster(
|
|
775
|
+
bound,
|
|
776
|
+
clusterIdentifier,
|
|
777
|
+
)
|
|
778
|
+
? bound
|
|
779
|
+
: null;
|
|
780
|
+
}
|
|
433
781
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
782
|
+
/*
|
|
783
|
+
* The readiness computation proper. Resolves the cluster's access target
|
|
784
|
+
* (resolveKubernetesAiAccessTarget), reads the bound Runner, the agent row
|
|
785
|
+
* and the credential as root (the cluster row is already tenant-scoped by
|
|
786
|
+
* the caller) and folds project gates in. Always sets aiAgent (the
|
|
787
|
+
* cluster's agent whether or not it is the target) and
|
|
788
|
+
* automaticInvestigation.
|
|
789
|
+
*/
|
|
790
|
+
@CaptureSpan()
|
|
791
|
+
public async getStatusForClusterModel(data: {
|
|
792
|
+
cluster: KubernetesCluster;
|
|
793
|
+
gates: KubernetesClusterAiAccessProjectGates;
|
|
794
|
+
/*
|
|
795
|
+
* The cluster's KubernetesAiAgent row when the caller already read it
|
|
796
|
+
* (getStatusesForSubject reads every cluster's in one query); null for
|
|
797
|
+
* "none", undefined to read it here.
|
|
798
|
+
*/
|
|
799
|
+
aiAgentRow?: KubernetesAiAgent | null | undefined;
|
|
800
|
+
}): Promise<KubernetesClusterAiAccessStatus> {
|
|
801
|
+
const { cluster, gates } = data;
|
|
802
|
+
const gaps: Array<KubernetesAiAccessGap> = [];
|
|
446
803
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
);
|
|
804
|
+
const remediationMode: KubernetesAiRemediationMode =
|
|
805
|
+
this.normalizeRemediationMode(cluster.aiRemediationMode);
|
|
806
|
+
const isInvestigationEnabled: boolean =
|
|
807
|
+
cluster.isAiInvestigationEnabled === true;
|
|
492
808
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
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
|
-
}
|
|
809
|
+
const agentRow: KubernetesAiAgent | null =
|
|
810
|
+
data.aiAgentRow !== undefined
|
|
811
|
+
? data.aiAgentRow
|
|
812
|
+
: await this.findAiAgentRowForStatus(cluster);
|
|
548
813
|
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
814
|
+
const loaded: LoadedKubernetesAiAccessTarget = await this.loadAccessTarget({
|
|
815
|
+
cluster,
|
|
816
|
+
agentRow,
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
const access: ResolvedTargetAccess = await this.getTargetAccess({
|
|
820
|
+
cluster,
|
|
821
|
+
loaded,
|
|
822
|
+
remediationMode,
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
gaps.push(...access.gaps);
|
|
565
826
|
|
|
566
827
|
if (!isInvestigationEnabled) {
|
|
567
828
|
gaps.push({
|
|
@@ -569,8 +830,7 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
569
830
|
title: "AI investigation is turned off for this cluster",
|
|
570
831
|
description:
|
|
571
832
|
"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.',
|
|
833
|
+
nextStep: `Turn on "Investigate with kubectl" on ${CLUSTER_AI_AGENT_PAGE}.`,
|
|
574
834
|
blocks: "investigation",
|
|
575
835
|
});
|
|
576
836
|
}
|
|
@@ -578,11 +838,10 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
578
838
|
if (remediationMode === KubernetesAiRemediationMode.Disabled) {
|
|
579
839
|
gaps.push({
|
|
580
840
|
code: "remediation_disabled",
|
|
581
|
-
title: "AI
|
|
841
|
+
title: "AI fixes are turned off for this cluster",
|
|
582
842
|
description:
|
|
583
843
|
"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.',
|
|
844
|
+
nextStep: `Set "Fixes" to "Ask for approval", "Automatic" or "Bypass approval" on ${CLUSTER_AI_AGENT_PAGE}.`,
|
|
586
845
|
blocks: "remediation",
|
|
587
846
|
});
|
|
588
847
|
}
|
|
@@ -592,7 +851,7 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
592
851
|
code: "project_ai_disabled",
|
|
593
852
|
title: "AI is disabled for this project",
|
|
594
853
|
description: "OneUptime AI is switched off at the project level.",
|
|
595
|
-
nextStep: "Enable AI under Project Settings → AI.",
|
|
854
|
+
nextStep: "Enable AI under Project Settings → AI Features.",
|
|
596
855
|
blocks: "both",
|
|
597
856
|
});
|
|
598
857
|
}
|
|
@@ -609,25 +868,46 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
609
868
|
});
|
|
610
869
|
}
|
|
611
870
|
|
|
871
|
+
if (gates.aiBalanceBlocker) {
|
|
872
|
+
gaps.push({
|
|
873
|
+
code: "ai_balance_insufficient",
|
|
874
|
+
title: "The project is out of AI credits",
|
|
875
|
+
description: gates.aiBalanceBlocker,
|
|
876
|
+
nextStep: AI_BALANCE_INSUFFICIENT_NEXT_STEP,
|
|
877
|
+
blocks: "both",
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
|
|
612
881
|
if (!gates.isAutoRemediationEnabled) {
|
|
613
882
|
gaps.push({
|
|
614
883
|
code: "project_auto_remediation_disabled",
|
|
615
884
|
title: "Auto-remediation is disabled for this project",
|
|
616
885
|
description:
|
|
617
886
|
"The project-level auto-remediation kill switch is off, so no AI fix can be proposed or run.",
|
|
618
|
-
nextStep:
|
|
887
|
+
nextStep:
|
|
888
|
+
"Enable auto-remediation under Project Settings → AI Features.",
|
|
619
889
|
blocks: "remediation",
|
|
620
890
|
});
|
|
621
891
|
}
|
|
622
892
|
|
|
623
|
-
|
|
893
|
+
/*
|
|
894
|
+
* The project's command-execution opt-in governs AI-composed commands
|
|
895
|
+
* on Runners. A cluster reached through its Kubernetes AI agent (or the
|
|
896
|
+
* chart's previous in-cluster Runner) is governed by the cluster's own
|
|
897
|
+
* Fixes setting and the chart's write RBAC instead, so the opt-in only
|
|
898
|
+
* gates a cluster reached through an advanced Runner and credential.
|
|
899
|
+
*/
|
|
900
|
+
if (
|
|
901
|
+
loaded.target.type === "advanced_runner" &&
|
|
902
|
+
!gates.isAiCommandExecutionEnabled
|
|
903
|
+
) {
|
|
624
904
|
gaps.push({
|
|
625
905
|
code: "project_ai_command_execution_disabled",
|
|
626
906
|
title: "AI command execution is not enabled for this project",
|
|
627
907
|
description:
|
|
628
|
-
"AI-composed commands (
|
|
908
|
+
"This cluster is reached through a Runner, and AI-composed commands (kubectl fixes included) never run on a Runner in a project that has not opted in.",
|
|
629
909
|
nextStep:
|
|
630
|
-
'Turn on "Enable AI Command Execution" under Project Settings → AI.',
|
|
910
|
+
'Turn on "Enable AI Command Execution" under Project Settings → AI Features.',
|
|
631
911
|
blocks: "remediation",
|
|
632
912
|
});
|
|
633
913
|
}
|
|
@@ -647,10 +927,17 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
647
927
|
clusterId: cluster.id!.toString(),
|
|
648
928
|
clusterName: cluster.name || cluster.clusterIdentifier || "cluster",
|
|
649
929
|
clusterIdentifier: cluster.clusterIdentifier,
|
|
650
|
-
runner:
|
|
651
|
-
accessMethod,
|
|
652
|
-
|
|
653
|
-
|
|
930
|
+
runner: access.runner,
|
|
931
|
+
accessMethod: access.accessMethod,
|
|
932
|
+
aiAgent: loaded.agent
|
|
933
|
+
? KubernetesAiAgentService.toSummary(loaded.agent)
|
|
934
|
+
: null,
|
|
935
|
+
automaticInvestigation: {
|
|
936
|
+
incidents: gates.automaticInvestigation?.incidents === true,
|
|
937
|
+
alerts: gates.automaticInvestigation?.alerts === true,
|
|
938
|
+
},
|
|
939
|
+
credentialId: access.credentialId,
|
|
940
|
+
credentialName: access.credentialName,
|
|
654
941
|
kubectlAllowlist: this.normalizeAllowlist(
|
|
655
942
|
cluster.aiKubectlCommandAllowlist,
|
|
656
943
|
),
|
|
@@ -670,19 +957,389 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
670
957
|
}
|
|
671
958
|
|
|
672
959
|
/*
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
960
|
+
* The cluster's agent row for a status. Never throws: a failed read is
|
|
961
|
+
* logged and reads as "no agent", which can only make the status say
|
|
962
|
+
* less is reachable (or fall back to a bound Runner), never more.
|
|
963
|
+
*/
|
|
964
|
+
private async findAiAgentRowForStatus(
|
|
965
|
+
cluster: KubernetesCluster,
|
|
966
|
+
): Promise<KubernetesAiAgent | null> {
|
|
967
|
+
if (!cluster.id || !cluster.projectId) {
|
|
968
|
+
return null;
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
try {
|
|
972
|
+
return await KubernetesAiAgentService.findForCluster({
|
|
973
|
+
projectId: cluster.projectId,
|
|
974
|
+
kubernetesClusterId: cluster.id,
|
|
975
|
+
});
|
|
976
|
+
} catch (error) {
|
|
977
|
+
logger.error(
|
|
978
|
+
`KubernetesClusterAiAccess: could not read the Kubernetes AI agent of cluster ${cluster.id.toString()}: ${error}`,
|
|
979
|
+
);
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// How the resolved target reaches the cluster, and what stops it.
|
|
985
|
+
private async getTargetAccess(data: {
|
|
986
|
+
cluster: KubernetesCluster;
|
|
987
|
+
loaded: LoadedKubernetesAiAccessTarget;
|
|
988
|
+
remediationMode: KubernetesAiRemediationMode;
|
|
989
|
+
}): Promise<ResolvedTargetAccess> {
|
|
990
|
+
const { cluster, loaded, remediationMode } = data;
|
|
991
|
+
const target: KubernetesAiAccessTarget = loaded.target;
|
|
992
|
+
|
|
993
|
+
switch (target.type) {
|
|
994
|
+
case "ai_agent":
|
|
995
|
+
return this.getAgentAccess({
|
|
996
|
+
cluster,
|
|
997
|
+
agent: target.agent,
|
|
998
|
+
isOnline: target.isOnline,
|
|
999
|
+
remediationMode,
|
|
1000
|
+
});
|
|
1001
|
+
case "legacy_runner":
|
|
1002
|
+
case "advanced_runner":
|
|
1003
|
+
return this.getRunnerAccess({
|
|
1004
|
+
cluster,
|
|
1005
|
+
runner: target.runner,
|
|
1006
|
+
isLegacy: target.type === "legacy_runner",
|
|
1007
|
+
remediationMode,
|
|
1008
|
+
});
|
|
1009
|
+
default:
|
|
1010
|
+
return {
|
|
1011
|
+
runner: null,
|
|
1012
|
+
accessMethod: "none",
|
|
1013
|
+
gaps: [
|
|
1014
|
+
/*
|
|
1015
|
+
* A bound id with no Runner row is only the race with a Runner
|
|
1016
|
+
* delete: the binding's foreign key is ON DELETE SET NULL, so
|
|
1017
|
+
* the next read has no binding at all and reads as not
|
|
1018
|
+
* connected.
|
|
1019
|
+
*/
|
|
1020
|
+
cluster.aiAccessRunnerId && !loaded.boundRunner
|
|
1021
|
+
? {
|
|
1022
|
+
code: "runner_missing",
|
|
1023
|
+
title: "The bound Runner was just deleted",
|
|
1024
|
+
description:
|
|
1025
|
+
"The Runner this cluster was bound to was deleted while its status was being read.",
|
|
1026
|
+
nextStep: `Reload ${CLUSTER_AI_AGENT_PAGE}.`,
|
|
1027
|
+
blocks: "both",
|
|
1028
|
+
}
|
|
1029
|
+
: {
|
|
1030
|
+
code: "ai_agent_not_connected",
|
|
1031
|
+
title: "The Kubernetes AI agent is not connected",
|
|
1032
|
+
description: `OneUptime AI runs kubectl on this cluster through the Kubernetes AI agent, and no agent has connected for this cluster yet.${this.getIgnoredBoundRunnerNote(
|
|
1033
|
+
{ cluster, boundRunner: loaded.boundRunner },
|
|
1034
|
+
)}`,
|
|
1035
|
+
nextStep: AI_AGENT_NOT_CONNECTED_NEXT_STEP,
|
|
1036
|
+
blocks: "both",
|
|
1037
|
+
},
|
|
1038
|
+
],
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
/*
|
|
1044
|
+
* A Runner still bound to a cluster that has no access target is another
|
|
1045
|
+
* cluster's in-cluster Runner (a kubernetes-agent row that is not this
|
|
1046
|
+
* cluster's): its ServiceAccount can never reach this cluster, so it is
|
|
1047
|
+
* ignored. Said, so the status does not read as if nothing were
|
|
1048
|
+
* selected; installing the agent (the gap's step) is still the fix.
|
|
1049
|
+
* Empty when nothing is bound.
|
|
1050
|
+
*/
|
|
1051
|
+
private getIgnoredBoundRunnerNote(data: {
|
|
1052
|
+
cluster: KubernetesCluster;
|
|
1053
|
+
boundRunner: Runner | null;
|
|
1054
|
+
}): string {
|
|
1055
|
+
const { cluster, boundRunner } = data;
|
|
1056
|
+
|
|
1057
|
+
if (!cluster.aiAccessRunnerId || !boundRunner) {
|
|
1058
|
+
return "";
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
const reportedCluster: string | undefined =
|
|
1062
|
+
parseKubernetesRunnerPosture(
|
|
1063
|
+
boundRunner.hostInfo,
|
|
1064
|
+
)?.clusterIdentifier?.trim() || undefined;
|
|
1065
|
+
|
|
1066
|
+
return ` Runner "${boundRunner.name || "Runner"}" is still selected for this cluster, but it is not this cluster's in-cluster Runner${
|
|
1067
|
+
reportedCluster ? ` (it reports cluster "${reportedCluster}")` : ""
|
|
1068
|
+
}, so OneUptime AI does not use it.`;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/*
|
|
1072
|
+
* The cluster's Kubernetes AI agent as the target. It runs kubectl with
|
|
1073
|
+
* its own ServiceAccount (accessMethod "in_cluster"), always accepts AI
|
|
1074
|
+
* commands and never carries a credential.
|
|
1075
|
+
*/
|
|
1076
|
+
private getAgentAccess(data: {
|
|
1077
|
+
cluster: KubernetesCluster;
|
|
1078
|
+
agent: KubernetesAiAgent;
|
|
1079
|
+
isOnline: boolean;
|
|
1080
|
+
remediationMode: KubernetesAiRemediationMode;
|
|
1081
|
+
}): ResolvedTargetAccess {
|
|
1082
|
+
const { cluster, agent, isOnline, remediationMode } = data;
|
|
1083
|
+
const gaps: Array<KubernetesAiAccessGap> = [];
|
|
1084
|
+
const posture: KubernetesAgentPosture | undefined =
|
|
1085
|
+
parseKubernetesAgentPosture(agent.posture);
|
|
1086
|
+
|
|
1087
|
+
const runner: KubernetesAiAccessRunnerSummary = {
|
|
1088
|
+
id: agent.id!.toString(),
|
|
1089
|
+
name: KUBERNETES_AI_AGENT_DISPLAY_NAME,
|
|
1090
|
+
kind: "ai_agent",
|
|
1091
|
+
isOnline,
|
|
1092
|
+
lastAliveAt: agent.lastAliveAt
|
|
1093
|
+
? OneUptimeDate.toString(agent.lastAliveAt)
|
|
1094
|
+
: undefined,
|
|
1095
|
+
canRunAiCommands: true,
|
|
1096
|
+
posture,
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
if (!isOnline) {
|
|
1100
|
+
const lastAliveText: string = agent.lastAliveAt
|
|
1101
|
+
? OneUptimeDate.getDateAsFormattedString(agent.lastAliveAt)
|
|
1102
|
+
: "an unknown time";
|
|
1103
|
+
|
|
1104
|
+
gaps.push({
|
|
1105
|
+
code: "ai_agent_offline",
|
|
1106
|
+
title: "The Kubernetes AI agent is offline",
|
|
1107
|
+
description:
|
|
1108
|
+
agent.connectionStatus === "disconnected"
|
|
1109
|
+
? `The Kubernetes AI agent signed off after its last report at ${lastAliveText}: it was stopped, uninstalled, turned off with aiAgent.enabled=false, or reset, or its pod is being replaced.`
|
|
1110
|
+
: `The Kubernetes AI agent last reported in at ${lastAliveText}.`,
|
|
1111
|
+
nextStep: `Check the agent's pod: ${getAiAgentLogsCommand(
|
|
1112
|
+
posture?.podNamespace,
|
|
1113
|
+
)}. It must be able to reach your OneUptime URL.`,
|
|
1114
|
+
blocks: "both",
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/*
|
|
1119
|
+
* The server stamps the cluster's identifier on the agent's posture
|
|
1120
|
+
* every time it reports, so this only fails for a cluster row without
|
|
1121
|
+
* an identifier. The enqueue chokepoint refuses the same case, so the
|
|
1122
|
+
* status must not call it ready.
|
|
1123
|
+
*/
|
|
1124
|
+
const isInClusterForThisCluster: boolean = isInClusterPostureForCluster(
|
|
1125
|
+
posture,
|
|
1126
|
+
cluster.clusterIdentifier,
|
|
1127
|
+
);
|
|
1128
|
+
|
|
1129
|
+
if (isOnline && !isInClusterForThisCluster) {
|
|
1130
|
+
gaps.push({
|
|
1131
|
+
code: "runner_cluster_mismatch",
|
|
1132
|
+
title: "The Kubernetes AI agent has not reported this cluster",
|
|
1133
|
+
description:
|
|
1134
|
+
"The agent's last report does not name this cluster, so OneUptime AI does not use it yet.",
|
|
1135
|
+
nextStep: `Reset the agent on ${CLUSTER_AI_AGENT_PAGE}; it reconnects on its own within a few minutes.`,
|
|
1136
|
+
blocks: "both",
|
|
1137
|
+
});
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
if (
|
|
1141
|
+
remediationMode !== KubernetesAiRemediationMode.Disabled &&
|
|
1142
|
+
posture?.allowWrites !== true
|
|
1143
|
+
) {
|
|
1144
|
+
gaps.push({
|
|
1145
|
+
code: "remediation_write_access_missing",
|
|
1146
|
+
title: "The Kubernetes AI agent is read-only",
|
|
1147
|
+
description:
|
|
1148
|
+
"The Kubernetes AI agent was installed without write access, so it cannot apply fixes.",
|
|
1149
|
+
nextStep: getRemediationWriteAccessNextStep("ai_agent"),
|
|
1150
|
+
blocks: "remediation",
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
return {
|
|
1155
|
+
runner,
|
|
1156
|
+
accessMethod: isInClusterForThisCluster ? "in_cluster" : "none",
|
|
1157
|
+
gaps,
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/*
|
|
1162
|
+
* A Runner as the target: the chart's previous in-cluster Runner of this
|
|
1163
|
+
* cluster (isLegacy), or a Runner an operator bound, which reaches the
|
|
1164
|
+
* cluster with the cluster's Kubernetes credential.
|
|
682
1165
|
*/
|
|
1166
|
+
private async getRunnerAccess(data: {
|
|
1167
|
+
cluster: KubernetesCluster;
|
|
1168
|
+
runner: Runner;
|
|
1169
|
+
isLegacy: boolean;
|
|
1170
|
+
remediationMode: KubernetesAiRemediationMode;
|
|
1171
|
+
}): Promise<ResolvedTargetAccess> {
|
|
1172
|
+
const { cluster, runner, isLegacy, remediationMode } = data;
|
|
1173
|
+
const gaps: Array<KubernetesAiAccessGap> = [];
|
|
1174
|
+
const posture: KubernetesRunnerPosture | undefined =
|
|
1175
|
+
parseKubernetesRunnerPosture(runner.hostInfo);
|
|
1176
|
+
const presence: RunnerPresence = this.getRunnerPresence(runner);
|
|
1177
|
+
|
|
1178
|
+
let accessMethod: KubernetesClusterAiAccessStatus["accessMethod"] = "none";
|
|
1179
|
+
let credentialId: string | undefined = undefined;
|
|
1180
|
+
let credentialName: string | undefined = undefined;
|
|
1181
|
+
|
|
1182
|
+
const summary: KubernetesAiAccessRunnerSummary = {
|
|
1183
|
+
id: runner.id!.toString(),
|
|
1184
|
+
name: runner.name || "Runner",
|
|
1185
|
+
kind: "runner",
|
|
1186
|
+
isOnline: presence.isOnline,
|
|
1187
|
+
lastAliveAt: runner.lastAlive
|
|
1188
|
+
? OneUptimeDate.toString(runner.lastAlive)
|
|
1189
|
+
: undefined,
|
|
1190
|
+
canRunAiCommands: runner.canRunAiCommands === true,
|
|
1191
|
+
posture,
|
|
1192
|
+
};
|
|
1193
|
+
|
|
1194
|
+
if (!presence.isOnline) {
|
|
1195
|
+
gaps.push(this.getRunnerOfflineGap({ runner, presence, isLegacy }));
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
if (runner.canRunAiCommands !== true) {
|
|
1199
|
+
gaps.push({
|
|
1200
|
+
code: "runner_ai_commands_disabled",
|
|
1201
|
+
title: "The Runner does not accept AI commands",
|
|
1202
|
+
description: `"Runs AI Remediation Commands" is turned off on Runner "${runner.name}", so it will not be served kubectl work.`,
|
|
1203
|
+
nextStep:
|
|
1204
|
+
'Turn on "Runs AI Remediation Commands" on the Runner (Project Settings → Runners).',
|
|
1205
|
+
blocks: "both",
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
/*
|
|
1210
|
+
* In-cluster access means "kubectl with the pod's own ServiceAccount",
|
|
1211
|
+
* which reaches whatever cluster the pod lives in. So it is usable only
|
|
1212
|
+
* when the Runner's posture names THIS cluster — a Runner that is
|
|
1213
|
+
* in-cluster somewhere else would run every command for this cluster
|
|
1214
|
+
* against the wrong one. The previous in-cluster Runner of this cluster
|
|
1215
|
+
* ignores a credential still selected: in-cluster access wins.
|
|
1216
|
+
*/
|
|
1217
|
+
const isInClusterForThisCluster: boolean = isInClusterPostureForCluster(
|
|
1218
|
+
posture,
|
|
1219
|
+
cluster.clusterIdentifier,
|
|
1220
|
+
);
|
|
1221
|
+
|
|
1222
|
+
if (isInClusterForThisCluster) {
|
|
1223
|
+
accessMethod = "in_cluster";
|
|
1224
|
+
} else if (isLegacy) {
|
|
1225
|
+
/*
|
|
1226
|
+
* This cluster's previous in-cluster Runner by its name, but its last
|
|
1227
|
+
* report does not say it runs here (a heartbeat that dropped its
|
|
1228
|
+
* posture). It is never given a credential, so it cannot be used —
|
|
1229
|
+
* and the AI agent, which needs neither, is the way forward.
|
|
1230
|
+
*/
|
|
1231
|
+
gaps.push({
|
|
1232
|
+
code: "runner_cluster_mismatch",
|
|
1233
|
+
title: "The previous in-cluster Runner has not reported this cluster",
|
|
1234
|
+
description: `Runner "${runner.name}" has not reported that it runs in this cluster, so OneUptime AI does not use it.`,
|
|
1235
|
+
nextStep: `Upgrade the Kubernetes agent chart: the Kubernetes AI agent replaces this Runner and your settings carry over.\n\n${AI_AGENT_INSTALL_COMMAND}`,
|
|
1236
|
+
blocks: "both",
|
|
1237
|
+
});
|
|
1238
|
+
} else if (cluster.aiAccessCredentialId) {
|
|
1239
|
+
const credential: RunbookCredential | null =
|
|
1240
|
+
await RunbookCredentialService.findOneBy({
|
|
1241
|
+
query: {
|
|
1242
|
+
_id: cluster.aiAccessCredentialId.toString(),
|
|
1243
|
+
projectId: cluster.projectId!,
|
|
1244
|
+
},
|
|
1245
|
+
select: {
|
|
1246
|
+
_id: true,
|
|
1247
|
+
name: true,
|
|
1248
|
+
credentialType: true,
|
|
1249
|
+
runners: { _id: true },
|
|
1250
|
+
},
|
|
1251
|
+
props: { isRoot: true },
|
|
1252
|
+
});
|
|
1253
|
+
|
|
1254
|
+
const isAssignedToRunner: boolean = Boolean(
|
|
1255
|
+
credential?.runners?.some((assigned: Runner) => {
|
|
1256
|
+
return assigned.id?.toString() === runner.id?.toString();
|
|
1257
|
+
}),
|
|
1258
|
+
);
|
|
1259
|
+
|
|
1260
|
+
if (
|
|
1261
|
+
!credential ||
|
|
1262
|
+
credential.credentialType !== RunbookCredentialType.Kubernetes ||
|
|
1263
|
+
!isAssignedToRunner
|
|
1264
|
+
) {
|
|
1265
|
+
gaps.push({
|
|
1266
|
+
code: "credential_missing",
|
|
1267
|
+
title: "The Kubernetes credential is not usable by the Runner",
|
|
1268
|
+
description: !credential
|
|
1269
|
+
? "The credential bound to this cluster no longer exists."
|
|
1270
|
+
: credential.credentialType !== RunbookCredentialType.Kubernetes
|
|
1271
|
+
? `"${credential.name}" is not a Kubernetes credential.`
|
|
1272
|
+
: `"${credential.name}" is not assigned to Runner "${runner.name}".`,
|
|
1273
|
+
nextStep: `Create a Kubernetes credential (API server URL + ServiceAccount token) under Project Settings → Runner Credentials, assign it to the Runner, and select it on ${CLUSTER_AI_AGENT_PAGE}.`,
|
|
1274
|
+
blocks: "both",
|
|
1275
|
+
});
|
|
1276
|
+
} else {
|
|
1277
|
+
accessMethod = "credential";
|
|
1278
|
+
credentialId = credential.id!.toString();
|
|
1279
|
+
credentialName = credential.name;
|
|
1280
|
+
}
|
|
1281
|
+
} else if (posture?.inCluster) {
|
|
1282
|
+
/*
|
|
1283
|
+
* In-cluster, but for no named cluster. Say so explicitly: the
|
|
1284
|
+
* operator picked this Runner believing "in-cluster" meant "in this
|
|
1285
|
+
* cluster". (A Runner that names a cluster reports an agent posture,
|
|
1286
|
+
* so it is a kubernetes-agent row and never reaches this branch:
|
|
1287
|
+
* another cluster's is no target at all — see
|
|
1288
|
+
* getIgnoredBoundRunnerNote.)
|
|
1289
|
+
*/
|
|
1290
|
+
gaps.push({
|
|
1291
|
+
code: "runner_cluster_mismatch",
|
|
1292
|
+
title: "The bound Runner did not report which cluster it runs in",
|
|
1293
|
+
description: `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.`,
|
|
1294
|
+
nextStep: `Clear the Runner on ${CLUSTER_AI_AGENT_PAGE} to use this cluster's Kubernetes AI agent, or select a Kubernetes credential for this cluster that is assigned to the Runner.`,
|
|
1295
|
+
blocks: "both",
|
|
1296
|
+
});
|
|
1297
|
+
} else {
|
|
1298
|
+
gaps.push({
|
|
1299
|
+
code: "credential_missing",
|
|
1300
|
+
title: "The Runner has no credential for this cluster",
|
|
1301
|
+
description: `Runner "${runner.name}" runs outside the cluster, so it needs a Kubernetes credential to reach the API server.`,
|
|
1302
|
+
nextStep: `Select a Kubernetes credential assigned to this Runner on ${CLUSTER_AI_AGENT_PAGE} — or clear the Runner there to use the Kubernetes AI agent, which needs no credential.`,
|
|
1303
|
+
blocks: "both",
|
|
1304
|
+
});
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
if (
|
|
1308
|
+
remediationMode !== KubernetesAiRemediationMode.Disabled &&
|
|
1309
|
+
posture?.inCluster &&
|
|
1310
|
+
posture.allowWrites !== true
|
|
1311
|
+
) {
|
|
1312
|
+
gaps.push({
|
|
1313
|
+
code: "remediation_write_access_missing",
|
|
1314
|
+
title: isLegacy
|
|
1315
|
+
? "The previous in-cluster Runner is read-only"
|
|
1316
|
+
: "The Runner is read-only",
|
|
1317
|
+
description: isLegacy
|
|
1318
|
+
? "The Kubernetes agent was installed without write access, so kubectl changes would be refused by the cluster."
|
|
1319
|
+
: `Runner "${runner.name}" reports that it may not change the cluster, so kubectl changes would be refused.`,
|
|
1320
|
+
nextStep: getRemediationWriteAccessNextStep(
|
|
1321
|
+
isLegacy ? "legacy_runner" : "credential_runner",
|
|
1322
|
+
),
|
|
1323
|
+
blocks: "remediation",
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
return {
|
|
1328
|
+
runner: summary,
|
|
1329
|
+
accessMethod,
|
|
1330
|
+
credentialId,
|
|
1331
|
+
credentialName,
|
|
1332
|
+
gaps,
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
|
|
683
1336
|
private getRunnerPresence(runner: Runner): RunnerPresence {
|
|
684
1337
|
const liveStatus: RunnerLiveStatus = getRunnerLiveStatus(runner.lastAlive);
|
|
685
1338
|
|
|
1339
|
+
/*
|
|
1340
|
+
* A freshly created row is Disconnected with no heartbeat yet: that is
|
|
1341
|
+
* "never connected", not a sign-off.
|
|
1342
|
+
*/
|
|
686
1343
|
const isSignedOff: boolean =
|
|
687
1344
|
runner.connectionStatus === RunnerConnectionStatus.Disconnected &&
|
|
688
1345
|
liveStatus !== RunnerLiveStatus.NeverConnected;
|
|
@@ -697,26 +1354,39 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
697
1354
|
private getRunnerOfflineGap(data: {
|
|
698
1355
|
runner: Runner;
|
|
699
1356
|
presence: RunnerPresence;
|
|
700
|
-
// The Runner is
|
|
701
|
-
|
|
1357
|
+
// The Runner is this cluster's previous in-cluster Runner.
|
|
1358
|
+
isLegacy: boolean;
|
|
702
1359
|
}): KubernetesAiAccessGap {
|
|
703
|
-
const { runner, presence,
|
|
1360
|
+
const { runner, presence, isLegacy } = data;
|
|
704
1361
|
const lastAliveText: string = runner.lastAlive
|
|
705
1362
|
? OneUptimeDate.getDateAsFormattedString(runner.lastAlive)
|
|
706
1363
|
: "an unknown time";
|
|
707
1364
|
|
|
1365
|
+
/*
|
|
1366
|
+
* The previous in-cluster Runner is not coming back by itself after a
|
|
1367
|
+
* chart upgrade (the new chart does not install it), and there is
|
|
1368
|
+
* nothing to fix in it: the step is the upgrade to the AI agent.
|
|
1369
|
+
*/
|
|
1370
|
+
if (isLegacy) {
|
|
1371
|
+
return {
|
|
1372
|
+
code: "runner_offline",
|
|
1373
|
+
title: presence.isSignedOff
|
|
1374
|
+
? "The previous in-cluster Runner signed off"
|
|
1375
|
+
: "The previous in-cluster Runner is offline",
|
|
1376
|
+
description: presence.isSignedOff
|
|
1377
|
+
? `Runner "${runner.name}" shut down after its last report at ${lastAliveText}.`
|
|
1378
|
+
: `Runner "${runner.name}" last reported in at ${lastAliveText}.`,
|
|
1379
|
+
nextStep: `Upgrade the Kubernetes agent chart: the Kubernetes AI agent replaces this Runner and your settings carry over.\n\n${AI_AGENT_INSTALL_COMMAND}`,
|
|
1380
|
+
blocks: "both",
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
|
|
708
1384
|
if (presence.isSignedOff) {
|
|
709
1385
|
return {
|
|
710
1386
|
code: "runner_offline",
|
|
711
|
-
title:
|
|
712
|
-
|
|
713
|
-
|
|
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.",
|
|
1387
|
+
title: "The Runner signed off",
|
|
1388
|
+
description: `Runner "${runner.name}" shut down cleanly after its last heartbeat at ${lastAliveText}: its container was stopped.`,
|
|
1389
|
+
nextStep: `Start the Runner container again, or clear the Runner on ${CLUSTER_AI_AGENT_PAGE} to use the Kubernetes AI agent.`,
|
|
720
1390
|
blocks: "both",
|
|
721
1391
|
};
|
|
722
1392
|
}
|
|
@@ -732,59 +1402,8 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
732
1402
|
description: isNeverConnected
|
|
733
1403
|
? `Runner "${runner.name}" exists but has not reported in yet.`
|
|
734
1404
|
: `Runner "${runner.name}" last reported in at ${lastAliveText}.`,
|
|
735
|
-
nextStep:
|
|
736
|
-
|
|
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()}`,
|
|
1405
|
+
nextStep:
|
|
1406
|
+
"Start the Runner container and make sure it can reach your OneUptime URL.",
|
|
788
1407
|
blocks: "both",
|
|
789
1408
|
};
|
|
790
1409
|
}
|
|
@@ -805,95 +1424,6 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
805
1424
|
.join("; ");
|
|
806
1425
|
}
|
|
807
1426
|
|
|
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
1427
|
// The Runner's reported posture names a cluster, and it is not this one.
|
|
898
1428
|
private postureNamesAnotherCluster(data: {
|
|
899
1429
|
runner: Runner;
|
|
@@ -1064,10 +1594,56 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1064
1594
|
const gates: KubernetesClusterAiAccessProjectGates =
|
|
1065
1595
|
await this.getProjectGates(data.projectId);
|
|
1066
1596
|
|
|
1597
|
+
return this.getStatusesForClusters({
|
|
1598
|
+
projectId: data.projectId,
|
|
1599
|
+
clusters,
|
|
1600
|
+
gates,
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
/*
|
|
1605
|
+
* The status of several clusters of one project, with every cluster's
|
|
1606
|
+
* agent row read in ONE query instead of one per cluster. When that read
|
|
1607
|
+
* fails, each status reads its own row (and treats a failure there as no
|
|
1608
|
+
* agent), exactly as a single status would.
|
|
1609
|
+
*/
|
|
1610
|
+
@CaptureSpan()
|
|
1611
|
+
public async getStatusesForClusters(data: {
|
|
1612
|
+
projectId: ObjectID;
|
|
1613
|
+
clusters: Array<KubernetesCluster>;
|
|
1614
|
+
gates: KubernetesClusterAiAccessProjectGates;
|
|
1615
|
+
}): Promise<Array<KubernetesClusterAiAccessStatus>> {
|
|
1616
|
+
let agentRows: Map<string, KubernetesAiAgent> | null = null;
|
|
1617
|
+
|
|
1618
|
+
try {
|
|
1619
|
+
agentRows = await KubernetesAiAgentService.findForClusters({
|
|
1620
|
+
projectId: data.projectId,
|
|
1621
|
+
kubernetesClusterIds: data.clusters
|
|
1622
|
+
.map((cluster: KubernetesCluster): ObjectID | undefined => {
|
|
1623
|
+
return cluster.id || undefined;
|
|
1624
|
+
})
|
|
1625
|
+
.filter((id: ObjectID | undefined): id is ObjectID => {
|
|
1626
|
+
return Boolean(id);
|
|
1627
|
+
}),
|
|
1628
|
+
});
|
|
1629
|
+
} catch (error) {
|
|
1630
|
+
logger.error(
|
|
1631
|
+
`KubernetesClusterAiAccess: could not read the Kubernetes AI agents of project ${data.projectId.toString()}; reading them one cluster at a time: ${error}`,
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1067
1635
|
const statuses: Array<KubernetesClusterAiAccessStatus> = [];
|
|
1068
1636
|
|
|
1069
|
-
for (const cluster of clusters) {
|
|
1070
|
-
statuses.push(
|
|
1637
|
+
for (const cluster of data.clusters) {
|
|
1638
|
+
statuses.push(
|
|
1639
|
+
await this.getStatusForClusterModel({
|
|
1640
|
+
cluster,
|
|
1641
|
+
gates: data.gates,
|
|
1642
|
+
aiAgentRow: agentRows
|
|
1643
|
+
? agentRows.get(cluster.id?.toString() || "") || null
|
|
1644
|
+
: undefined,
|
|
1645
|
+
}),
|
|
1646
|
+
);
|
|
1071
1647
|
}
|
|
1072
1648
|
|
|
1073
1649
|
return statuses;
|
|
@@ -1075,12 +1651,14 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1075
1651
|
|
|
1076
1652
|
/*
|
|
1077
1653
|
* ------------------------------------------------------------------
|
|
1078
|
-
*
|
|
1654
|
+
* Previous in-cluster Runner registration
|
|
1079
1655
|
* ------------------------------------------------------------------
|
|
1080
1656
|
*/
|
|
1081
1657
|
|
|
1082
1658
|
/*
|
|
1083
|
-
* Called by the Runner
|
|
1659
|
+
* Called by the in-cluster Runner older kubernetes-agent charts deploy
|
|
1660
|
+
* (the Kubernetes AI agent replaces it and registers through
|
|
1661
|
+
* KubernetesAiAgentService instead). Authenticated
|
|
1084
1662
|
* by the project's telemetry ingestion key (the same key the agent ships
|
|
1085
1663
|
* telemetry with), so installing the chart with one extra flag is the
|
|
1086
1664
|
* whole setup. Idempotent per (project, cluster): a restarted pod gets a
|
|
@@ -1111,15 +1689,30 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1111
1689
|
* secret assigned to it, runbooks or code fixes turned on, or another
|
|
1112
1690
|
* cluster bound to it — is refused: re-keying it would hand all of that
|
|
1113
1691
|
* to whoever holds the ingestion key. The operator removes the extras
|
|
1114
|
-
* (the Runner then comes back still bound), or
|
|
1115
|
-
* the
|
|
1116
|
-
* page — a deleted Runner's cluster is left unbound, see below.
|
|
1692
|
+
* (the Runner then comes back still bound), or upgrades the chart to
|
|
1693
|
+
* the Kubernetes AI agent, which needs no Runner at all.
|
|
1117
1694
|
* - It finds the row it may re-key by this cluster's agent Runner NAME
|
|
1118
1695
|
* only — never by the posture a Runner reports about itself — and only
|
|
1119
1696
|
* binds a cluster that never had a Runner bound: a cluster whose
|
|
1120
1697
|
* binding was cleared (or whose Runner was deleted) stays unbound, and
|
|
1121
1698
|
* an operator's AI settings are never overwritten, only the chart's
|
|
1122
1699
|
* defaults fill in a cluster nobody configured.
|
|
1700
|
+
* - While the cluster's Kubernetes AI agent is online the Runner is
|
|
1701
|
+
* refused (superseded_by_ai_agent) before anything is written: the
|
|
1702
|
+
* agent replaces it. The refusal clears on its own once the agent
|
|
1703
|
+
* stops, so a helm rollback to a chart with the Runner works again for
|
|
1704
|
+
* a cluster still bound to that Runner (already_bound — every cluster
|
|
1705
|
+
* the Runner served before the upgrade).
|
|
1706
|
+
* - A cluster that has a Kubernetes AI agent row is never given the
|
|
1707
|
+
* chart's defaults by a Runner's first bind (the agent's own first
|
|
1708
|
+
* connection decides those). The binding rules below are unchanged by
|
|
1709
|
+
* the agent row: a cluster that never had a Runner bound and never ran
|
|
1710
|
+
* kubectl is bound with no switch moved, but one where AI already ran
|
|
1711
|
+
* kubectl — through the agent too: its commands and "Test connection"
|
|
1712
|
+
* record the same history — stays unbound (left_unbound_by_operator).
|
|
1713
|
+
* That history cannot tell the agent's commands from a revoked
|
|
1714
|
+
* Runner's, so it fails closed: nothing reaches the cluster until its
|
|
1715
|
+
* agent is back online.
|
|
1123
1716
|
* - Every refusal is a KubernetesAgentRegistrationRefusedException whose
|
|
1124
1717
|
* reason tells the Runner whether waiting helps.
|
|
1125
1718
|
*
|
|
@@ -1177,6 +1770,30 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1177
1770
|
throw new BadDataException("Cluster could not be resolved.");
|
|
1178
1771
|
}
|
|
1179
1772
|
|
|
1773
|
+
/*
|
|
1774
|
+
* The Kubernetes AI agent replaces this Runner: while the cluster's
|
|
1775
|
+
* agent is online nothing is created, re-keyed or bound. Checked
|
|
1776
|
+
* first, so a rolled-back pod that keeps retrying during an upgrade
|
|
1777
|
+
* never takes the cluster back from a working agent.
|
|
1778
|
+
*/
|
|
1779
|
+
const aiAgent: KubernetesAiAgent | null =
|
|
1780
|
+
await KubernetesAiAgentService.findForCluster({
|
|
1781
|
+
projectId: data.projectId,
|
|
1782
|
+
kubernetesClusterId: cluster.id!,
|
|
1783
|
+
});
|
|
1784
|
+
|
|
1785
|
+
if (aiAgent && KubernetesAiAgentService.isOnline(aiAgent)) {
|
|
1786
|
+
logger.warn(
|
|
1787
|
+
`KubernetesClusterAiAccess: refused to register the in-cluster Runner of cluster "${clusterIdentifier}" in project ${data.projectId.toString()}: the cluster's Kubernetes AI agent is online and replaces it.`,
|
|
1788
|
+
);
|
|
1789
|
+
|
|
1790
|
+
throw new KubernetesAgentRegistrationRefusedException({
|
|
1791
|
+
reason: "superseded_by_ai_agent",
|
|
1792
|
+
retryAfterSeconds: SUPERSEDED_BY_AI_AGENT_RETRY_AFTER_SECONDS,
|
|
1793
|
+
message: getSupersededByAiAgentMessage(clusterIdentifier),
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1180
1797
|
/*
|
|
1181
1798
|
* What the row stores: the body's posture, with the cluster and
|
|
1182
1799
|
* in-cluster set by the service. A re-key may fill in the write scope
|
|
@@ -1239,7 +1856,7 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1239
1856
|
|
|
1240
1857
|
throw new KubernetesAgentRegistrationRefusedException({
|
|
1241
1858
|
reason: "runner_belongs_to_another_cluster",
|
|
1242
|
-
message: `
|
|
1859
|
+
message: `Upgrade the Kubernetes agent chart of cluster "${clusterIdentifier}": its Kubernetes AI agent replaces the in-cluster Runner and does not need Runner "${runner.name}". 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}".`,
|
|
1243
1860
|
});
|
|
1244
1861
|
}
|
|
1245
1862
|
|
|
@@ -1319,13 +1936,21 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1319
1936
|
* binding's foreign key is ON DELETE SET NULL, so Postgres clears
|
|
1320
1937
|
* aiAccessRunnerId in the same statement. Deleting the Runner is
|
|
1321
1938
|
* how an operator revokes or resets it, so it must not come back
|
|
1322
|
-
* bound a few heartbeats later.
|
|
1323
|
-
*
|
|
1939
|
+
* bound a few heartbeats later. Such a cluster uses its
|
|
1940
|
+
* Kubernetes AI agent instead.
|
|
1941
|
+
* History counts whoever made it: with a Kubernetes AI agent row
|
|
1942
|
+
* it is usually the agent's (it records every command and "Test
|
|
1943
|
+
* connection" on the cluster), but it cannot be told apart from a
|
|
1944
|
+
* revoked Runner's, so such a cluster stays unbound too and is
|
|
1945
|
+
* reached again once its agent is back online. A rollback keeps
|
|
1946
|
+
* working through the Runner a cluster is still bound to
|
|
1947
|
+
* (already_bound above), not through a new binding here.
|
|
1324
1948
|
* - that never had a Runner bound but was configured on the AI page
|
|
1325
1949
|
* (aiAccessConfiguredAt: an operator chose a mode, the switches
|
|
1326
|
-
* or an allowlist BEFORE installing the chart)
|
|
1327
|
-
*
|
|
1328
|
-
*
|
|
1950
|
+
* or an allowlist BEFORE installing the chart), or that already
|
|
1951
|
+
* has a Kubernetes AI agent row that never ran kubectl here, is
|
|
1952
|
+
* bound to this agent Runner with every setting left exactly as
|
|
1953
|
+
* it is — the one-flag install still just works.
|
|
1329
1954
|
* - that nobody ever configured takes the chart's intent:
|
|
1330
1955
|
* investigation on, remediation "ask for approval" when the chart
|
|
1331
1956
|
* also granted write RBAC (a mode already on the row is kept).
|
|
@@ -1366,7 +1991,14 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1366
1991
|
}
|
|
1367
1992
|
} else if (hadRunnerBound || hasCommandHistory) {
|
|
1368
1993
|
bindingState = "left_unbound_by_operator";
|
|
1369
|
-
} else if (cluster.aiAccessConfiguredAt) {
|
|
1994
|
+
} else if (cluster.aiAccessConfiguredAt || aiAgent) {
|
|
1995
|
+
/*
|
|
1996
|
+
* Configured by an operator before the chart was installed, or the
|
|
1997
|
+
* cluster already has a Kubernetes AI agent (offline, or this
|
|
1998
|
+
* registration would have been refused) that has not run kubectl
|
|
1999
|
+
* here: bind, so AI works through this Runner, but leave every
|
|
2000
|
+
* switch — and the configured marker — exactly as they are.
|
|
2001
|
+
*/
|
|
1370
2002
|
bindingState = "bound_keeping_operator_settings";
|
|
1371
2003
|
|
|
1372
2004
|
// Only the binding: not one of the operator's switches moves.
|
|
@@ -1402,7 +2034,9 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1402
2034
|
|
|
1403
2035
|
if (bindingState === "left_unbound_by_operator") {
|
|
1404
2036
|
logger.info(
|
|
1405
|
-
`KubernetesClusterAiAccess: cluster "${clusterIdentifier}" in project ${data.projectId.toString()} had a Runner bound before (or has run kubectl
|
|
2037
|
+
`KubernetesClusterAiAccess: cluster "${clusterIdentifier}" in project ${data.projectId.toString()} had a Runner bound before (or has run kubectl${
|
|
2038
|
+
aiAgent ? ", possibly through its Kubernetes AI agent" : ""
|
|
2039
|
+
}) but has none bound now; the in-cluster Runner registered without binding it.`,
|
|
1406
2040
|
);
|
|
1407
2041
|
}
|
|
1408
2042
|
|
|
@@ -1419,6 +2053,11 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1419
2053
|
agentRunnerExistedBefore,
|
|
1420
2054
|
appliedRemediationMode,
|
|
1421
2055
|
reKeyAdmission,
|
|
2056
|
+
hasAiAgent: Boolean(aiAgent),
|
|
2057
|
+
hadRunnerBound,
|
|
2058
|
+
aiAgentPodNamespace: aiAgent
|
|
2059
|
+
? parseKubernetesAgentPosture(aiAgent.posture)?.podNamespace
|
|
2060
|
+
: undefined,
|
|
1422
2061
|
});
|
|
1423
2062
|
|
|
1424
2063
|
return {
|
|
@@ -1582,11 +2221,11 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1582
2221
|
*/
|
|
1583
2222
|
throw new KubernetesAgentRegistrationRefusedException({
|
|
1584
2223
|
reason: "runner_holds_more_than_defaults",
|
|
1585
|
-
message: `
|
|
2224
|
+
message: `Upgrade the Kubernetes agent chart: its Kubernetes AI agent replaces this in-cluster Runner. Or, under Project Settings → Runners, on Runner "${data.runner.name}": ${this.describeHoldingRemedies(
|
|
1586
2225
|
holdings,
|
|
1587
|
-
)}
|
|
2226
|
+
)}. It is offline, but it holds more than an in-cluster Runner's defaults (${this.describeHoldings(
|
|
1588
2227
|
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
|
|
2228
|
+
)}), 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.`,
|
|
1590
2229
|
});
|
|
1591
2230
|
}
|
|
1592
2231
|
|
|
@@ -1605,9 +2244,10 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1605
2244
|
* kubectl access only (no runbooks, no code fixes, nothing assigned) bound
|
|
1606
2245
|
* to its own cluster; anything else was entrusted to the row by an
|
|
1607
2246
|
* operator. "Runs Runbooks" is read the way the claim path reads it: on
|
|
1608
|
-
* unless explicitly false.
|
|
2247
|
+
* unless explicitly false. Also what decides whether an unused previous
|
|
2248
|
+
* in-cluster Runner may be retired (KubernetesAiAgentService).
|
|
1609
2249
|
*/
|
|
1610
|
-
|
|
2250
|
+
public async getRunnerHoldingsBeyondDefaults(data: {
|
|
1611
2251
|
runner: Runner;
|
|
1612
2252
|
clusterId: ObjectID;
|
|
1613
2253
|
projectId: ObjectID;
|
|
@@ -1676,7 +2316,7 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1676
2316
|
if (otherBoundClusters > 0) {
|
|
1677
2317
|
holdings.push({
|
|
1678
2318
|
description: `the AI access Runner of ${otherBoundClusters} other cluster(s)`,
|
|
1679
|
-
remedy: `
|
|
2319
|
+
remedy: `clear the Runner on the AI agent page of the ${otherBoundClusters} other cluster(s) bound to it`,
|
|
1680
2320
|
});
|
|
1681
2321
|
}
|
|
1682
2322
|
|
|
@@ -1750,6 +2390,12 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1750
2390
|
appliedRemediationMode?: KubernetesAiRemediationMode | undefined;
|
|
1751
2391
|
// How an existing row's re-key was admitted; undefined for a new row.
|
|
1752
2392
|
reKeyAdmission?: AgentRunnerReKeyAdmission | undefined;
|
|
2393
|
+
// The cluster has a Kubernetes AI agent row (offline: this one got in).
|
|
2394
|
+
hasAiAgent?: boolean | undefined;
|
|
2395
|
+
// A Runner was bound to the cluster before (aiAccessRunnerBoundAt).
|
|
2396
|
+
hadRunnerBound?: boolean | undefined;
|
|
2397
|
+
// The namespace the cluster's agent reported, when known.
|
|
2398
|
+
aiAgentPodNamespace?: string | undefined;
|
|
1753
2399
|
}): Promise<void> {
|
|
1754
2400
|
try {
|
|
1755
2401
|
const runnerName: string = data.runner.name || "kubernetes-agent";
|
|
@@ -1768,15 +2414,36 @@ class KubernetesClusterAiAccessServiceClass {
|
|
|
1768
2414
|
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
2415
|
data.appliedRemediationMode,
|
|
1770
2416
|
)}.`;
|
|
2417
|
+
} else if (
|
|
2418
|
+
data.bindingState === "bound_keeping_operator_settings" &&
|
|
2419
|
+
data.hasAiAgent &&
|
|
2420
|
+
!data.cluster.aiAccessConfiguredAt
|
|
2421
|
+
) {
|
|
2422
|
+
displayColor = Green500;
|
|
2423
|
+
feedInfoInMarkdown = `🤖 The in-cluster Runner **${runnerName}** ${registered} from the Kubernetes agent chart (${writes}) while this cluster's Kubernetes AI agent is offline, and was bound so AI works through it: no Runner was bound here before and AI had not run kubectl here yet. No AI setting was changed. The Kubernetes AI agent takes over again as soon as it is back online.`;
|
|
1771
2424
|
} else if (data.bindingState === "bound_keeping_operator_settings") {
|
|
1772
2425
|
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 ${
|
|
2426
|
+
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 agent page, so no AI setting was changed: AI investigation with kubectl is ${
|
|
1774
2427
|
data.cluster.isAiInvestigationEnabled === true ? "on" : "off"
|
|
1775
2428
|
} and AI remediation is ${this.describeRemediationMode(
|
|
1776
2429
|
data.cluster.aiRemediationMode,
|
|
1777
2430
|
)}, as an operator chose.`;
|
|
2431
|
+
} else if (
|
|
2432
|
+
data.bindingState === "left_unbound_by_operator" &&
|
|
2433
|
+
data.hasAiAgent &&
|
|
2434
|
+
!data.hadRunnerBound &&
|
|
2435
|
+
!data.cluster.aiAccessRunnerId
|
|
2436
|
+
) {
|
|
2437
|
+
/*
|
|
2438
|
+
* No Runner was ever bound, so the history that kept this one
|
|
2439
|
+
* unbound is most likely the agent's own — not an operator's
|
|
2440
|
+
* doing. Say that, and where AI access comes back from.
|
|
2441
|
+
*/
|
|
2442
|
+
feedInfoInMarkdown = `🤖 The in-cluster Runner **${runnerName}** ${registered} (${writes}) while this cluster's Kubernetes AI agent is offline, and was left unbound: AI has already run kubectl on this cluster (through its Kubernetes AI agent, or a Runner that is no longer bound), and a registering Runner is never bound over that history. No AI setting was changed. AI reaches this cluster again when its Kubernetes AI agent is back online: check the agent's pod with \`${getAiAgentLogsCommand(
|
|
2443
|
+
data.aiAgentPodNamespace,
|
|
2444
|
+
)}\`, or upgrade the Kubernetes agent chart again.`;
|
|
1778
2445
|
} 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.
|
|
2446
|
+
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. Upgrade the Kubernetes agent chart to use the Kubernetes AI agent instead.`;
|
|
1780
2447
|
} else if (data.bindingState === "bound_to_other_runner") {
|
|
1781
2448
|
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
2449
|
} else if (data.reKeyAdmission === "continuity") {
|