@oneuptime/common 14.0.9 → 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 +2 -0
- package/Models/DatabaseModels/PodmanHost.ts +213 -0
- package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
- package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
- package/Models/DatabaseModels/RunnerJob.ts +144 -0
- package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
- package/Server/API/AutoRemediationAPI.ts +392 -1
- package/Server/API/ResourceAiAccessAPI.ts +1669 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Infrastructure/Semaphore.ts +22 -0
- package/Server/Middleware/TelemetryIngest.ts +15 -5
- package/Server/Services/AnalyticsDatabaseService.ts +45 -1
- package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
- 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/MetricRecordingRuleService.ts +47 -0
- package/Server/Services/PodmanHostService.ts +109 -1
- package/Server/Services/ProxmoxClusterService.ts +110 -1
- 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 +611 -4
- 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/Remediation/RemediationCommandTools.ts +1434 -32
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +935 -21
- 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 +72 -2
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +396 -13
- 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/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 +366 -143
- 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/Dashboard/ClusterAccessNotice.test.tsx +93 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
- package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -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/RunbookStepTypeMaps.test.ts +38 -7
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
- package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
- package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
- package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
- package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
- package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
- package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +63 -5
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +103 -5
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +19 -1
- package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
- 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/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
- 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 +105 -0
- package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
- package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
- package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
- package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
- package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -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/RemediationCommandTools.test.ts +94 -1
- package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1249 -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/CommandPlanExecutorResource.test.ts +717 -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/TelemetryIngestionKeyGuard.test.ts +33 -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/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -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/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/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
- 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/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
- package/Types/AI/ResourceAiAccessApi.ts +140 -0
- package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +183 -2
- 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/Telemetry/TelemetryIngestSurface.ts +14 -2
- package/Utils/AI/InvestigationReport.ts +121 -15
- 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/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 +2 -0
- package/build/dist/Models/DatabaseModels/Index.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/ProxmoxCluster.js +219 -0
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.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 +145 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.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/Server/API/AutoRemediationAPI.js +231 -1
- package/build/dist/Server/API/AutoRemediationAPI.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/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 +2 -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 +12 -5
- package/build/dist/Server/Middleware/TelemetryIngest.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/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/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/ProxmoxClusterService.js +104 -0
- package/build/dist/Server/Services/ProxmoxClusterService.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 +344 -5
- package/build/dist/Server/Services/RunnerJobService.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/Remediation/RemediationCommandTools.js +1029 -30
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +633 -28
- 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 +50 -4
- 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/AutoRemediation/CommandPlanExecutor.js +290 -13
- 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/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 +263 -110
- 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/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 +116 -29
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.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/Telemetry/TelemetryIngestSurface.js +13 -2
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.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/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/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
- package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import {
|
|
6
|
+
COMPOSE_DIRECTORY_COMMENT,
|
|
7
|
+
RESOURCE_AI_AGENT_IMAGE,
|
|
8
|
+
RESOURCE_AI_PROTECTED_TARGETS_ENV,
|
|
9
|
+
ResourceAiAgentInstall,
|
|
10
|
+
ResourceAiAgentInstallVariable,
|
|
11
|
+
ResourceAiAgentWriteAccessCommands,
|
|
12
|
+
doesCollectorShipResourceAiAgent,
|
|
13
|
+
getResourceAiAgentComposeSnippet,
|
|
14
|
+
getResourceAiAgentDirectory,
|
|
15
|
+
getResourceAiAgentInstall,
|
|
16
|
+
getResourceAiAgentLogsCommand,
|
|
17
|
+
getResourceAiAgentServiceName,
|
|
18
|
+
getResourceAiAgentStatusCommand,
|
|
19
|
+
getResourceAiAgentWriteAccessCommands,
|
|
20
|
+
getResourceAiAgentWriteDisclosure,
|
|
21
|
+
toComposeEnvironmentEntry,
|
|
22
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/ResourceAiAgent/ResourceAiAgentInstall";
|
|
23
|
+
import AiResourceType, {
|
|
24
|
+
AI_RESOURCE_TYPE_INFO,
|
|
25
|
+
ALL_AI_RESOURCE_TYPES,
|
|
26
|
+
AiResourceTypeInfo,
|
|
27
|
+
} from "../../../Types/ResourceAiAgent/AiResourceType";
|
|
28
|
+
import {
|
|
29
|
+
RESOURCE_AI_AGENT_DEFAULT_HEALTH_PORT,
|
|
30
|
+
RESOURCE_AI_AGENT_IMAGE_REPOSITORY,
|
|
31
|
+
RESOURCE_AI_AGENT_RESOURCE_NAME_ENV,
|
|
32
|
+
RESOURCE_AI_AGENT_RESOURCE_TYPE_ENV,
|
|
33
|
+
RESOURCE_AI_ALLOW_WRITES_ENV,
|
|
34
|
+
RESOURCE_AI_WRITE_TARGETS_ENV,
|
|
35
|
+
} from "../../../Types/ResourceAiAgent/ResourceAiAccess";
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* The install and write-access instructions a resource's AI agent page
|
|
39
|
+
* shows. Every name in them comes from the shared contract (the image, the
|
|
40
|
+
* resource-type and write variables, each type's alias and identity
|
|
41
|
+
* variables), the snippet is valid YAML an operator can paste, and it names
|
|
42
|
+
* the service exactly as the collector's own docker-compose.yml does when
|
|
43
|
+
* that file ships the agent.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
// Variables whose values are credentials, and values compose interpolates.
|
|
47
|
+
const SECRET_NAME_REGEX: RegExp = /TOKEN|KEY|PASSWORD|SECRET/;
|
|
48
|
+
const INTERPOLATED_REGEX: RegExp = /^\$\{/;
|
|
49
|
+
|
|
50
|
+
const RESOURCE_ID: string = "44444444-0000-4000-8000-000000000004";
|
|
51
|
+
|
|
52
|
+
const REPO_ROOT: string = path.resolve(__dirname, "../../../../..");
|
|
53
|
+
|
|
54
|
+
// The collector each type's agent runs beside.
|
|
55
|
+
const COLLECTOR_DIRECTORIES: Record<AiResourceType, string> = {
|
|
56
|
+
[AiResourceType.DockerHost]: "DockerAgent",
|
|
57
|
+
[AiResourceType.PodmanHost]: "PodmanAgent",
|
|
58
|
+
[AiResourceType.DockerSwarmCluster]: "DockerSwarmAgent",
|
|
59
|
+
[AiResourceType.ProxmoxCluster]: "ProxmoxAgent",
|
|
60
|
+
[AiResourceType.VMwareVCenter]: "VMwareAgent",
|
|
61
|
+
[AiResourceType.CephCluster]: "CephAgent",
|
|
62
|
+
[AiResourceType.DatabaseServer]: "DatabaseAgent",
|
|
63
|
+
[AiResourceType.Host]: "HostAIAgent",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
interface ComposeService {
|
|
67
|
+
image?: string;
|
|
68
|
+
container_name?: string;
|
|
69
|
+
user?: string;
|
|
70
|
+
volumes?: Array<string>;
|
|
71
|
+
environment?: Array<string> | Record<string, string>;
|
|
72
|
+
privileged?: boolean;
|
|
73
|
+
pid?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function parseSnippet(snippet: string): Record<string, ComposeService> {
|
|
77
|
+
const document: { services: Record<string, ComposeService> } = yaml.load(
|
|
78
|
+
snippet,
|
|
79
|
+
) as { services: Record<string, ComposeService> };
|
|
80
|
+
return document.services;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// A service's environment as name -> value, from either compose form.
|
|
84
|
+
function environmentOf(service: ComposeService): Record<string, string> {
|
|
85
|
+
if (!service.environment) {
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!Array.isArray(service.environment)) {
|
|
90
|
+
return service.environment;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const result: Record<string, string> = {};
|
|
94
|
+
|
|
95
|
+
for (const entry of service.environment) {
|
|
96
|
+
const at: number = entry.indexOf("=");
|
|
97
|
+
result[entry.slice(0, at)] = entry.slice(at + 1);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function snippetService(
|
|
104
|
+
type: AiResourceType,
|
|
105
|
+
identity?: string | null,
|
|
106
|
+
): ComposeService {
|
|
107
|
+
const services: Record<string, ComposeService> = parseSnippet(
|
|
108
|
+
getResourceAiAgentComposeSnippet({
|
|
109
|
+
resourceType: type,
|
|
110
|
+
resourceId: RESOURCE_ID,
|
|
111
|
+
identity,
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
const service: ComposeService | undefined =
|
|
115
|
+
services[getResourceAiAgentServiceName(type)];
|
|
116
|
+
|
|
117
|
+
if (!service) {
|
|
118
|
+
throw new Error(`The ${type} snippet has no agent service.`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return service;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// The agent service a collector's docker-compose.yml ships, if it does.
|
|
125
|
+
function collectorAgentService(
|
|
126
|
+
type: AiResourceType,
|
|
127
|
+
): { name: string; service: ComposeService } | null {
|
|
128
|
+
const file: string = path.join(
|
|
129
|
+
REPO_ROOT,
|
|
130
|
+
"agents",
|
|
131
|
+
COLLECTOR_DIRECTORIES[type],
|
|
132
|
+
"docker-compose.yml",
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (!fs.existsSync(file)) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const document: { services?: Record<string, ComposeService> } = yaml.load(
|
|
140
|
+
fs.readFileSync(file, "utf8"),
|
|
141
|
+
) as { services?: Record<string, ComposeService> };
|
|
142
|
+
|
|
143
|
+
for (const [name, service] of Object.entries(document.services || {})) {
|
|
144
|
+
if (
|
|
145
|
+
typeof service.image === "string" &&
|
|
146
|
+
service.image.startsWith(`${RESOURCE_AI_AGENT_IMAGE_REPOSITORY}:`)
|
|
147
|
+
) {
|
|
148
|
+
return { name, service };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/*
|
|
156
|
+
* The directory the collector's install.sh installs into (its
|
|
157
|
+
* INSTALL_DIR default), or null when it creates none (it starts plain
|
|
158
|
+
* containers instead).
|
|
159
|
+
*/
|
|
160
|
+
function installerDirectory(type: AiResourceType): string | null {
|
|
161
|
+
const installer: string = fs.readFileSync(
|
|
162
|
+
path.join(REPO_ROOT, "agents", COLLECTOR_DIRECTORIES[type], "install.sh"),
|
|
163
|
+
"utf8",
|
|
164
|
+
);
|
|
165
|
+
const match: RegExpMatchArray | null = installer.match(
|
|
166
|
+
/^INSTALL_DIR="(?:\$\{INSTALL_DIR:-)?([^"}]+)\}?"/m,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
return match ? match[1]! : null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
describe("the agent's service", () => {
|
|
173
|
+
test("is named after the agent alias, the collectors' convention", () => {
|
|
174
|
+
expect(
|
|
175
|
+
ALL_AI_RESOURCE_TYPES.map((type: AiResourceType): string => {
|
|
176
|
+
return getResourceAiAgentServiceName(type);
|
|
177
|
+
}),
|
|
178
|
+
).toEqual([
|
|
179
|
+
"oneuptime-docker-ai-agent",
|
|
180
|
+
"oneuptime-podman-ai-agent",
|
|
181
|
+
"oneuptime-docker-swarm-ai-agent",
|
|
182
|
+
"oneuptime-proxmox-ai-agent",
|
|
183
|
+
"oneuptime-vmware-ai-agent",
|
|
184
|
+
"oneuptime-ceph-ai-agent",
|
|
185
|
+
"oneuptime-database-ai-agent",
|
|
186
|
+
"oneuptime-host-ai-agent",
|
|
187
|
+
]);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test("runs the released resource AI agent image", () => {
|
|
191
|
+
expect(RESOURCE_AI_AGENT_IMAGE).toBe(
|
|
192
|
+
`${RESOURCE_AI_AGENT_IMAGE_REPOSITORY}:release`,
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("the compose snippet", () => {
|
|
198
|
+
test.each(ALL_AI_RESOURCE_TYPES)(
|
|
199
|
+
"%s: valid YAML with the image, read-only root and the contract's variables",
|
|
200
|
+
(type: AiResourceType) => {
|
|
201
|
+
const info: AiResourceTypeInfo = AI_RESOURCE_TYPE_INFO[type];
|
|
202
|
+
const service: ComposeService = snippetService(type);
|
|
203
|
+
const environment: Record<string, string> = environmentOf(service);
|
|
204
|
+
|
|
205
|
+
expect(service.image).toBe(RESOURCE_AI_AGENT_IMAGE);
|
|
206
|
+
expect((service as Record<string, unknown>)["read_only"]).toBe(true);
|
|
207
|
+
expect((service as Record<string, unknown>)["tmpfs"]).toEqual(["/tmp"]);
|
|
208
|
+
expect(environment[RESOURCE_AI_AGENT_RESOURCE_TYPE_ENV]).toBe(
|
|
209
|
+
info.agentAlias,
|
|
210
|
+
);
|
|
211
|
+
expect(environment["ONEUPTIME_URL"]).toBe("${ONEUPTIME_URL}");
|
|
212
|
+
// Read-only unless the .env says otherwise.
|
|
213
|
+
expect(environment[RESOURCE_AI_ALLOW_WRITES_ENV]).toBe(
|
|
214
|
+
`\${${RESOURCE_AI_ALLOW_WRITES_ENV}:-false}`,
|
|
215
|
+
);
|
|
216
|
+
expect(environment[RESOURCE_AI_WRITE_TARGETS_ENV]).toBe(
|
|
217
|
+
`\${${RESOURCE_AI_WRITE_TARGETS_ENV}:-}`,
|
|
218
|
+
);
|
|
219
|
+
// What the operator protects in .env reaches the agent.
|
|
220
|
+
expect(environment[RESOURCE_AI_PROTECTED_TARGETS_ENV]).toBe(
|
|
221
|
+
"${ONEUPTIME_AI_PROTECTED_TARGETS:-}",
|
|
222
|
+
);
|
|
223
|
+
// One key the collector already has.
|
|
224
|
+
expect(
|
|
225
|
+
["ONEUPTIME_SERVICE_TOKEN", "ONEUPTIME_TELEMETRY_INGESTION_KEY"].filter(
|
|
226
|
+
(key: string): boolean => {
|
|
227
|
+
return key in environment;
|
|
228
|
+
},
|
|
229
|
+
),
|
|
230
|
+
).toHaveLength(1);
|
|
231
|
+
// Never a credential value typed into the page.
|
|
232
|
+
for (const [name, value] of Object.entries(environment)) {
|
|
233
|
+
if (SECRET_NAME_REGEX.test(name)) {
|
|
234
|
+
expect(value).toMatch(/^\$\{[A-Z_]+(:-)?\}$/);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
test.each(
|
|
241
|
+
ALL_AI_RESOURCE_TYPES.filter((type: AiResourceType): boolean => {
|
|
242
|
+
return type !== AiResourceType.DatabaseServer;
|
|
243
|
+
}),
|
|
244
|
+
)(
|
|
245
|
+
"%s: without the identity, falls back to the collector's own default",
|
|
246
|
+
(type: AiResourceType) => {
|
|
247
|
+
const info: AiResourceTypeInfo = AI_RESOURCE_TYPE_INFO[type];
|
|
248
|
+
const variable: string = info.identityEnvVars[0]!;
|
|
249
|
+
|
|
250
|
+
expect(environmentOf(snippetService(type))[variable]).toBe(
|
|
251
|
+
`\${${variable}:-${info.defaultIdentityPlaceholders[0] || ""}}`,
|
|
252
|
+
);
|
|
253
|
+
},
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
test.each(
|
|
257
|
+
ALL_AI_RESOURCE_TYPES.filter((type: AiResourceType): boolean => {
|
|
258
|
+
return type !== AiResourceType.DatabaseServer;
|
|
259
|
+
}),
|
|
260
|
+
)(
|
|
261
|
+
"%s: with the identity known, pins the agent to exactly this resource",
|
|
262
|
+
(type: AiResourceType) => {
|
|
263
|
+
const variable: string = AI_RESOURCE_TYPE_INFO[type].identityEnvVars[0]!;
|
|
264
|
+
|
|
265
|
+
expect(
|
|
266
|
+
environmentOf(snippetService(type, "prod-east-01"))[variable],
|
|
267
|
+
).toBe("prod-east-01");
|
|
268
|
+
},
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
test("a database server is pinned by its id, never its name", () => {
|
|
272
|
+
const environment: Record<string, string> = environmentOf(
|
|
273
|
+
snippetService(AiResourceType.DatabaseServer, "ignored"),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
expect(environment["DATABASE_SERVER_ID"]).toBe(RESOURCE_ID);
|
|
277
|
+
expect(environment["DATABASE_SYSTEM"]).toBe("${DATABASE_SYSTEM}");
|
|
278
|
+
// One agent per database, several per machine: no fixed container name.
|
|
279
|
+
expect(
|
|
280
|
+
snippetService(AiResourceType.DatabaseServer).container_name,
|
|
281
|
+
).toBeUndefined();
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test("a snippet pinned to this resource does not let the .env's resource name move the agent", () => {
|
|
285
|
+
expect(
|
|
286
|
+
environmentOf(
|
|
287
|
+
snippetService(AiResourceType.DockerHost, "prod-docker-01"),
|
|
288
|
+
),
|
|
289
|
+
).not.toHaveProperty(RESOURCE_AI_AGENT_RESOURCE_NAME_ENV);
|
|
290
|
+
expect(
|
|
291
|
+
environmentOf(snippetService(AiResourceType.DatabaseServer)),
|
|
292
|
+
).not.toHaveProperty(RESOURCE_AI_AGENT_RESOURCE_NAME_ENV);
|
|
293
|
+
// Unpinned, it is passed like the collector's own service passes it.
|
|
294
|
+
expect(
|
|
295
|
+
environmentOf(snippetService(AiResourceType.DockerHost))[
|
|
296
|
+
RESOURCE_AI_AGENT_RESOURCE_NAME_ENV
|
|
297
|
+
],
|
|
298
|
+
).toBe("${ONEUPTIME_AI_AGENT_RESOURCE_NAME:-}");
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("an identity with odd characters survives YAML and compose interpolation", () => {
|
|
302
|
+
expect(toComposeEnvironmentEntry("HOST_NAME", "web-01")).toBe(
|
|
303
|
+
"HOST_NAME=web-01",
|
|
304
|
+
);
|
|
305
|
+
expect(toComposeEnvironmentEntry("HOST_NAME", "my host: #1 $HOME")).toBe(
|
|
306
|
+
'"HOST_NAME=my host: #1 $$HOME"',
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
const environment: Record<string, string> = environmentOf(
|
|
310
|
+
snippetService(AiResourceType.Host, "my host: #1 $HOME"),
|
|
311
|
+
);
|
|
312
|
+
// YAML gives compose the $$-escaped text, which compose reads as one $.
|
|
313
|
+
expect(environment["HOST_NAME"]).toBe("my host: #1 $$HOME");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("the socket kinds mount their socket and run as root; the API kinds do not", () => {
|
|
317
|
+
expect(snippetService(AiResourceType.DockerHost).volumes).toEqual([
|
|
318
|
+
"/var/run/docker.sock:/var/run/docker.sock:ro",
|
|
319
|
+
]);
|
|
320
|
+
expect(snippetService(AiResourceType.PodmanHost).volumes).toEqual([
|
|
321
|
+
"/run/podman/podman.sock:/run/podman/podman.sock:ro",
|
|
322
|
+
]);
|
|
323
|
+
expect(snippetService(AiResourceType.DockerSwarmCluster).volumes).toEqual([
|
|
324
|
+
"/var/run/docker.sock:/var/run/docker.sock:ro",
|
|
325
|
+
]);
|
|
326
|
+
for (const type of [
|
|
327
|
+
AiResourceType.DockerHost,
|
|
328
|
+
AiResourceType.PodmanHost,
|
|
329
|
+
AiResourceType.DockerSwarmCluster,
|
|
330
|
+
AiResourceType.Host,
|
|
331
|
+
]) {
|
|
332
|
+
expect(snippetService(type).user).toBe("0:0");
|
|
333
|
+
}
|
|
334
|
+
for (const type of [
|
|
335
|
+
AiResourceType.ProxmoxCluster,
|
|
336
|
+
AiResourceType.VMwareVCenter,
|
|
337
|
+
AiResourceType.CephCluster,
|
|
338
|
+
AiResourceType.DatabaseServer,
|
|
339
|
+
]) {
|
|
340
|
+
expect(snippetService(type).user).toBe("1000:1000");
|
|
341
|
+
expect(snippetService(type).privileged).toBeUndefined();
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test("a host agent runs privileged in the host's pid namespace, as a file of its own", () => {
|
|
346
|
+
const snippet: string = getResourceAiAgentComposeSnippet({
|
|
347
|
+
resourceType: AiResourceType.Host,
|
|
348
|
+
resourceId: RESOURCE_ID,
|
|
349
|
+
});
|
|
350
|
+
const service: ComposeService = snippetService(AiResourceType.Host);
|
|
351
|
+
|
|
352
|
+
expect(service.privileged).toBe(true);
|
|
353
|
+
expect(service.pid).toBe("host");
|
|
354
|
+
expect(snippet).not.toContain("stay as they are");
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
test("a Ceph agent mounts ./ceph read-only for its own conf and keyring", () => {
|
|
358
|
+
expect(snippetService(AiResourceType.CephCluster).volumes).toEqual([
|
|
359
|
+
"./ceph:/etc/ceph:ro",
|
|
360
|
+
]);
|
|
361
|
+
expect(
|
|
362
|
+
environmentOf(snippetService(AiResourceType.CephCluster))[
|
|
363
|
+
"CEPH_CLIENT_ID"
|
|
364
|
+
],
|
|
365
|
+
).toBe("${CEPH_CLIENT_ID:-oneuptime-ai}");
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test("a host agent shares the host's network, like a native collector", () => {
|
|
369
|
+
expect(
|
|
370
|
+
(snippetService(AiResourceType.Host) as Record<string, unknown>)[
|
|
371
|
+
"network_mode"
|
|
372
|
+
],
|
|
373
|
+
).toBe("host");
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("the API kinds read their connection from the collector's .env", () => {
|
|
377
|
+
expect(
|
|
378
|
+
Object.keys(environmentOf(snippetService(AiResourceType.ProxmoxCluster))),
|
|
379
|
+
).toEqual(
|
|
380
|
+
expect.arrayContaining([
|
|
381
|
+
"PVE_HOST",
|
|
382
|
+
"PVE_VERIFY_SSL",
|
|
383
|
+
"PVE_API_TOKEN_ID",
|
|
384
|
+
"PVE_API_TOKEN_SECRET",
|
|
385
|
+
]),
|
|
386
|
+
);
|
|
387
|
+
expect(
|
|
388
|
+
Object.keys(environmentOf(snippetService(AiResourceType.VMwareVCenter))),
|
|
389
|
+
).toEqual(
|
|
390
|
+
expect.arrayContaining([
|
|
391
|
+
"VCENTER_ENDPOINT",
|
|
392
|
+
"VCENTER_USERNAME",
|
|
393
|
+
"VCENTER_PASSWORD",
|
|
394
|
+
]),
|
|
395
|
+
);
|
|
396
|
+
expect(
|
|
397
|
+
Object.keys(environmentOf(snippetService(AiResourceType.DatabaseServer))),
|
|
398
|
+
).toEqual(
|
|
399
|
+
expect.arrayContaining([
|
|
400
|
+
"DATABASE_SYSTEM",
|
|
401
|
+
"DATABASE_ENDPOINT",
|
|
402
|
+
"DATABASE_USERNAME",
|
|
403
|
+
"DATABASE_PASSWORD",
|
|
404
|
+
]),
|
|
405
|
+
);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
test("the agent's own credentials in .env reach it, so fixes do not fall back to the collector's read-only ones", () => {
|
|
409
|
+
expect(
|
|
410
|
+
environmentOf(snippetService(AiResourceType.ProxmoxCluster)),
|
|
411
|
+
).toEqual(
|
|
412
|
+
expect.objectContaining({
|
|
413
|
+
ONEUPTIME_AI_PVE_API_TOKEN_ID: "${ONEUPTIME_AI_PVE_API_TOKEN_ID:-}",
|
|
414
|
+
ONEUPTIME_AI_PVE_API_TOKEN_SECRET:
|
|
415
|
+
"${ONEUPTIME_AI_PVE_API_TOKEN_SECRET:-}",
|
|
416
|
+
PVE_PORT: "${PVE_PORT:-}",
|
|
417
|
+
PVE_CA_FILE: "${PVE_CA_FILE:-}",
|
|
418
|
+
}),
|
|
419
|
+
);
|
|
420
|
+
expect(environmentOf(snippetService(AiResourceType.VMwareVCenter))).toEqual(
|
|
421
|
+
expect.objectContaining({
|
|
422
|
+
ONEUPTIME_AI_VCENTER_USERNAME: "${ONEUPTIME_AI_VCENTER_USERNAME:-}",
|
|
423
|
+
ONEUPTIME_AI_VCENTER_PASSWORD: "${ONEUPTIME_AI_VCENTER_PASSWORD:-}",
|
|
424
|
+
VCENTER_CA_FILE: "${VCENTER_CA_FILE:-}",
|
|
425
|
+
GOVC_DATACENTER: "${GOVC_DATACENTER:-}",
|
|
426
|
+
}),
|
|
427
|
+
);
|
|
428
|
+
expect(
|
|
429
|
+
environmentOf(snippetService(AiResourceType.DatabaseServer)),
|
|
430
|
+
).toEqual(
|
|
431
|
+
expect.objectContaining({
|
|
432
|
+
ONEUPTIME_AI_DATABASE_USERNAME: "${ONEUPTIME_AI_DATABASE_USERNAME:-}",
|
|
433
|
+
ONEUPTIME_AI_DATABASE_PASSWORD: "${ONEUPTIME_AI_DATABASE_PASSWORD:-}",
|
|
434
|
+
ONEUPTIME_AI_DATABASE_NAME: "${ONEUPTIME_AI_DATABASE_NAME:-}",
|
|
435
|
+
}),
|
|
436
|
+
);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
test("a database agent connects the way the collector does: TLS as .env says, never silently without it", () => {
|
|
440
|
+
expect(
|
|
441
|
+
environmentOf(snippetService(AiResourceType.DatabaseServer)),
|
|
442
|
+
).toEqual(
|
|
443
|
+
expect.objectContaining({
|
|
444
|
+
DATABASE_TLS_INSECURE: "${DATABASE_TLS_INSECURE:-true}",
|
|
445
|
+
DATABASE_TLS_INSECURE_SKIP_VERIFY:
|
|
446
|
+
"${DATABASE_TLS_INSECURE_SKIP_VERIFY:-false}",
|
|
447
|
+
DATABASE_ENDPOINT_HOST: "${DATABASE_ENDPOINT_HOST:-}",
|
|
448
|
+
DATABASE_ENDPOINT_PORT: "${DATABASE_ENDPOINT_PORT:-}",
|
|
449
|
+
ONEUPTIME_AI_DATABASE_CA_FILE: "${ONEUPTIME_AI_DATABASE_CA_FILE:-}",
|
|
450
|
+
}),
|
|
451
|
+
);
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
describe("the snippet matches what the collectors ship", () => {
|
|
456
|
+
test("the collectors the page says ship the agent do ship it", () => {
|
|
457
|
+
for (const type of ALL_AI_RESOURCE_TYPES) {
|
|
458
|
+
if (doesCollectorShipResourceAiAgent(type)) {
|
|
459
|
+
expect(collectorAgentService(type)).not.toBeNull();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
test.each(ALL_AI_RESOURCE_TYPES)(
|
|
465
|
+
"%s: the page says the collector ships the agent exactly when its compose file does",
|
|
466
|
+
(type: AiResourceType) => {
|
|
467
|
+
// Otherwise the page asks for a second copy of a service the file has.
|
|
468
|
+
expect(doesCollectorShipResourceAiAgent(type)).toBe(
|
|
469
|
+
collectorAgentService(type) !== null,
|
|
470
|
+
);
|
|
471
|
+
},
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
test.each(ALL_AI_RESOURCE_TYPES)(
|
|
475
|
+
"%s: the snippet passes every variable the shipped service passes, with its value",
|
|
476
|
+
(type: AiResourceType) => {
|
|
477
|
+
const shipped: { name: string; service: ComposeService } | null =
|
|
478
|
+
collectorAgentService(type);
|
|
479
|
+
|
|
480
|
+
if (!shipped) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const info: AiResourceTypeInfo = AI_RESOURCE_TYPE_INFO[type];
|
|
485
|
+
const ours: Record<string, string> = environmentOf(snippetService(type));
|
|
486
|
+
const theirs: Record<string, string> = environmentOf(shipped.service);
|
|
487
|
+
|
|
488
|
+
for (const [name, value] of Object.entries(theirs)) {
|
|
489
|
+
if (
|
|
490
|
+
name === RESOURCE_AI_AGENT_RESOURCE_NAME_ENV &&
|
|
491
|
+
type === AiResourceType.DatabaseServer
|
|
492
|
+
) {
|
|
493
|
+
// Pinned by its id: the .env must not move it (see below).
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
/*
|
|
497
|
+
* Compose passes a container only what its service lists: a
|
|
498
|
+
* variable missing here is a .env setting the agent never sees.
|
|
499
|
+
*/
|
|
500
|
+
expect(ours).toHaveProperty(name);
|
|
501
|
+
if (name === "DATABASE_SERVER_ID") {
|
|
502
|
+
// The page pins its own id.
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
if (name === info.identityEnvVars[0]) {
|
|
506
|
+
// Without the identity known, the collector's own default.
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
expect(`${name}=${ours[name]}`).toBe(`${name}=${value}`);
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
);
|
|
513
|
+
|
|
514
|
+
test.each(ALL_AI_RESOURCE_TYPES)(
|
|
515
|
+
"%s: a shipped agent service has the snippet's name, type, identity and variables",
|
|
516
|
+
(type: AiResourceType) => {
|
|
517
|
+
const shipped: { name: string; service: ComposeService } | null =
|
|
518
|
+
collectorAgentService(type);
|
|
519
|
+
|
|
520
|
+
if (!shipped) {
|
|
521
|
+
// Nothing shipped yet: the page's own snippet is the install.
|
|
522
|
+
expect(doesCollectorShipResourceAiAgent(type)).toBe(false);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const ours: ComposeService = snippetService(type);
|
|
527
|
+
const theirs: Record<string, string> = environmentOf(shipped.service);
|
|
528
|
+
|
|
529
|
+
expect(shipped.name).toBe(getResourceAiAgentServiceName(type));
|
|
530
|
+
expect(theirs[RESOURCE_AI_AGENT_RESOURCE_TYPE_ENV]).toBe(
|
|
531
|
+
AI_RESOURCE_TYPE_INFO[type].agentAlias,
|
|
532
|
+
);
|
|
533
|
+
expect(shipped.service.user).toBe(ours.user);
|
|
534
|
+
|
|
535
|
+
for (const [name, value] of Object.entries(environmentOf(ours))) {
|
|
536
|
+
if (name === "DATABASE_SERVER_ID") {
|
|
537
|
+
// The page pins its own id; a collector has none to pin.
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
expect(theirs).toHaveProperty(name);
|
|
541
|
+
if (
|
|
542
|
+
INTERPOLATED_REGEX.test(value) &&
|
|
543
|
+
name !== RESOURCE_AI_WRITE_TARGETS_ENV
|
|
544
|
+
) {
|
|
545
|
+
expect(theirs[name]).toBe(value);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
for (const volume of ours.volumes || []) {
|
|
550
|
+
expect(shipped.service.volumes || []).toContain(volume);
|
|
551
|
+
}
|
|
552
|
+
},
|
|
553
|
+
);
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
describe("the install instructions", () => {
|
|
557
|
+
test.each(ALL_AI_RESOURCE_TYPES)(
|
|
558
|
+
"%s: where, what, how to start it, and every variable it reads",
|
|
559
|
+
(type: AiResourceType) => {
|
|
560
|
+
const info: AiResourceTypeInfo = AI_RESOURCE_TYPE_INFO[type];
|
|
561
|
+
const install: ResourceAiAgentInstall = getResourceAiAgentInstall({
|
|
562
|
+
resourceType: type,
|
|
563
|
+
resourceId: RESOURCE_ID,
|
|
564
|
+
});
|
|
565
|
+
const names: Array<string> = install.variables.map(
|
|
566
|
+
(variable: ResourceAiAgentInstallVariable): string => {
|
|
567
|
+
return variable.name;
|
|
568
|
+
},
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
const directory: string | null = getResourceAiAgentDirectory(type);
|
|
572
|
+
|
|
573
|
+
expect(install.whereText).toContain(
|
|
574
|
+
directory || "your docker-compose.yml",
|
|
575
|
+
);
|
|
576
|
+
expect(install.composeSnippet).toBe(
|
|
577
|
+
getResourceAiAgentComposeSnippet({
|
|
578
|
+
resourceType: type,
|
|
579
|
+
resourceId: RESOURCE_ID,
|
|
580
|
+
}),
|
|
581
|
+
);
|
|
582
|
+
expect(install.startCommand).toBe(
|
|
583
|
+
`${directory ? `cd ${directory}` : COMPOSE_DIRECTORY_COMMENT}\n${
|
|
584
|
+
type === AiResourceType.PodmanHost ? "podman" : "docker"
|
|
585
|
+
} compose up -d ${getResourceAiAgentServiceName(type)}`,
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
expect(names[0]).toBe(RESOURCE_AI_AGENT_RESOURCE_TYPE_ENV);
|
|
589
|
+
expect(install.variables[0]!.value).toBe(info.agentAlias);
|
|
590
|
+
expect(names).toContain("ONEUPTIME_URL");
|
|
591
|
+
expect(names).toContain(RESOURCE_AI_ALLOW_WRITES_ENV);
|
|
592
|
+
expect(names).toContain(RESOURCE_AI_WRITE_TARGETS_ENV);
|
|
593
|
+
expect(names).toContain(RESOURCE_AI_PROTECTED_TARGETS_ENV);
|
|
594
|
+
expect(new Set(names).size).toBe(names.length);
|
|
595
|
+
// The identity variable the agent registers with.
|
|
596
|
+
expect(names).toContain(
|
|
597
|
+
type === AiResourceType.DatabaseServer
|
|
598
|
+
? "DATABASE_SERVER_ID"
|
|
599
|
+
: info.identityEnvVars[0],
|
|
600
|
+
);
|
|
601
|
+
// Every variable of the snippet is explained.
|
|
602
|
+
for (const name of Object.keys(environmentOf(snippetService(type)))) {
|
|
603
|
+
expect(names).toContain(name);
|
|
604
|
+
}
|
|
605
|
+
for (const variable of install.variables) {
|
|
606
|
+
expect(variable.description.trim().length).toBeGreaterThan(0);
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
test("says the collector's compose already ships it where it does", () => {
|
|
612
|
+
// Usually installed with install.sh: that comes first.
|
|
613
|
+
expect(
|
|
614
|
+
getResourceAiAgentInstall({
|
|
615
|
+
resourceType: AiResourceType.DockerHost,
|
|
616
|
+
resourceId: RESOURCE_ID,
|
|
617
|
+
}).whereText,
|
|
618
|
+
).toBe(
|
|
619
|
+
"Installed the Docker agent with install.sh? Run it again: it now starts this agent too, as the container oneuptime-docker-ai-agent. With Compose, the Docker agent's docker-compose.yml ships this service; for an older Compose install, add it to your docker-compose.yml — it takes every variable below from the same .env.",
|
|
620
|
+
);
|
|
621
|
+
expect(
|
|
622
|
+
getResourceAiAgentInstall({
|
|
623
|
+
resourceType: AiResourceType.ProxmoxCluster,
|
|
624
|
+
resourceId: RESOURCE_ID,
|
|
625
|
+
}).whereText,
|
|
626
|
+
).toBe(
|
|
627
|
+
"The Proxmox agent's docker-compose.yml ships this service; new installs run it already. For an older install, add it to the docker-compose.yml in /opt/oneuptime-proxmox-agent — it takes every variable below from the same .env.",
|
|
628
|
+
);
|
|
629
|
+
// Its compose file ships the service: never "add" a second copy.
|
|
630
|
+
expect(
|
|
631
|
+
getResourceAiAgentInstall({
|
|
632
|
+
resourceType: AiResourceType.DatabaseServer,
|
|
633
|
+
resourceId: RESOURCE_ID,
|
|
634
|
+
}).whereText,
|
|
635
|
+
).toBe(
|
|
636
|
+
"The database agent's docker-compose.yml ships this service; new installs run it already. For an older install, add it to the docker-compose.yml in /opt/oneuptime-database-agent — it takes every variable below from the same .env.",
|
|
637
|
+
);
|
|
638
|
+
expect(
|
|
639
|
+
getResourceAiAgentInstall({
|
|
640
|
+
resourceType: AiResourceType.Host,
|
|
641
|
+
resourceId: RESOURCE_ID,
|
|
642
|
+
}).whereText,
|
|
643
|
+
).toBe(
|
|
644
|
+
"Save this as docker-compose.yml in /opt/oneuptime-host-ai-agent on the host, next to a .env file.",
|
|
645
|
+
);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
test("the identity row shows the name this resource reports when it is known", () => {
|
|
649
|
+
const known: ResourceAiAgentInstallVariable = getResourceAiAgentInstall({
|
|
650
|
+
resourceType: AiResourceType.DockerHost,
|
|
651
|
+
resourceId: RESOURCE_ID,
|
|
652
|
+
identity: "prod-docker-01",
|
|
653
|
+
}).variables[1]!;
|
|
654
|
+
|
|
655
|
+
expect(known).toEqual({
|
|
656
|
+
name: "DOCKER_HOST_NAME",
|
|
657
|
+
value: "prod-docker-01",
|
|
658
|
+
description:
|
|
659
|
+
"The name this Docker host reports. The agent must register with exactly this name, or it serves a different Docker host.",
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
const unknown: ResourceAiAgentInstallVariable = getResourceAiAgentInstall({
|
|
663
|
+
resourceType: AiResourceType.Host,
|
|
664
|
+
resourceId: RESOURCE_ID,
|
|
665
|
+
}).variables[1]!;
|
|
666
|
+
|
|
667
|
+
expect(unknown.name).toBe("HOST_NAME");
|
|
668
|
+
expect(unknown.value).toBe("from .env");
|
|
669
|
+
|
|
670
|
+
expect(
|
|
671
|
+
getResourceAiAgentInstall({
|
|
672
|
+
resourceType: AiResourceType.DatabaseServer,
|
|
673
|
+
resourceId: RESOURCE_ID,
|
|
674
|
+
}).variables[1],
|
|
675
|
+
).toEqual(
|
|
676
|
+
expect.objectContaining({
|
|
677
|
+
name: "DATABASE_SERVER_ID",
|
|
678
|
+
value: RESOURCE_ID,
|
|
679
|
+
}),
|
|
680
|
+
);
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
test("the protected targets row says what the agent protects on its own", () => {
|
|
684
|
+
const proxmox: ResourceAiAgentInstallVariable | undefined =
|
|
685
|
+
getResourceAiAgentInstall({
|
|
686
|
+
resourceType: AiResourceType.ProxmoxCluster,
|
|
687
|
+
resourceId: RESOURCE_ID,
|
|
688
|
+
}).variables.find((variable: ResourceAiAgentInstallVariable) => {
|
|
689
|
+
return variable.name === RESOURCE_AI_PROTECTED_TARGETS_ENV;
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
expect(proxmox?.value).toBe("(empty)");
|
|
693
|
+
expect(proxmox?.description).toContain("VMID of the guest it runs in");
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
test("a literal the service sets is shown as it is, not as from .env", () => {
|
|
697
|
+
expect(
|
|
698
|
+
getResourceAiAgentInstall({
|
|
699
|
+
resourceType: AiResourceType.Host,
|
|
700
|
+
resourceId: RESOURCE_ID,
|
|
701
|
+
}).variables.find((variable: ResourceAiAgentInstallVariable) => {
|
|
702
|
+
return variable.name === "TINI_SUBREAPER";
|
|
703
|
+
})?.value,
|
|
704
|
+
).toBe("1");
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
test.each(ALL_AI_RESOURCE_TYPES)(
|
|
708
|
+
"%s: every command and sentence names only a directory the collector's installer creates",
|
|
709
|
+
(type: AiResourceType) => {
|
|
710
|
+
const expected: string | null = installerDirectory(type);
|
|
711
|
+
const install: ResourceAiAgentInstall = getResourceAiAgentInstall({
|
|
712
|
+
resourceType: type,
|
|
713
|
+
resourceId: RESOURCE_ID,
|
|
714
|
+
});
|
|
715
|
+
const write: ResourceAiAgentWriteAccessCommands =
|
|
716
|
+
getResourceAiAgentWriteAccessCommands(type);
|
|
717
|
+
const texts: Array<string> = [
|
|
718
|
+
install.whereText,
|
|
719
|
+
install.startCommand,
|
|
720
|
+
...install.prerequisites,
|
|
721
|
+
getResourceAiAgentLogsCommand(type),
|
|
722
|
+
getResourceAiAgentStatusCommand(type),
|
|
723
|
+
write.envIntro,
|
|
724
|
+
write.restartCommand,
|
|
725
|
+
write.installerNote || "",
|
|
726
|
+
];
|
|
727
|
+
|
|
728
|
+
expect(getResourceAiAgentDirectory(type)).toBe(expected);
|
|
729
|
+
|
|
730
|
+
for (const text of texts) {
|
|
731
|
+
for (const match of text.matchAll(/(?:^|\s)cd (\S+)/g)) {
|
|
732
|
+
expect(match[1]).toBe(expected);
|
|
733
|
+
}
|
|
734
|
+
for (const match of text.matchAll(/\/opt\/[\w.-]+/g)) {
|
|
735
|
+
expect(match[0]).toBe(expected);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
);
|
|
740
|
+
|
|
741
|
+
test.each(ALL_AI_RESOURCE_TYPES)(
|
|
742
|
+
"%s: the prerequisites are there, for the types that have any",
|
|
743
|
+
(type: AiResourceType) => {
|
|
744
|
+
const install: ResourceAiAgentInstall = getResourceAiAgentInstall({
|
|
745
|
+
resourceType: type,
|
|
746
|
+
resourceId: RESOURCE_ID,
|
|
747
|
+
});
|
|
748
|
+
expect(install.prerequisites.length).toBeGreaterThan(0);
|
|
749
|
+
for (const prerequisite of install.prerequisites) {
|
|
750
|
+
expect(prerequisite.trim().length).toBeGreaterThan(0);
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
);
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
describe("the logs and status commands", () => {
|
|
757
|
+
test("name the agent's container, in the collector's runtime", () => {
|
|
758
|
+
expect(getResourceAiAgentLogsCommand(AiResourceType.DockerHost)).toBe(
|
|
759
|
+
"docker logs --tail 100 oneuptime-docker-ai-agent",
|
|
760
|
+
);
|
|
761
|
+
expect(getResourceAiAgentLogsCommand(AiResourceType.PodmanHost)).toBe(
|
|
762
|
+
"podman logs --tail 100 oneuptime-podman-ai-agent",
|
|
763
|
+
);
|
|
764
|
+
expect(getResourceAiAgentLogsCommand(AiResourceType.ProxmoxCluster)).toBe(
|
|
765
|
+
"docker logs --tail 100 oneuptime-proxmox-ai-agent",
|
|
766
|
+
);
|
|
767
|
+
// No container name: the compose service, from its directory.
|
|
768
|
+
expect(getResourceAiAgentLogsCommand(AiResourceType.DatabaseServer)).toBe(
|
|
769
|
+
"cd /opt/oneuptime-database-agent\ndocker compose logs --tail=100 oneuptime-database-ai-agent",
|
|
770
|
+
);
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
test("the status command reads the agent's own health server", () => {
|
|
774
|
+
expect(getResourceAiAgentStatusCommand(AiResourceType.CephCluster)).toBe(
|
|
775
|
+
`docker exec oneuptime-ceph-ai-agent wget -qO- http://127.0.0.1:${RESOURCE_AI_AGENT_DEFAULT_HEALTH_PORT}/status`,
|
|
776
|
+
);
|
|
777
|
+
expect(getResourceAiAgentStatusCommand(AiResourceType.DatabaseServer)).toBe(
|
|
778
|
+
`cd /opt/oneuptime-database-agent\ndocker compose exec oneuptime-database-ai-agent wget -qO- http://127.0.0.1:${RESOURCE_AI_AGENT_DEFAULT_HEALTH_PORT}/status`,
|
|
779
|
+
);
|
|
780
|
+
});
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
describe("the write switch", () => {
|
|
784
|
+
test.each(ALL_AI_RESOURCE_TYPES)(
|
|
785
|
+
"%s: the exact variable, recommended with targets where the type names any",
|
|
786
|
+
(type: AiResourceType) => {
|
|
787
|
+
const commands: ResourceAiAgentWriteAccessCommands =
|
|
788
|
+
getResourceAiAgentWriteAccessCommands(type);
|
|
789
|
+
|
|
790
|
+
expect(commands.allTargetsEnv).toBe(
|
|
791
|
+
`${RESOURCE_AI_ALLOW_WRITES_ENV}=true`,
|
|
792
|
+
);
|
|
793
|
+
expect(commands.restartCommand).toContain(
|
|
794
|
+
`compose up -d ${getResourceAiAgentServiceName(type)}`,
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
if (type === AiResourceType.DatabaseServer) {
|
|
798
|
+
// A session id is not a target anyone can scope in advance.
|
|
799
|
+
expect(commands.scopedEnv).toBeNull();
|
|
800
|
+
expect(commands.scopedNote).toBeNull();
|
|
801
|
+
} else {
|
|
802
|
+
expect(commands.scopedEnv).toMatch(
|
|
803
|
+
new RegExp(
|
|
804
|
+
`^${RESOURCE_AI_ALLOW_WRITES_ENV}=true\\n${RESOURCE_AI_WRITE_TARGETS_ENV}=\\S+$`,
|
|
805
|
+
),
|
|
806
|
+
);
|
|
807
|
+
expect(commands.scopedNote).toContain("refused by the agent itself");
|
|
808
|
+
}
|
|
809
|
+
},
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
test("says how to do it with install.sh where that is how the collector is installed", () => {
|
|
813
|
+
const docker: ResourceAiAgentWriteAccessCommands =
|
|
814
|
+
getResourceAiAgentWriteAccessCommands(AiResourceType.DockerHost);
|
|
815
|
+
|
|
816
|
+
expect(docker.installerNote).toBe(
|
|
817
|
+
"Installed the Docker agent with install.sh? Run it again with ONEUPTIME_AI_ALLOW_WRITES=true (and ONEUPTIME_AI_WRITE_TARGETS) set in its environment: it starts the agent again with them. Started the agent with docker run? Remove it (docker rm -f oneuptime-docker-ai-agent) and start it again with -e ONEUPTIME_AI_ALLOW_WRITES=true.",
|
|
818
|
+
);
|
|
819
|
+
// Compose second, from wherever its docker-compose.yml is.
|
|
820
|
+
expect(docker.envIntro).toBe(
|
|
821
|
+
"With Compose instead? Recommended: allow only the targets AI may fix. Set these in the .env next to your docker-compose.yml:",
|
|
822
|
+
);
|
|
823
|
+
expect(docker.restartCommand).toBe(
|
|
824
|
+
`${COMPOSE_DIRECTORY_COMMENT}\ndocker compose up -d oneuptime-docker-ai-agent`,
|
|
825
|
+
);
|
|
826
|
+
|
|
827
|
+
const podman: ResourceAiAgentWriteAccessCommands =
|
|
828
|
+
getResourceAiAgentWriteAccessCommands(AiResourceType.PodmanHost);
|
|
829
|
+
|
|
830
|
+
expect(podman.installerNote).toContain("Podman agent with install.sh");
|
|
831
|
+
expect(podman.installerNote).toContain(
|
|
832
|
+
"podman rm -f oneuptime-podman-ai-agent",
|
|
833
|
+
);
|
|
834
|
+
expect(podman.restartCommand).toBe(
|
|
835
|
+
`${COMPOSE_DIRECTORY_COMMENT}\npodman compose up -d oneuptime-podman-ai-agent`,
|
|
836
|
+
);
|
|
837
|
+
|
|
838
|
+
const ceph: ResourceAiAgentWriteAccessCommands =
|
|
839
|
+
getResourceAiAgentWriteAccessCommands(AiResourceType.CephCluster);
|
|
840
|
+
|
|
841
|
+
expect(ceph.installerNote).toBeNull();
|
|
842
|
+
expect(ceph.envIntro).toBe(
|
|
843
|
+
"Recommended: allow only the targets AI may fix. Set these in the .env next to its docker-compose.yml:",
|
|
844
|
+
);
|
|
845
|
+
expect(
|
|
846
|
+
getResourceAiAgentWriteAccessCommands(AiResourceType.DatabaseServer)
|
|
847
|
+
.envIntro,
|
|
848
|
+
).toBe("Set this in the .env next to its docker-compose.yml:");
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
test.each(ALL_AI_RESOURCE_TYPES)(
|
|
852
|
+
"%s: the disclosure says what writes allow and what OneUptime never does",
|
|
853
|
+
(type: AiResourceType) => {
|
|
854
|
+
const disclosure: string = getResourceAiAgentWriteDisclosure(type);
|
|
855
|
+
|
|
856
|
+
expect(disclosure).toMatch(/^Write access lets the agent /);
|
|
857
|
+
expect(disclosure).toContain("OneUptime still holds the line");
|
|
858
|
+
},
|
|
859
|
+
);
|
|
860
|
+
});
|