@oneuptime/common 14.0.3 → 14.0.6
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/Alert.ts +55 -0
- package/Models/DatabaseModels/DatabaseServer.ts +1650 -0
- package/Models/DatabaseModels/DatabaseServerEndpoint.ts +540 -0
- package/Models/DatabaseModels/DatabaseServerFeed.ts +625 -0
- package/Models/DatabaseModels/DatabaseServerLabelRule.ts +514 -0
- package/Models/DatabaseModels/DatabaseServerOwnerRule.ts +596 -0
- package/Models/DatabaseModels/DatabaseServerOwnerTeam.ts +491 -0
- package/Models/DatabaseModels/DatabaseServerOwnerUser.ts +490 -0
- package/Models/DatabaseModels/Incident.ts +55 -0
- package/Models/DatabaseModels/Index.ts +16 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +26 -0
- package/Models/DatabaseModels/ScheduledMaintenance.ts +55 -0
- package/Models/DatabaseModels/TelemetryException.ts +2 -0
- package/Models/DatabaseModels/UserProjectSsoConsent.ts +146 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.ts +673 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/MultipartFormData.ts +1 -1
- package/Server/Middleware/TelemetryIngest.ts +15 -8
- package/Server/Services/AlertReminderRuleService.ts +100 -24
- package/Server/Services/DatabaseServerEndpointService.ts +895 -0
- package/Server/Services/DatabaseServerFeedService.ts +128 -0
- package/Server/Services/DatabaseServerLabelRuleEngineService.ts +340 -0
- package/Server/Services/DatabaseServerLabelRuleService.ts +14 -0
- package/Server/Services/DatabaseServerOwnerRuleEngineService.ts +425 -0
- package/Server/Services/DatabaseServerOwnerRuleService.ts +14 -0
- package/Server/Services/DatabaseServerOwnerTeamService.ts +180 -0
- package/Server/Services/DatabaseServerOwnerUserService.ts +173 -0
- package/Server/Services/DatabaseServerService.ts +4546 -0
- package/Server/Services/DatabaseService.ts +14 -8
- package/Server/Services/DockerResourceService.ts +43 -2
- package/Server/Services/IncidentReminderRuleService.ts +103 -23
- package/Server/Services/Index.ts +16 -0
- package/Server/Services/InventoryItemService.ts +113 -5
- package/Server/Services/IoTDeviceService.ts +145 -37
- package/Server/Services/MetricService.ts +1 -1
- package/Server/Services/OpenTelemetryIngestService.ts +19 -0
- package/Server/Services/PodmanResourceService.ts +43 -2
- package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +107 -23
- package/Server/Services/ServiceService.ts +1 -1
- package/Server/Services/TelemetryUsageBillingService.ts +29 -2
- package/Server/Services/UserProjectSsoConsentService.ts +74 -0
- package/Server/Services/UserService.ts +14 -1
- package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +18 -0
- package/Server/Types/Database/QueryHelper.ts +84 -2
- package/Server/Utils/AI/Toolbox/MetricTools.ts +13 -2
- package/Server/Utils/AI/Toolbox/ResourceTools.ts +81 -2
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
- package/Server/Utils/Database/AffectedResourceRelations.ts +6 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +33 -0
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +192 -9
- package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +2264 -755
- package/Server/Utils/Monitor/MonitorIncident.ts +28 -1
- package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +268 -6
- package/Server/Utils/Monitor/MonitorResource.ts +47 -11
- package/Server/Utils/Monitor/MonitorResourceContext.ts +1 -0
- package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +239 -21
- package/Server/Utils/Monitor/PerSeriesResolutionRootCause.ts +43 -0
- package/Server/Utils/Monitor/SeriesResourceLabels.ts +291 -0
- package/Server/Utils/Monitor/SeriesResourceLinker.ts +111 -12
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
- package/Server/Utils/StartServer.ts +26 -6
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
- package/Server/Utils/Telemetry/DatabaseEndpointDiscovery.ts +688 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +5 -1
- package/Server/Utils/Telemetry/LogExceptionExtractor.ts +18 -1
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +175 -12
- package/Server/Utils/Telemetry/ResourceFacetResolver.ts +12 -0
- package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +606 -36
- package/Server/Utils/Telemetry/Telemetry.ts +16 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +22 -22
- package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +16 -10
- package/Tests/App/Accounts/SignedInRedirect.test.tsx +241 -0
- package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +221 -4
- package/Tests/App/Dashboard/AIInsightsResourcePageContext.test.ts +42 -0
- package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +100 -0
- package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +86 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +2 -0
- package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +19 -0
- package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +14 -0
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +26 -1
- package/Tests/App/Dashboard/DashboardAppSsoError.test.tsx +501 -0
- package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +64 -1
- package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +178 -18
- package/Tests/App/Dashboard/DashboardMasterPageSsoRedirect.test.tsx +444 -0
- package/Tests/App/Dashboard/DashboardNavBarForeignHiddenRule.test.tsx +573 -0
- package/Tests/App/Dashboard/DatabaseChartAxes.test.tsx +431 -0
- package/Tests/App/Dashboard/DatabaseContainerDetailBadge.test.tsx +249 -0
- package/Tests/App/Dashboard/DatabaseDashboardE2EFixes.test.ts +681 -0
- package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +1801 -0
- package/Tests/App/Dashboard/DatabaseFleetSummaryRefresh.test.tsx +280 -0
- package/Tests/App/Dashboard/DatabaseFormHelpers.test.tsx +321 -0
- package/Tests/App/Dashboard/DatabaseMetricMonitorLink.test.tsx +795 -0
- package/Tests/App/Dashboard/DatabaseOverviewHeader.test.tsx +580 -0
- package/Tests/App/Dashboard/DatabaseRecommendationsEngineMetrics.test.ts +270 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +1486 -0
- package/Tests/App/Dashboard/DatabaseServerEngineMetricsFilter.test.ts +223 -0
- package/Tests/App/Dashboard/DatabaseServerPresentation.test.ts +435 -0
- package/Tests/App/Dashboard/DatabaseServerSummary.test.ts +186 -0
- package/Tests/App/Dashboard/DatabaseServerTelemetryQueries.test.ts +1553 -0
- package/Tests/App/Dashboard/DatabaseTableTruncation.test.tsx +326 -0
- package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +512 -0
- package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +774 -0
- package/Tests/App/Dashboard/DatabaseViewLayout.test.tsx +233 -0
- package/Tests/App/Dashboard/DatabaseWorkloadBadge.test.tsx +972 -0
- package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +5 -0
- package/Tests/App/Dashboard/DiscoveryReviewUnnamedHostHint.test.tsx +1061 -0
- package/Tests/App/Dashboard/EventOverlayScope.test.ts +147 -0
- package/Tests/App/Dashboard/EventOverlayScopeHook.test.tsx +1 -0
- package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +1 -0
- package/Tests/App/Dashboard/MetricsViewerDrillDown.test.tsx +310 -0
- package/Tests/App/Dashboard/MetricsViewerEntityScopedRows.test.tsx +495 -0
- package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +132 -1
- package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +5 -0
- package/Tests/App/Dashboard/NetworkTopologyExplorerSearch.test.tsx +1267 -0
- package/Tests/App/Dashboard/ProductOrientation.test.tsx +4 -3
- package/Tests/App/Dashboard/RecommendationsLoadedContext.test.tsx +349 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +62 -0
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +14 -0
- package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +4 -1
- package/Tests/App/Dashboard/RumOverviewPage.test.tsx +127 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +2 -0
- package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +1085 -0
- package/Tests/App/Dashboard/SiteSearchBox.test.tsx +2110 -0
- package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +168 -8
- package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +10 -0
- package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +2 -1
- package/Tests/App/StatusPage/StatusPageHeaderForeignHiddenRule.test.tsx +284 -0
- package/Tests/ClassStrings.ts +178 -0
- package/Tests/Fixtures/MobilePasskeyUser.json +1 -2
- package/Tests/ForeignHiddenRuleGuard.ts +2152 -0
- package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +2250 -0
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +24 -5
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
- package/Tests/RenderedMarkup.ts +518 -0
- package/Tests/ResponsiveVisibility.test.ts +490 -4
- package/Tests/ResponsiveVisibility.ts +505 -52
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +541 -0
- package/Tests/Server/Infrastructure/Postgres/AddUserProjectSsoConsentMigration.test.ts +234 -0
- package/Tests/Server/Services/ContainerSnapshotUpsertPostgres.test.ts +319 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +13 -0
- package/Tests/Server/Services/DatabaseServerEndpointService.test.ts +2033 -0
- package/Tests/Server/Services/DatabaseServerFeedAndOwnerServices.test.ts +529 -0
- package/Tests/Server/Services/DatabaseServerLifecycle.test.ts +3302 -0
- package/Tests/Server/Services/DatabaseServerRegistries.test.ts +603 -0
- package/Tests/Server/Services/DatabaseServerRuleEngines.test.ts +754 -0
- package/Tests/Server/Services/DatabaseServerService.test.ts +3558 -0
- package/Tests/Server/Services/DatabaseServerSqlPostgres.test.ts +2045 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedPostgres.test.ts +362 -0
- package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedReply.test.ts +210 -0
- package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +18 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +2 -0
- package/Tests/Server/Services/IncomingCallPolicyEscalationRuleTarget.test.ts +225 -0
- package/Tests/Server/Services/InventoryItemDisplayName.test.ts +280 -2
- package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +8 -2
- package/Tests/Server/Services/IoTDeviceLatestMetricsPostgres.test.ts +689 -0
- package/Tests/Server/Services/IoTDeviceService.test.ts +379 -0
- package/Tests/Server/Services/LatestMetricsValuesShape.test.ts +360 -0
- package/Tests/Server/Services/LlmModelPriceService.test.ts +402 -0
- package/Tests/Server/Services/MetricServiceAggregate.test.ts +12 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
- package/Tests/Server/Services/OwnerRowUniqueness.test.ts +61 -9
- package/Tests/Server/Services/ReminderRuleLookupPostgres.test.ts +2250 -0
- package/Tests/Server/Services/ReminderRuleRefreshOpenSubjectsPaging.test.ts +653 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +68 -20
- package/Tests/Server/Services/SavedViewDefaults.test.ts +325 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
- package/Tests/Server/Services/StatusPageCertificateService.test.ts +169 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +191 -0
- package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +9 -0
- package/Tests/Server/Services/UserProjectSsoConsentPostgres.test.ts +153 -0
- package/Tests/Server/Services/UserProjectSsoConsentService.test.ts +192 -0
- package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +71 -0
- package/Tests/Server/Services/UserSignupIdTakeoverPostgres.test.ts +238 -0
- package/Tests/Server/Services/WorkspaceNotificationSummaryHelpers.test.ts +592 -0
- package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +3 -0
- package/Tests/Server/Types/Database/EventOverlayScopePostgres.test.ts +1 -0
- package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +11 -0
- package/Tests/Server/Types/Database/Permissions/InfrastructureOwnershipScope.test.ts +8 -0
- package/Tests/Server/Types/Database/QueryHelperBooleanForCriteriaBackedRule.test.ts +1094 -0
- package/Tests/Server/Types/Database/QueryHelperSearchAllWords.test.ts +651 -0
- package/Tests/Server/Utils/AI/AlertWriteTools.test.ts +256 -0
- package/Tests/Server/Utils/AI/IncidentWriteTools.test.ts +482 -0
- package/Tests/Server/Utils/AI/ResourceTools.test.ts +134 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +54 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +41 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +279 -9
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaDisplayContext.test.ts +933 -0
- package/Tests/Server/Utils/Monitor/DatabaseMaintenanceSuppression.test.ts +582 -0
- package/Tests/Server/Utils/Monitor/DatabaseServerMonitorLinking.test.ts +753 -0
- package/Tests/Server/Utils/Monitor/MetricDecimalThreshold.test.ts +417 -0
- package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +119 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +60 -25
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +1815 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesAffectedResources.test.ts +1447 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesRatioEmail.test.ts +352 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformMetricUnits.test.ts +717 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformRawScan.test.ts +2125 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformSeriesAffectedResources.test.ts +1288 -0
- package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +88 -0
- package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +205 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +62 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceDatabaseMaintenance.test.ts +803 -0
- package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +133 -3
- package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/PerSeriesRecoveryResolution.test.ts +472 -1
- package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
- package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +236 -2
- package/Tests/Server/Utils/SeriesResourceLabels.test.ts +70 -0
- package/Tests/Server/Utils/StartServerProtobufContentType.test.ts +54 -0
- package/Tests/Server/Utils/Telemetry/DatabaseEndpointDiscovery.test.ts +1895 -0
- package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +7 -0
- package/Tests/Server/Utils/Telemetry/LogExceptionExtractorSeverityGate.test.ts +90 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +570 -0
- package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +97 -2
- package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +1337 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +295 -0
- package/Tests/Types/AI/AIResourceContext.test.ts +30 -0
- package/Tests/Types/BaseDatabase/AggregationType.test.ts +5 -1
- package/Tests/Types/Dashboard/KubernetesDashboardTemplate.test.ts +184 -0
- package/Tests/Types/Dashboard/KubernetesDashboardTemplates.test.ts +107 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerClassifier.test.ts +3644 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerCommand.test.ts +340 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerCommandCorpus.ts +826 -0
- package/Tests/Types/DatabaseServer/DatabaseContainerCommandPostgres.test.ts +150 -0
- package/Tests/Types/DatabaseServer/DatabaseEndpoint.test.ts +2657 -0
- package/Tests/Types/DatabaseServer/DatabaseServerDiscoverySource.test.ts +64 -0
- package/Tests/Types/DatabaseServer/DatabaseServerEntityKeys.test.ts +952 -0
- package/Tests/Types/DatabaseServer/DatabaseServerMetricCatalog.test.ts +1161 -0
- package/Tests/Types/DatabaseServer/DatabaseSystem.test.ts +1514 -0
- package/Tests/Types/DatabaseServer/DatabaseTelemetryResolver.test.ts +1558 -0
- package/Tests/Types/Monitor/CephAlertTemplates.test.ts +255 -6
- package/Tests/Types/Monitor/DatabaseAlertTemplates.test.ts +2254 -0
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +292 -0
- package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +272 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +253 -9
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +13 -5
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +4 -2
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +111 -2
- package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +61 -0
- package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +114 -0
- package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +202 -0
- package/Tests/Types/NetworkDevice/DiscoveredHostNamingStatus.test.ts +401 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +4 -1
- package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +24 -2
- package/Tests/UI/Components/Card.test.tsx +93 -2
- package/Tests/UI/Components/FeedOptions.test.ts +6 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +8 -1
- package/Tests/UI/Components/ForeignHiddenRuleResilience.test.tsx +1147 -0
- package/Tests/UI/Components/HeaderRightRail.test.tsx +41 -1
- package/Tests/UI/Components/LogsEntityNames.test.ts +16 -1
- package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +7 -3
- package/Tests/UI/Components/ResourceFacetConfigs.test.ts +2 -1
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/SrOnlyForeignRule.test.tsx +994 -0
- package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +34 -1
- package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +4 -1
- package/Tests/UI/ForeignHiddenRuleGuard.test.ts +1951 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +76 -0
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +19 -1
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +53 -0
- package/Tests/UI/Rum/ReplaySignals.test.ts +54 -0
- package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +19 -1
- package/Tests/UI/TailwindPlayCdnForeignHiddenRule.test.ts +1005 -0
- package/Tests/UI/ThemeGroupHoverReveal.test.ts +847 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +70 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +21 -0
- package/Tests/UI/Utils/ModelAPISsoRedirect.test.ts +127 -0
- package/Tests/UI/Utils/Navigation.test.ts +102 -0
- package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +23 -0
- package/Tests/Utils/Dashboard/KubernetesClusterCpuTileRepair.test.ts +370 -0
- package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
- package/Tests/Utils/MetricUnitUtil.test.ts +227 -2
- package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +288 -2
- package/Tests/Utils/Monitor/PlatformResourceIdentityAndUnits.test.ts +1559 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.test.ts +2546 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +315 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +336 -1
- package/Tests/Utils/NetworkDiscovery/NetbiosNameUtil.test.ts +244 -0
- package/Tests/Utils/Rules/MonitorTypeRuleCriteria.test.ts +181 -0
- package/Tests/Utils/ValueFormatter.test.ts +50 -0
- package/Tests/tsconfig.json +16 -0
- package/Types/AI/AIResourceContext.ts +7 -0
- package/Types/BaseDatabase/AggregationType.ts +7 -0
- package/Types/Dashboard/DashboardTemplates.ts +22 -8
- package/Types/DatabaseServer/DatabaseConnectionSpan.ts +77 -0
- package/Types/DatabaseServer/DatabaseContainerClassifier.ts +2068 -0
- package/Types/DatabaseServer/DatabaseContainerCommand.ts +801 -0
- package/Types/DatabaseServer/DatabaseEndpoint.ts +1943 -0
- package/Types/DatabaseServer/DatabaseServerDiscoverySource.ts +68 -0
- package/Types/DatabaseServer/DatabaseServerMetricCatalog.ts +966 -0
- package/Types/DatabaseServer/DatabaseSystem.ts +2124 -0
- package/Types/DatabaseServer/DatabaseTelemetryResolver.ts +555 -0
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/Types/Monitor/CephAlertTemplates.ts +18 -0
- package/Types/Monitor/DatabaseAlertTemplates.ts +2020 -0
- package/Types/Monitor/KubernetesAlertTemplates.ts +64 -24
- package/Types/Monitor/KubernetesMetricCatalog.ts +118 -19
- package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +14 -0
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +93 -10
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +109 -8
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +36 -7
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +43 -2
- package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +41 -6
- package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +26 -0
- package/Types/NetworkDevice/DiscoveredHostNamingStatus.ts +147 -0
- package/Types/Permission.ts +267 -0
- package/Types/Probe/ProbeApiIngestResponse.ts +8 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +10 -0
- package/Types/Rules/RuleRun.ts +12 -0
- package/Types/Rum/SessionReplayCustomEvents.ts +29 -3
- package/Types/Rum/WebVitals.ts +13 -0
- package/Types/Telemetry/EntityType.ts +23 -0
- package/Types/Telemetry/ResourceFacetCatalog.ts +14 -0
- package/Types/Telemetry/ServiceType.ts +8 -0
- package/UI/Components/Banner/Banner.tsx +1 -1
- package/UI/Components/Card/Card.tsx +1 -1
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +2 -2
- package/UI/Components/Charts/Line/LineChart.tsx +7 -0
- package/UI/Components/CommandPalette/CommandPalette.tsx +1 -1
- package/UI/Components/Date/StartAndEndDate.tsx +5 -5
- package/UI/Components/Diagram/HorizontalStepChain.tsx +1 -1
- package/UI/Components/Feed/FeedOptionsButton.tsx +1 -1
- package/UI/Components/Forms/BasicForm.tsx +1 -1
- package/UI/Components/Header/Header.tsx +1 -1
- package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -1
- package/UI/Components/HeaderAlert/HeaderAlert.tsx +1 -1
- package/UI/Components/Input/Input.tsx +11 -0
- package/UI/Components/LogsViewer/components/FacetValueRow.tsx +1 -1
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +1 -1
- package/UI/Components/Modal/ModalFooter.tsx +1 -1
- package/UI/Components/ModelDetail/CardModelDetail.tsx +2 -2
- package/UI/Components/ModelTable/BaseModelTable.tsx +4 -4
- package/UI/Components/Navbar/NavBar.tsx +9 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +2 -2
- package/UI/Components/Page/Page.tsx +1 -1
- package/UI/Components/Pagination/Pagination.tsx +3 -3
- package/UI/Components/ProgressButtons/ProgressButtonItem.tsx +1 -1
- package/UI/Components/SideMenu/SideMenu.tsx +4 -1
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +4 -2
- package/UI/Components/StatusPage/ResourceList.tsx +6 -6
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +1 -1
- package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +1 -1
- package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +1 -1
- package/UI/Components/Workflow/ComponentsModal.tsx +2 -2
- package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
- package/UI/Styles/Theme.css +8 -1
- package/UI/Utils/Navigation.ts +89 -42
- package/UI/Utils/Telemetry/TelemetryEntityNames.ts +7 -0
- package/UI/Utils/TelemetryService.ts +4 -3
- package/Utils/Dashboard/KubernetesClusterCpuTileRepair.ts +147 -0
- package/Utils/LabelRuleImportExport.ts +2 -0
- package/Utils/MetricUnitUtil.ts +103 -11
- package/Utils/Monitor/MetricValueFormatter.ts +66 -13
- package/Utils/Monitor/PlatformMetricUnitUtil.ts +215 -0
- package/Utils/Monitor/PlatformResourceIdentity.ts +514 -0
- package/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.ts +425 -0
- package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +32 -0
- package/Utils/NetworkDiscovery/NetbiosNameUtil.ts +73 -0
- package/Utils/Telemetry/DatabaseServerEntityKeys.ts +275 -0
- package/Utils/Telemetry/EntityKey.ts +176 -0
- package/Utils/ValueFormatter.ts +16 -0
- package/build/dist/Models/DatabaseModels/Alert.js +54 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseServer.js +1683 -0
- package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js +559 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js +648 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js +522 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js +603 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js +506 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js.map +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js +505 -0
- package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Incident.js +54 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +16 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryException.js +2 -0
- package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js +162 -0
- package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js +240 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MultipartFormData.js +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +15 -8
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AlertReminderRuleService.js +72 -23
- package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseServerEndpointService.js +694 -0
- package/build/dist/Server/Services/DatabaseServerEndpointService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerFeedService.js +110 -0
- package/build/dist/Server/Services/DatabaseServerFeedService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js +272 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleService.js +13 -0
- package/build/dist/Server/Services/DatabaseServerLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js +340 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js +13 -0
- package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js +160 -0
- package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerOwnerUserService.js +153 -0
- package/build/dist/Server/Services/DatabaseServerOwnerUserService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseServerService.js +3272 -0
- package/build/dist/Server/Services/DatabaseServerService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseService.js +8 -4
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerResourceService.js +30 -3
- package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
- package/build/dist/Server/Services/IncidentReminderRuleService.js +73 -23
- package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +16 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +88 -5
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceService.js +111 -24
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +24 -9
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/PodmanResourceService.js +30 -3
- package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +75 -23
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +1 -1
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +23 -2
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/UserProjectSsoConsentService.js +79 -0
- package/build/dist/Server/Services/UserProjectSsoConsentService.js.map +1 -0
- package/build/dist/Server/Services/UserService.js +13 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +18 -0
- package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +73 -2
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +13 -2
- package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +58 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +1 -1
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +6 -0
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +31 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +135 -14
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -6
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1489 -524
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +196 -6
- package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +36 -9
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +175 -17
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js +29 -0
- package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +211 -0
- package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +82 -10
- package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +20 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js +442 -0
- package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +15 -1
- package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +121 -5
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +12 -0
- package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +438 -36
- package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +12 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +17 -18
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +16 -10
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -1
- package/build/dist/Types/AI/AIResourceContext.js +7 -0
- package/build/dist/Types/AI/AIResourceContext.js.map +1 -1
- package/build/dist/Types/BaseDatabase/AggregationType.js +7 -0
- package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardTemplates.js +22 -8
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js +71 -0
- package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js +1462 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js +691 -0
- package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js +1407 -0
- package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js +60 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js +804 -0
- package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseSystem.js +1860 -0
- package/build/dist/Types/DatabaseServer/DatabaseSystem.js.map +1 -0
- package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js +406 -0
- package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js.map +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Monitor/CephAlertTemplates.js +18 -0
- package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/DatabaseAlertTemplates.js +1518 -0
- package/build/dist/Types/Monitor/DatabaseAlertTemplates.js.map +1 -0
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +57 -24
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +104 -18
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +80 -3
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +10 -3
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +29 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +21 -6
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -1
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +25 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
- package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js +136 -0
- package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js.map +1 -0
- package/build/dist/Types/Permission.js +234 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +10 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -1
- package/build/dist/Types/Rum/WebVitals.js +12 -0
- package/build/dist/Types/Rum/WebVitals.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityType.js +23 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +14 -0
- package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -1
- package/build/dist/Types/Telemetry/ServiceType.js +8 -0
- package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
- package/build/dist/UI/Components/Banner/Banner.js +1 -1
- package/build/dist/UI/Components/Banner/Banner.js.map +1 -1
- package/build/dist/UI/Components/Card/Card.js +1 -1
- package/build/dist/UI/Components/Card/Card.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js +1 -1
- package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
- package/build/dist/UI/Components/Date/StartAndEndDate.js +5 -5
- package/build/dist/UI/Components/Date/StartAndEndDate.js.map +1 -1
- package/build/dist/UI/Components/Diagram/HorizontalStepChain.js +1 -1
- package/build/dist/UI/Components/Diagram/HorizontalStepChain.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +1 -1
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
- package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
- package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js +1 -1
- package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +4 -4
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +9 -2
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Components/Page/Page.js +1 -1
- package/build/dist/UI/Components/Page/Page.js.map +1 -1
- package/build/dist/UI/Components/Pagination/Pagination.js +3 -3
- package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
- package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js +1 -1
- package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js.map +1 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -1
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +3 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceList.js +6 -6
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +1 -1
- package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +2 -2
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +66 -33
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +7 -0
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -1
- package/build/dist/UI/Utils/TelemetryService.js +3 -2
- package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
- package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js +110 -0
- package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js.map +1 -0
- package/build/dist/Utils/LabelRuleImportExport.js +2 -0
- package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
- package/build/dist/Utils/MetricUnitUtil.js +77 -13
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +58 -13
- package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -1
- package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js +161 -0
- package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/PlatformResourceIdentity.js +323 -0
- package/build/dist/Utils/Monitor/PlatformResourceIdentity.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js +270 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +23 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js +61 -0
- package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js +178 -0
- package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js.map +1 -0
- package/build/dist/Utils/Telemetry/EntityKey.js +133 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/build/dist/Utils/ValueFormatter.js +15 -0
- package/build/dist/Utils/ValueFormatter.js.map +1 -1
- package/package.json +1 -1
- package/tsconfig.tests.json +7 -5
|
@@ -0,0 +1,1407 @@
|
|
|
1
|
+
import { getDatabaseSystemDisplayName, getDatabaseSystemFamily, getDefaultDatabasePort, normalizeDatabaseSystem, trimTrailingCharacter, } from "./DatabaseSystem";
|
|
2
|
+
/*
|
|
3
|
+
* DNS suffixes that only resolve inside one network: `.local` (mDNS, and the
|
|
4
|
+
* `cluster.local` Kubernetes domain), `.internal` (ICANN's private-use TLD —
|
|
5
|
+
* `ec2.internal`, `compute.internal`, GCE's `c.<project>.internal`),
|
|
6
|
+
* `.home.arpa` (RFC 8375) and `.localdomain` (the placeholder domain of a
|
|
7
|
+
* machine with none). The same name in two VPCs or clusters can be two
|
|
8
|
+
* different servers, so they are qualified like private IPs.
|
|
9
|
+
*/
|
|
10
|
+
export const NETWORK_SCOPED_NAME_SUFFIXES = [
|
|
11
|
+
".local",
|
|
12
|
+
".internal",
|
|
13
|
+
".home.arpa",
|
|
14
|
+
".localdomain",
|
|
15
|
+
];
|
|
16
|
+
/*
|
|
17
|
+
* The attributes a SQL Server client span may name its instance in, most
|
|
18
|
+
* specific first: the legacy `db.mssql.instance_name`, then the instance
|
|
19
|
+
* half of `db.namespace` (`instance|database` for a named instance).
|
|
20
|
+
*/
|
|
21
|
+
export const DATABASE_INSTANCE_NAME_ATTRIBUTE = "db.mssql.instance_name";
|
|
22
|
+
export const DATABASE_NAMESPACE_ATTRIBUTE = "db.namespace";
|
|
23
|
+
export const DATABASE_INSTANCE_ATTRIBUTES = [
|
|
24
|
+
DATABASE_INSTANCE_NAME_ATTRIBUTE,
|
|
25
|
+
DATABASE_NAMESPACE_ATTRIBUTE,
|
|
26
|
+
];
|
|
27
|
+
const SQL_SERVER_SYSTEM = "microsoft.sql_server";
|
|
28
|
+
// The default instance: reached on the engine's default port, named by none.
|
|
29
|
+
const SQL_SERVER_DEFAULT_INSTANCE = "mssqlserver";
|
|
30
|
+
/*
|
|
31
|
+
* SQL Server instance names: at most 16 letters, digits, "_" and "$"
|
|
32
|
+
* (lowercased here). "#" is refused: it would end the address as a URL
|
|
33
|
+
* fragment, and the name could not round-trip.
|
|
34
|
+
*/
|
|
35
|
+
const SQL_SERVER_INSTANCE_REGEX = /^[a-z0-9_$-]{1,16}$/;
|
|
36
|
+
// `scheme://`, including the JDBC `jdbc:postgresql://` spelling.
|
|
37
|
+
const URL_SCHEME_REGEX = /^(?:jdbc:)?[a-z][a-z0-9+.-]*:\/\//i;
|
|
38
|
+
/*
|
|
39
|
+
* Where the address part of a value ends: a path, a query, a fragment, or
|
|
40
|
+
* the `;property=value` list of JDBC SQL Server URLs.
|
|
41
|
+
*/
|
|
42
|
+
const ADDRESS_END_REGEX = /[/?#;]/;
|
|
43
|
+
/*
|
|
44
|
+
* ADO.NET / SQL Server `tcp:host,1433` protocol prefix. Not stripped when
|
|
45
|
+
* what follows is only digits, so a host literally named "tcp" with a port
|
|
46
|
+
* ("tcp:5432") is still read as host:port.
|
|
47
|
+
*/
|
|
48
|
+
const SQL_SERVER_TCP_PREFIX_REGEX = /^tcp:(?!\d+$)/i;
|
|
49
|
+
/*
|
|
50
|
+
* SQL Server spellings of "this machine": `(local)`, `.` and LocalDB. They
|
|
51
|
+
* are mapped to "localhost" so the loopback rule treats them like any other
|
|
52
|
+
* loopback address.
|
|
53
|
+
*/
|
|
54
|
+
const LOCAL_MACHINE_ALIASES = new Set([
|
|
55
|
+
"(local)",
|
|
56
|
+
".",
|
|
57
|
+
"(localdb)",
|
|
58
|
+
]);
|
|
59
|
+
/*
|
|
60
|
+
* "localhost" and the /etc/hosts names distributions give the loopback
|
|
61
|
+
* address. Not `*.localhost`, but they resolve to 127.0.0.1 / ::1 on the
|
|
62
|
+
* caller's own machine all the same.
|
|
63
|
+
*/
|
|
64
|
+
const LOOPBACK_HOST_NAMES = new Set([
|
|
65
|
+
"localhost",
|
|
66
|
+
"localhost.localdomain",
|
|
67
|
+
"localhost4",
|
|
68
|
+
"localhost4.localdomain4",
|
|
69
|
+
"localhost6",
|
|
70
|
+
"localhost6.localdomain6",
|
|
71
|
+
"ip6-localhost",
|
|
72
|
+
"ip6-loopback",
|
|
73
|
+
]);
|
|
74
|
+
/*
|
|
75
|
+
* Names that resolve to the machine the caller runs on (Docker Desktop,
|
|
76
|
+
* Podman, kind), so like loopback they mean something different for every
|
|
77
|
+
* caller. Besides these, every `*.docker.internal` name and every
|
|
78
|
+
* `host.<tool>.internal` name (minikube, k3d, Lima, OrbStack, Rancher
|
|
79
|
+
* Desktop) is host-relative too (isHostRelativeDatabaseHost).
|
|
80
|
+
*/
|
|
81
|
+
const HOST_RELATIVE_NAMES = new Set([
|
|
82
|
+
"host.docker.internal",
|
|
83
|
+
"host.containers.internal",
|
|
84
|
+
"gateway.docker.internal",
|
|
85
|
+
"docker.for.mac.localhost",
|
|
86
|
+
"kubernetes.docker.internal",
|
|
87
|
+
]);
|
|
88
|
+
const DOCKER_DESKTOP_SUFFIX = ".docker.internal";
|
|
89
|
+
const EPHEMERAL_CLOUD_PLATFORMS = new Set([
|
|
90
|
+
"aws_ecs",
|
|
91
|
+
"aws_lambda",
|
|
92
|
+
"aws_app_runner",
|
|
93
|
+
"gcp_cloud_run",
|
|
94
|
+
"gcp_cloud_functions",
|
|
95
|
+
"gcp_app_engine",
|
|
96
|
+
"azure_container_apps",
|
|
97
|
+
"azure_container_instances",
|
|
98
|
+
"azure_functions",
|
|
99
|
+
]);
|
|
100
|
+
const KUBERNETES_IDENTITY_ATTRIBUTES = [
|
|
101
|
+
"k8s.pod.name",
|
|
102
|
+
"k8s.namespace.name",
|
|
103
|
+
"k8s.cluster.name",
|
|
104
|
+
"k8s.node.name",
|
|
105
|
+
];
|
|
106
|
+
const KUBERNETES_CLUSTER_DOMAIN_SUFFIX = ".svc.cluster.local";
|
|
107
|
+
/*
|
|
108
|
+
* `<a>.<b>.svc` or `<a>.<b>.svc.<any cluster domain>` (a Service), and the
|
|
109
|
+
* three-label `<pod>.<svc>.<ns>.svc…` form (a StatefulSet member behind a
|
|
110
|
+
* headless Service).
|
|
111
|
+
*/
|
|
112
|
+
const KUBERNETES_SERVICE_DNS_REGEX = /^((?:[a-z0-9_-]+\.){1,2}[a-z0-9_-]+)\.svc(?:\..+)?$/;
|
|
113
|
+
/*
|
|
114
|
+
* Second labels that make a two-label name a private DNS zone rather than a
|
|
115
|
+
* Kubernetes `<service>.<namespace>`: `db.internal` from a pod is far more
|
|
116
|
+
* likely a VPC name than a Service in a namespace called "internal". Such a
|
|
117
|
+
* name is still network-scoped (NETWORK_SCOPED_NAME_SUFFIXES).
|
|
118
|
+
*/
|
|
119
|
+
const NON_NAMESPACE_TOP_LABELS = new Set([
|
|
120
|
+
"local",
|
|
121
|
+
"internal",
|
|
122
|
+
"localdomain",
|
|
123
|
+
]);
|
|
124
|
+
// RFC 1123 label, as Kubernetes validates namespace / Service / pod names.
|
|
125
|
+
const DNS_LABEL_REGEX = /^[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?$/;
|
|
126
|
+
const HOSTNAME_LABEL_REGEX = /^[a-z0-9_-]{1,63}$/;
|
|
127
|
+
const DIGITS_ONLY_REGEX = /^\d+$/;
|
|
128
|
+
const IPV6_CHARACTERS_REGEX = /^[0-9a-f:.]+$/;
|
|
129
|
+
const IPV4_REGEX = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
130
|
+
const IPV6_GROUP_REGEX = /^[0-9a-f]{1,4}$/;
|
|
131
|
+
// A character a bare user name never has: a dot, colon, bracket or backslash.
|
|
132
|
+
const NOT_A_BARE_WORD_REGEX = /[.:[\]\\]/;
|
|
133
|
+
// The member number of an Atlas host: `<cluster>-shard-<NN>-<NN>`.
|
|
134
|
+
const ATLAS_MEMBER_NUMBER_REGEX = /^\d{2}-\d{2}$/;
|
|
135
|
+
const ATLAS_MEMBER_MARKER = "-shard-";
|
|
136
|
+
const ATLAS_DOMAIN = "mongodb.net";
|
|
137
|
+
const DISPLAY_NAME_MAX_LENGTH = 100;
|
|
138
|
+
// How much of a rejected value a message repeats back.
|
|
139
|
+
const MAX_ECHOED_VALUE_LENGTH = 100;
|
|
140
|
+
// ---- low-level parsing ---------------------------------------------------
|
|
141
|
+
function parseIpv4Octets(text) {
|
|
142
|
+
const match = IPV4_REGEX.exec(text);
|
|
143
|
+
if (!match) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const octets = [
|
|
147
|
+
Number(match[1]),
|
|
148
|
+
Number(match[2]),
|
|
149
|
+
Number(match[3]),
|
|
150
|
+
Number(match[4]),
|
|
151
|
+
];
|
|
152
|
+
for (const octet of octets) {
|
|
153
|
+
if (octet > 255) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return octets;
|
|
158
|
+
}
|
|
159
|
+
function parseIpv4(text) {
|
|
160
|
+
const octets = parseIpv4Octets(text);
|
|
161
|
+
return octets ? octets.join(".") : null;
|
|
162
|
+
}
|
|
163
|
+
/** The eight 16-bit groups of an IPv6 literal (zone id stripped), or null. */
|
|
164
|
+
function parseIpv6Groups(text) {
|
|
165
|
+
let value = text.trim().toLowerCase();
|
|
166
|
+
const zoneIndex = value.indexOf("%");
|
|
167
|
+
if (zoneIndex >= 0) {
|
|
168
|
+
value = value.substring(0, zoneIndex);
|
|
169
|
+
}
|
|
170
|
+
if (!value.includes(":") || !IPV6_CHARACTERS_REGEX.test(value)) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
// An embedded dotted IPv4 tail ("::ffff:10.0.0.5") becomes two groups.
|
|
174
|
+
const lastColon = value.lastIndexOf(":");
|
|
175
|
+
const tail = value.substring(lastColon + 1);
|
|
176
|
+
if (tail.includes(".")) {
|
|
177
|
+
const octets = parseIpv4Octets(tail);
|
|
178
|
+
if (!octets) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
value = `${value.substring(0, lastColon + 1)}${((octets[0] << 8) |
|
|
182
|
+
octets[1]).toString(16)}:${((octets[2] << 8) | octets[3]).toString(16)}`;
|
|
183
|
+
}
|
|
184
|
+
const doubleColonCount = value.split("::").length - 1;
|
|
185
|
+
if (doubleColonCount > 1) {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
let parts;
|
|
189
|
+
if (doubleColonCount === 1) {
|
|
190
|
+
const halves = value.split("::");
|
|
191
|
+
const head = halves[0] ? halves[0].split(":") : [];
|
|
192
|
+
const rest = halves[1] ? halves[1].split(":") : [];
|
|
193
|
+
const missing = 8 - head.length - rest.length;
|
|
194
|
+
if (missing < 1) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
parts = [...head, ...new Array(missing).fill("0"), ...rest];
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
parts = value.split(":");
|
|
201
|
+
}
|
|
202
|
+
if (parts.length !== 8) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const groups = [];
|
|
206
|
+
for (const part of parts) {
|
|
207
|
+
if (!IPV6_GROUP_REGEX.test(part)) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
groups.push(parseInt(part, 16));
|
|
211
|
+
}
|
|
212
|
+
return groups;
|
|
213
|
+
}
|
|
214
|
+
/*
|
|
215
|
+
* Canonical text form of an IPv6 literal (RFC 5952: lowercase, no leading
|
|
216
|
+
* zeros, the longest run of two or more zero groups compressed to "::"), or
|
|
217
|
+
* the dotted IPv4 address for an IPv4-mapped one (::ffff:a.b.c.d).
|
|
218
|
+
*/
|
|
219
|
+
function canonicalizeIpv6(text) {
|
|
220
|
+
const groups = parseIpv6Groups(text);
|
|
221
|
+
if (!groups) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const isIpv4Mapped = groups[0] === 0 &&
|
|
225
|
+
groups[1] === 0 &&
|
|
226
|
+
groups[2] === 0 &&
|
|
227
|
+
groups[3] === 0 &&
|
|
228
|
+
groups[4] === 0 &&
|
|
229
|
+
groups[5] === 0xffff;
|
|
230
|
+
if (isIpv4Mapped) {
|
|
231
|
+
return [
|
|
232
|
+
groups[6] >> 8,
|
|
233
|
+
groups[6] & 0xff,
|
|
234
|
+
groups[7] >> 8,
|
|
235
|
+
groups[7] & 0xff,
|
|
236
|
+
].join(".");
|
|
237
|
+
}
|
|
238
|
+
let bestStart = -1;
|
|
239
|
+
let bestLength = 0;
|
|
240
|
+
let runStart = -1;
|
|
241
|
+
for (let index = 0; index <= groups.length; index++) {
|
|
242
|
+
if (index < groups.length && groups[index] === 0) {
|
|
243
|
+
if (runStart < 0) {
|
|
244
|
+
runStart = index;
|
|
245
|
+
}
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
if (runStart >= 0) {
|
|
249
|
+
const runLength = index - runStart;
|
|
250
|
+
if (runLength > bestLength) {
|
|
251
|
+
bestStart = runStart;
|
|
252
|
+
bestLength = runLength;
|
|
253
|
+
}
|
|
254
|
+
runStart = -1;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const hex = groups.map((group) => {
|
|
258
|
+
return group.toString(16);
|
|
259
|
+
});
|
|
260
|
+
if (bestLength < 2) {
|
|
261
|
+
return hex.join(":");
|
|
262
|
+
}
|
|
263
|
+
const head = hex.slice(0, bestStart).join(":");
|
|
264
|
+
const tail = hex.slice(bestStart + bestLength).join(":");
|
|
265
|
+
return `${head}::${tail}`;
|
|
266
|
+
}
|
|
267
|
+
function isValidHostname(host) {
|
|
268
|
+
if (host.length === 0 || host.length > 253) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
const labels = host.split(".");
|
|
272
|
+
for (const label of labels) {
|
|
273
|
+
if (!HOSTNAME_LABEL_REGEX.test(label)) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/*
|
|
278
|
+
* A top-level label is never all digits (RFC 3696 §2), so "999.1.1.1" or
|
|
279
|
+
* "12345" is a mangled address, not a name.
|
|
280
|
+
*/
|
|
281
|
+
return !DIGITS_ONLY_REGEX.test(labels[labels.length - 1]);
|
|
282
|
+
}
|
|
283
|
+
function toValidPort(value) {
|
|
284
|
+
let numeric;
|
|
285
|
+
if (typeof value === "number") {
|
|
286
|
+
numeric = value;
|
|
287
|
+
}
|
|
288
|
+
else if (typeof value === "string") {
|
|
289
|
+
const trimmed = value.trim();
|
|
290
|
+
if (!DIGITS_ONLY_REGEX.test(trimmed)) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
numeric = Number(trimmed);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
return Number.isInteger(numeric) && numeric >= 1 && numeric <= 65535
|
|
299
|
+
? numeric
|
|
300
|
+
: null;
|
|
301
|
+
}
|
|
302
|
+
function canonicalClusterName(value) {
|
|
303
|
+
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
304
|
+
}
|
|
305
|
+
function canonicalDnsLabel(value) {
|
|
306
|
+
if (typeof value !== "string") {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
const label = value.trim().toLowerCase();
|
|
310
|
+
return DNS_LABEL_REGEX.test(label) ? label : null;
|
|
311
|
+
}
|
|
312
|
+
function isNonBlankText(value) {
|
|
313
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
314
|
+
}
|
|
315
|
+
/*
|
|
316
|
+
* A SQL Server instance name as it becomes part of an identity: trimmed,
|
|
317
|
+
* lowercased, validated; the default instance (MSSQLSERVER) is no instance.
|
|
318
|
+
* `undefined` for an invalid name, null for none.
|
|
319
|
+
*/
|
|
320
|
+
function canonicalInstanceName(value) {
|
|
321
|
+
if (value === null || value === undefined) {
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
if (typeof value !== "string") {
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
const instance = value.trim().toLowerCase();
|
|
328
|
+
if (!instance) {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
if (!SQL_SERVER_INSTANCE_REGEX.test(instance)) {
|
|
332
|
+
return undefined;
|
|
333
|
+
}
|
|
334
|
+
return instance === SQL_SERVER_DEFAULT_INSTANCE ? null : instance;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* A host as stored in a DatabaseEndpoint, split into the network host and
|
|
338
|
+
* the SQL Server named instance it may carry (`sql1.corp\inst01` →
|
|
339
|
+
* `sql1.corp` + `inst01`; everything else → the host + "").
|
|
340
|
+
*/
|
|
341
|
+
export function splitDatabaseHostInstance(host) {
|
|
342
|
+
const value = typeof host === "string" ? host : "";
|
|
343
|
+
const index = value.indexOf("\\");
|
|
344
|
+
return index >= 0
|
|
345
|
+
? { host: value.substring(0, index), instance: value.substring(index + 1) }
|
|
346
|
+
: { host: value, instance: "" };
|
|
347
|
+
}
|
|
348
|
+
function joinHostInstance(host, instance) {
|
|
349
|
+
return instance ? `${host}\\${instance}` : host;
|
|
350
|
+
}
|
|
351
|
+
/*
|
|
352
|
+
* Where the address in a raw value is: the URL authority (scheme, userinfo
|
|
353
|
+
* and path dropped) or the value up to its path / query / JDBC properties,
|
|
354
|
+
* `tcp:` prefix dropped. May still hold a host list. Null for nothing.
|
|
355
|
+
*/
|
|
356
|
+
function extractAddressText(raw) {
|
|
357
|
+
if (typeof raw !== "string") {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
let value = raw.trim();
|
|
361
|
+
if (!value || value.startsWith("/")) {
|
|
362
|
+
return null;
|
|
363
|
+
}
|
|
364
|
+
const schemeMatch = URL_SCHEME_REGEX.exec(value);
|
|
365
|
+
if (schemeMatch) {
|
|
366
|
+
const afterScheme = value.substring(schemeMatch[0].length);
|
|
367
|
+
const authorityEnd = afterScheme.search(ADDRESS_END_REGEX);
|
|
368
|
+
let authority = authorityEnd >= 0 ? afterScheme.substring(0, authorityEnd) : afterScheme;
|
|
369
|
+
const userInfoEnd = authority.lastIndexOf("@");
|
|
370
|
+
if (userInfoEnd >= 0) {
|
|
371
|
+
authority = authority.substring(userInfoEnd + 1);
|
|
372
|
+
}
|
|
373
|
+
value = authority;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
value = value.replace(SQL_SERVER_TCP_PREFIX_REGEX, "");
|
|
377
|
+
const pathStart = value.search(ADDRESS_END_REGEX);
|
|
378
|
+
if (pathStart >= 0) {
|
|
379
|
+
value = value.substring(0, pathStart);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
value = value.trim();
|
|
383
|
+
return value ? value : null;
|
|
384
|
+
}
|
|
385
|
+
/*
|
|
386
|
+
* One host of an address: `host`, `host:port`, `[v6]:port`, bare IPv6,
|
|
387
|
+
* `host\instance[:port]` and the SQL Server local-machine spellings.
|
|
388
|
+
* `portText` is a port found beside it (the `,1433` of SQL Server); a port
|
|
389
|
+
* outside 1..65535 is dropped, or — with `strictPort` — rejects the value.
|
|
390
|
+
*/
|
|
391
|
+
function parseSingleHost(text, initialPortText, strictPort) {
|
|
392
|
+
let value = text.trim();
|
|
393
|
+
let portText = initialPortText;
|
|
394
|
+
// "host\instance" (optionally "host\instance:port"): a SQL Server named instance.
|
|
395
|
+
let instance = null;
|
|
396
|
+
const backslashIndex = value.indexOf("\\");
|
|
397
|
+
if (backslashIndex >= 0) {
|
|
398
|
+
let instanceText = value.substring(backslashIndex + 1).trim();
|
|
399
|
+
value = value.substring(0, backslashIndex).trim();
|
|
400
|
+
const colonIndex = instanceText.indexOf(":");
|
|
401
|
+
if (colonIndex >= 0) {
|
|
402
|
+
if (portText === null) {
|
|
403
|
+
portText = instanceText.substring(colonIndex + 1);
|
|
404
|
+
}
|
|
405
|
+
instanceText = instanceText.substring(0, colonIndex);
|
|
406
|
+
}
|
|
407
|
+
const canonical = canonicalInstanceName(instanceText);
|
|
408
|
+
if (canonical === undefined) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
instance = canonical;
|
|
412
|
+
}
|
|
413
|
+
if (!value) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
let host;
|
|
417
|
+
if (LOCAL_MACHINE_ALIASES.has(value.toLowerCase())) {
|
|
418
|
+
host = "localhost";
|
|
419
|
+
}
|
|
420
|
+
else if (value.startsWith("[")) {
|
|
421
|
+
const closing = value.indexOf("]");
|
|
422
|
+
if (closing < 0) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
const rest = value.substring(closing + 1);
|
|
426
|
+
if (rest) {
|
|
427
|
+
if (!rest.startsWith(":")) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
if (portText === null) {
|
|
431
|
+
portText = rest.substring(1);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
const ipv6 = canonicalizeIpv6(value.substring(1, closing));
|
|
435
|
+
if (!ipv6) {
|
|
436
|
+
// "[REDACTED]", "[HASHED:ab12cd34]" and other bracketed non-addresses.
|
|
437
|
+
return null;
|
|
438
|
+
}
|
|
439
|
+
host = ipv6;
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
const colonCount = (value.match(/:/g) || []).length;
|
|
443
|
+
if (colonCount >= 2) {
|
|
444
|
+
const ipv6 = canonicalizeIpv6(value);
|
|
445
|
+
if (!ipv6) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
host = ipv6;
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
let hostText = value;
|
|
452
|
+
if (colonCount === 1) {
|
|
453
|
+
const colonIndex = value.indexOf(":");
|
|
454
|
+
hostText = value.substring(0, colonIndex);
|
|
455
|
+
if (portText === null) {
|
|
456
|
+
portText = value.substring(colonIndex + 1);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
hostText = trimTrailingCharacter(hostText.trim().toLowerCase(), ".");
|
|
460
|
+
if (!hostText) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
const ipv4 = parseIpv4(hostText);
|
|
464
|
+
if (ipv4) {
|
|
465
|
+
host = ipv4;
|
|
466
|
+
}
|
|
467
|
+
else if (isValidHostname(hostText)) {
|
|
468
|
+
host = hostText;
|
|
469
|
+
}
|
|
470
|
+
else {
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
let port = null;
|
|
476
|
+
if (portText !== null && portText.trim() !== "") {
|
|
477
|
+
const trimmedPort = portText.trim();
|
|
478
|
+
if (!DIGITS_ONLY_REGEX.test(trimmedPort)) {
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
port = toValidPort(trimmedPort);
|
|
482
|
+
if (port === null && strictPort) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return instance ? { host, port, instance } : { host, port };
|
|
487
|
+
}
|
|
488
|
+
// Canonical order of the hosts of a list: host, then instance, then port.
|
|
489
|
+
function compareParsedHosts(a, b) {
|
|
490
|
+
var _a, _b;
|
|
491
|
+
if (a.host !== b.host) {
|
|
492
|
+
return a.host < b.host ? -1 : 1;
|
|
493
|
+
}
|
|
494
|
+
const instanceA = a.instance || "";
|
|
495
|
+
const instanceB = b.instance || "";
|
|
496
|
+
if (instanceA !== instanceB) {
|
|
497
|
+
return instanceA < instanceB ? -1 : 1;
|
|
498
|
+
}
|
|
499
|
+
return ((_a = a.port) !== null && _a !== void 0 ? _a : 0) - ((_b = b.port) !== null && _b !== void 0 ? _b : 0);
|
|
500
|
+
}
|
|
501
|
+
function parseAddressHosts(raw, strictPort) {
|
|
502
|
+
var _a;
|
|
503
|
+
const address = extractAddressText(raw);
|
|
504
|
+
if (address === null) {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
const parts = address.split(",");
|
|
508
|
+
// "host,1433": SQL Server's port separator, not a list.
|
|
509
|
+
if (parts.length === 2 && DIGITS_ONLY_REGEX.test(parts[1].trim())) {
|
|
510
|
+
const single = parseSingleHost(parts[0], parts[1].trim(), strictPort);
|
|
511
|
+
return single ? [single] : null;
|
|
512
|
+
}
|
|
513
|
+
const hosts = [];
|
|
514
|
+
const seen = new Set();
|
|
515
|
+
for (const part of parts) {
|
|
516
|
+
if (!part.trim()) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
const parsed = parseSingleHost(part, null, strictPort);
|
|
520
|
+
// A list is one logical server only when every host in it is readable.
|
|
521
|
+
if (!parsed) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
const key = `${parsed.host}\\${parsed.instance || ""}:${(_a = parsed.port) !== null && _a !== void 0 ? _a : ""}`;
|
|
525
|
+
if (!seen.has(key)) {
|
|
526
|
+
seen.add(key);
|
|
527
|
+
hosts.push(parsed);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (hosts.length === 0) {
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
533
|
+
hosts.sort(compareParsedHosts);
|
|
534
|
+
return hosts;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Parse a raw address attribute / connection string into a canonical host
|
|
538
|
+
* and an optional port. Accepts `host`, `host:port`, `[v6]:port`, bare IPv6
|
|
539
|
+
* (zone id stripped), URLs (`scheme://user:pw@host:port/db?x` — the userinfo
|
|
540
|
+
* split on the LAST "@" before the first "/"; JDBC `;key=value` properties
|
|
541
|
+
* dropped), host lists and the SQL Server forms `tcp:host,1433`,
|
|
542
|
+
* `host\instance` (the instance returned beside the host; MSSQLSERVER, the
|
|
543
|
+
* default instance, is no instance), `(local)` and `.`.
|
|
544
|
+
*
|
|
545
|
+
* A host list (`m1:27017,m2:27017`) names ONE logical server, so it yields
|
|
546
|
+
* the same host whatever order a client lists the members in: the first in
|
|
547
|
+
* canonical order (see parseHostAndPortList). Every listed host must parse.
|
|
548
|
+
*
|
|
549
|
+
* The host must then be a valid IPv4, IPv6 or RFC 1123 hostname — anything
|
|
550
|
+
* else (scrubbed values, "*", whitespace, a unix socket path) is null. A
|
|
551
|
+
* port outside 1..65535 is dropped (null); a non-numeric port rejects the
|
|
552
|
+
* whole value.
|
|
553
|
+
*/
|
|
554
|
+
export function parseHostAndPort(raw) {
|
|
555
|
+
const hosts = parseAddressHosts(raw, false);
|
|
556
|
+
return hosts && hosts.length > 0 ? hosts[0] : null;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Every host a raw address names, deduplicated and in canonical order —
|
|
560
|
+
* one entry for a single host, several for a host list — or null when the
|
|
561
|
+
* value (or any host in its list) is unparseable. The first entry is what
|
|
562
|
+
* parseHostAndPort returns.
|
|
563
|
+
*/
|
|
564
|
+
export function parseHostAndPortList(raw) {
|
|
565
|
+
return parseAddressHosts(raw, false);
|
|
566
|
+
}
|
|
567
|
+
// ---- host classification -------------------------------------------------
|
|
568
|
+
/** True for an IPv4 or IPv6 literal (brackets and a `\instance` tolerated). */
|
|
569
|
+
export function isIpLiteralHost(host) {
|
|
570
|
+
if (typeof host !== "string") {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
const value = splitDatabaseHostInstance(host.trim())
|
|
574
|
+
.host.trim()
|
|
575
|
+
.replace(/^\[(.*)\]$/, "$1");
|
|
576
|
+
if (!value) {
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
return parseIpv4(value) !== null || parseIpv6Groups(value) !== null;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* localhost (and the distributions' /etc/hosts aliases such as
|
|
583
|
+
* localhost.localdomain), *.localhost, 127/8, ::1, the unspecified addresses
|
|
584
|
+
* (:: and 0.0.0.0) and their IPv4-mapped IPv6 spellings.
|
|
585
|
+
*/
|
|
586
|
+
export function isLoopbackDatabaseHost(host) {
|
|
587
|
+
if (typeof host !== "string") {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
let value = splitDatabaseHostInstance(host.trim().toLowerCase()).host.trim();
|
|
591
|
+
if (!value) {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
if (LOCAL_MACHINE_ALIASES.has(value)) {
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
if (value.includes(":")) {
|
|
598
|
+
value = canonicalizeIpv6(value.replace(/^\[(.*)\]$/, "$1")) || value;
|
|
599
|
+
}
|
|
600
|
+
value = trimTrailingCharacter(value, ".");
|
|
601
|
+
if (LOOPBACK_HOST_NAMES.has(value) || value.endsWith(".localhost")) {
|
|
602
|
+
return true;
|
|
603
|
+
}
|
|
604
|
+
if (value === "::1" || value === "::" || value === "0.0.0.0") {
|
|
605
|
+
return true;
|
|
606
|
+
}
|
|
607
|
+
const octets = parseIpv4Octets(value);
|
|
608
|
+
return octets !== null && octets[0] === 127;
|
|
609
|
+
}
|
|
610
|
+
/*
|
|
611
|
+
* `host.<tool>.internal`: minikube, k3d, Lima, OrbStack, Rancher Desktop —
|
|
612
|
+
* the per-machine gateway name of each local Kubernetes / container tool.
|
|
613
|
+
* Checked with string operations (the tool label is one RFC 1123 label).
|
|
614
|
+
*/
|
|
615
|
+
function isHostGatewayName(value) {
|
|
616
|
+
const labels = value.split(".");
|
|
617
|
+
return (labels.length === 3 &&
|
|
618
|
+
labels[0] === "host" &&
|
|
619
|
+
labels[2] === "internal" &&
|
|
620
|
+
DNS_LABEL_REGEX.test(labels[1]));
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* host.docker.internal and the other "the machine I run on" names: every
|
|
624
|
+
* `*.docker.internal` name, every `host.<tool>.internal` name (minikube,
|
|
625
|
+
* k3d, Lima, OrbStack, Rancher Desktop) and Podman's
|
|
626
|
+
* host.containers.internal.
|
|
627
|
+
*/
|
|
628
|
+
export function isHostRelativeDatabaseHost(host) {
|
|
629
|
+
if (typeof host !== "string") {
|
|
630
|
+
return false;
|
|
631
|
+
}
|
|
632
|
+
const value = trimTrailingCharacter(splitDatabaseHostInstance(host.trim().toLowerCase()).host.trim(), ".");
|
|
633
|
+
return (HOST_RELATIVE_NAMES.has(value) ||
|
|
634
|
+
value.endsWith(DOCKER_DESKTOP_SUFFIX) ||
|
|
635
|
+
isHostGatewayName(value));
|
|
636
|
+
}
|
|
637
|
+
function stripBracketsAndInstance(host) {
|
|
638
|
+
return splitDatabaseHostInstance(host.trim())
|
|
639
|
+
.host.trim()
|
|
640
|
+
.replace(/^\[(.*)\]$/, "$1");
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* RFC 1918 (10/8, 172.16/12, 192.168/16), CGNAT (100.64/10) and IPv6 ULA
|
|
644
|
+
* (fc00::/7) — ranges that are reused by every cluster and VPC, so an
|
|
645
|
+
* address in them only identifies something together with where it was
|
|
646
|
+
* seen from.
|
|
647
|
+
*/
|
|
648
|
+
export function isPrivateIpHost(host) {
|
|
649
|
+
if (typeof host !== "string") {
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
const value = stripBracketsAndInstance(host);
|
|
653
|
+
const octets = parseIpv4Octets(value);
|
|
654
|
+
if (octets) {
|
|
655
|
+
const first = octets[0];
|
|
656
|
+
const second = octets[1];
|
|
657
|
+
return (first === 10 ||
|
|
658
|
+
(first === 172 && second >= 16 && second <= 31) ||
|
|
659
|
+
(first === 192 && second === 168) ||
|
|
660
|
+
(first === 100 && second >= 64 && second <= 127));
|
|
661
|
+
}
|
|
662
|
+
const canonical = value.includes(":")
|
|
663
|
+
? canonicalizeIpv6(value)
|
|
664
|
+
: null;
|
|
665
|
+
if (!canonical) {
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
668
|
+
if (!canonical.includes(":")) {
|
|
669
|
+
// IPv4-mapped: classify the unwrapped IPv4 address.
|
|
670
|
+
return isPrivateIpHost(canonical);
|
|
671
|
+
}
|
|
672
|
+
const groups = parseIpv6Groups(canonical);
|
|
673
|
+
return groups !== null && (groups[0] & 0xfe00) === 0xfc00;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Link-local addresses — IPv4 169.254/16 and IPv6 fe80::/10 (zone id
|
|
677
|
+
* dropped). Every network link has its own, so one never identifies a
|
|
678
|
+
* server on its own (always LOCAL scope, see getDatabaseEndpointScope).
|
|
679
|
+
*/
|
|
680
|
+
export function isLinkLocalIpHost(host) {
|
|
681
|
+
if (typeof host !== "string") {
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
const value = stripBracketsAndInstance(host);
|
|
685
|
+
const octets = parseIpv4Octets(value);
|
|
686
|
+
if (octets) {
|
|
687
|
+
return octets[0] === 169 && octets[1] === 254;
|
|
688
|
+
}
|
|
689
|
+
const canonical = value.includes(":")
|
|
690
|
+
? canonicalizeIpv6(value)
|
|
691
|
+
: null;
|
|
692
|
+
if (!canonical) {
|
|
693
|
+
return false;
|
|
694
|
+
}
|
|
695
|
+
if (!canonical.includes(":")) {
|
|
696
|
+
return isLinkLocalIpHost(canonical);
|
|
697
|
+
}
|
|
698
|
+
const groups = parseIpv6Groups(canonical);
|
|
699
|
+
return groups !== null && (groups[0] & 0xffc0) === 0xfe80;
|
|
700
|
+
}
|
|
701
|
+
function isSingleLabelName(host) {
|
|
702
|
+
return !host.includes(".") && !isIpLiteralHost(host);
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* True for a host that only resolves inside one Kubernetes cluster or
|
|
706
|
+
* private network — cluster-local DNS and the other private DNS zones
|
|
707
|
+
* (NETWORK_SCOPED_NAME_SUFFIXES), private IPs and link-local IPs — and so
|
|
708
|
+
* is qualified with the caller's cluster (`…@<cluster>`), or LOCAL scope
|
|
709
|
+
* without one. A `\instance` is ignored.
|
|
710
|
+
*/
|
|
711
|
+
export function isClusterScopedDatabaseHost(host) {
|
|
712
|
+
if (typeof host !== "string") {
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
const value = trimTrailingCharacter(splitDatabaseHostInstance(host.trim().toLowerCase()).host.trim(), ".");
|
|
716
|
+
if (!value) {
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
for (const suffix of NETWORK_SCOPED_NAME_SUFFIXES) {
|
|
720
|
+
if (value.endsWith(suffix)) {
|
|
721
|
+
return true;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return isPrivateIpHost(value) || isLinkLocalIpHost(value);
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* True for a Kubernetes Service DNS name — `<service>.<namespace>.svc`, with
|
|
728
|
+
* or without its cluster domain (`.svc.cluster.local`), or
|
|
729
|
+
* `<pod>.<service>.<namespace>.svc…` for a StatefulSet member — which only a
|
|
730
|
+
* pod inside one cluster can resolve. The same test the endpoint parser
|
|
731
|
+
* expands such names with. Case and a trailing dot are ignored, as is a
|
|
732
|
+
* `\instance`; a port or `@cluster` is not part of a host.
|
|
733
|
+
*/
|
|
734
|
+
export function isKubernetesServiceDnsHost(host) {
|
|
735
|
+
if (typeof host !== "string") {
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
const value = trimTrailingCharacter(splitDatabaseHostInstance(host.trim().toLowerCase()).host.trim(), ".");
|
|
739
|
+
return value.length > 0 && KUBERNETES_SERVICE_DNS_REGEX.test(value);
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* True when the caller runs in Kubernetes as far as its context tells: it
|
|
743
|
+
* has a namespace or a cluster (or the discovery query said so, see
|
|
744
|
+
* DatabaseCallerContext.runsInKubernetes).
|
|
745
|
+
*/
|
|
746
|
+
export function isKubernetesDatabaseCaller(caller) {
|
|
747
|
+
if (!caller) {
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
return (caller.runsInKubernetes === true ||
|
|
751
|
+
isNonBlankText(caller.kubernetesNamespace) ||
|
|
752
|
+
canonicalClusterName(caller.kubernetesClusterName).length > 0);
|
|
753
|
+
}
|
|
754
|
+
/*
|
|
755
|
+
* `<pod>-<ordinal>.<service>`: a two-label name whose first label is a
|
|
756
|
+
* StatefulSet pod (`mongo-0`, `redis-node-2`) and whose second is the
|
|
757
|
+
* headless Service of the same release (`mongo`, `mongo-headless`,
|
|
758
|
+
* `redis-headless` — the same first dash-separated word) — a member in the
|
|
759
|
+
* caller's own namespace, not a Service in another one.
|
|
760
|
+
*/
|
|
761
|
+
function isStatefulSetMemberShortName(labels) {
|
|
762
|
+
const pod = labels[0];
|
|
763
|
+
const dash = pod.lastIndexOf("-");
|
|
764
|
+
if (dash <= 0 || dash === pod.length - 1) {
|
|
765
|
+
return false;
|
|
766
|
+
}
|
|
767
|
+
if (!DIGITS_ONLY_REGEX.test(pod.substring(dash + 1))) {
|
|
768
|
+
return false;
|
|
769
|
+
}
|
|
770
|
+
const release = pod.split("-")[0];
|
|
771
|
+
return release.length > 0 && labels[1].split("-")[0] === release;
|
|
772
|
+
}
|
|
773
|
+
/*
|
|
774
|
+
* Rule 3 (Kubernetes DNS): any `<a>.<b>.svc[.<domain>]` (and the member form
|
|
775
|
+
* `<pod>.<svc>.<ns>.svc[.<domain>]`) becomes `….svc.cluster.local`, so a
|
|
776
|
+
* custom cluster domain and the short `.svc` form key like the FQDN; a
|
|
777
|
+
* single-label name from a caller in a known namespace becomes that
|
|
778
|
+
* namespace's Service FQDN. From a caller that runs in Kubernetes, a
|
|
779
|
+
* two-label name is what the pod's resolver search path makes of it:
|
|
780
|
+
* `<service>.<namespace>` → `<service>.<namespace>.svc.cluster.local`, and
|
|
781
|
+
* a StatefulSet member of the caller's own namespace
|
|
782
|
+
* (`mongo-0.mongo-headless`) → `mongo-0.mongo-headless.<ns>.svc.cluster.local`
|
|
783
|
+
* (or, with no namespace known, the Service reading). Two-label names from
|
|
784
|
+
* anything else — a VM, a laptop — are left alone: there they are domains.
|
|
785
|
+
*/
|
|
786
|
+
function applyKubernetesDnsRules(host, kubernetesNamespace, runsInKubernetes) {
|
|
787
|
+
const serviceMatch = KUBERNETES_SERVICE_DNS_REGEX.exec(host);
|
|
788
|
+
if (serviceMatch) {
|
|
789
|
+
return `${serviceMatch[1]}${KUBERNETES_CLUSTER_DOMAIN_SUFFIX}`;
|
|
790
|
+
}
|
|
791
|
+
const namespace = canonicalDnsLabel(kubernetesNamespace);
|
|
792
|
+
if (isSingleLabelName(host)) {
|
|
793
|
+
if (namespace) {
|
|
794
|
+
return `${host}.${namespace}${KUBERNETES_CLUSTER_DOMAIN_SUFFIX}`;
|
|
795
|
+
}
|
|
796
|
+
return host;
|
|
797
|
+
}
|
|
798
|
+
if (!runsInKubernetes) {
|
|
799
|
+
return host;
|
|
800
|
+
}
|
|
801
|
+
const labels = host.split(".");
|
|
802
|
+
if (labels.length !== 2 ||
|
|
803
|
+
!DNS_LABEL_REGEX.test(labels[0]) ||
|
|
804
|
+
!DNS_LABEL_REGEX.test(labels[1]) ||
|
|
805
|
+
NON_NAMESPACE_TOP_LABELS.has(labels[1])) {
|
|
806
|
+
return host;
|
|
807
|
+
}
|
|
808
|
+
if (namespace && isStatefulSetMemberShortName(labels)) {
|
|
809
|
+
return `${host}.${namespace}${KUBERNETES_CLUSTER_DOMAIN_SUFFIX}`;
|
|
810
|
+
}
|
|
811
|
+
return `${host}${KUBERNETES_CLUSTER_DOMAIN_SUFFIX}`;
|
|
812
|
+
}
|
|
813
|
+
function buildEndpoint(host, port, kubernetesClusterName) {
|
|
814
|
+
return kubernetesClusterName
|
|
815
|
+
? { host, port, kubernetesClusterName }
|
|
816
|
+
: { host, port };
|
|
817
|
+
}
|
|
818
|
+
// ---- caller context ------------------------------------------------------
|
|
819
|
+
/*
|
|
820
|
+
* Resource attributes are FLAT semconv keys; the `resource.`-prefixed
|
|
821
|
+
* spelling used by stored telemetry rows is accepted as a fallback so a
|
|
822
|
+
* caller holding a stored attribute map gets the same answer.
|
|
823
|
+
*/
|
|
824
|
+
function readResourceAttribute(attributes, key) {
|
|
825
|
+
if (!attributes || typeof attributes !== "object") {
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
let value = attributes[key];
|
|
829
|
+
if (value === undefined || value === null || value === "") {
|
|
830
|
+
value = attributes[`resource.${key}`];
|
|
831
|
+
}
|
|
832
|
+
if (typeof value === "string") {
|
|
833
|
+
const trimmed = value.trim();
|
|
834
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
835
|
+
}
|
|
836
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
837
|
+
return String(value);
|
|
838
|
+
}
|
|
839
|
+
return null;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* True when the resource that made a call is not a stable machine: anything
|
|
843
|
+
* in Kubernetes, in a container, on a FaaS or managed-container platform, or
|
|
844
|
+
* with no `os.type` at all (no `system` resource detector ran, so
|
|
845
|
+
* `host.name` is whatever the SDK guessed — usually a pod or container
|
|
846
|
+
* name). Its `host.name` must never become a database's identity.
|
|
847
|
+
*/
|
|
848
|
+
export function isEphemeralCaller(resourceAttributes) {
|
|
849
|
+
const attributes = resourceAttributes && typeof resourceAttributes === "object"
|
|
850
|
+
? resourceAttributes
|
|
851
|
+
: {};
|
|
852
|
+
for (const key of KUBERNETES_IDENTITY_ATTRIBUTES) {
|
|
853
|
+
if (readResourceAttribute(attributes, key)) {
|
|
854
|
+
return true;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
if (readResourceAttribute(attributes, "container.id") ||
|
|
858
|
+
readResourceAttribute(attributes, "container.runtime")) {
|
|
859
|
+
return true;
|
|
860
|
+
}
|
|
861
|
+
for (const key of Object.keys(attributes)) {
|
|
862
|
+
const bareKey = key.startsWith("resource.")
|
|
863
|
+
? key.substring("resource.".length)
|
|
864
|
+
: key;
|
|
865
|
+
if (bareKey.startsWith("faas.") && readResourceAttribute(attributes, key)) {
|
|
866
|
+
return true;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
const cloudPlatform = readResourceAttribute(attributes, "cloud.platform");
|
|
870
|
+
if (cloudPlatform &&
|
|
871
|
+
EPHEMERAL_CLOUD_PLATFORMS.has(cloudPlatform.toLowerCase())) {
|
|
872
|
+
return true;
|
|
873
|
+
}
|
|
874
|
+
return readResourceAttribute(attributes, "os.type") === null;
|
|
875
|
+
}
|
|
876
|
+
/** The parts of a caller's resource that endpoint canonicalization needs. */
|
|
877
|
+
export function buildDatabaseCallerContext(resourceAttributes) {
|
|
878
|
+
return {
|
|
879
|
+
kubernetesNamespace: readResourceAttribute(resourceAttributes, "k8s.namespace.name"),
|
|
880
|
+
kubernetesClusterName: readResourceAttribute(resourceAttributes, "k8s.cluster.name"),
|
|
881
|
+
hostName: readResourceAttribute(resourceAttributes, "host.name"),
|
|
882
|
+
isEphemeral: isEphemeralCaller(resourceAttributes),
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* The SQL Server named instance a client span names beside its address —
|
|
887
|
+
* `db.mssql.instance_name`, else the instance half of a `db.namespace` of
|
|
888
|
+
* the form `instance|database` — or null. Only for SQL Server: other
|
|
889
|
+
* engines give `db.namespace` other meanings. Returned raw (untrimmed);
|
|
890
|
+
* canonicalizeDatabaseEndpoint validates it. Pass the result as its
|
|
891
|
+
* `instance`; anything that memoizes on the attributes it reads should key
|
|
892
|
+
* on this derived instance (as DatabaseCallEntityKeys does) or include
|
|
893
|
+
* DATABASE_INSTANCE_ATTRIBUTES.
|
|
894
|
+
*/
|
|
895
|
+
export function readDatabaseInstanceName(input) {
|
|
896
|
+
if (!input || typeof input.getAttribute !== "function") {
|
|
897
|
+
return null;
|
|
898
|
+
}
|
|
899
|
+
if (normalizeDatabaseSystem(input.system) !== SQL_SERVER_SYSTEM) {
|
|
900
|
+
return null;
|
|
901
|
+
}
|
|
902
|
+
const toText = (value) => {
|
|
903
|
+
if (typeof value === "string") {
|
|
904
|
+
return value === "" ? null : value;
|
|
905
|
+
}
|
|
906
|
+
if (typeof value === "number") {
|
|
907
|
+
return String(value);
|
|
908
|
+
}
|
|
909
|
+
return null;
|
|
910
|
+
};
|
|
911
|
+
const instanceName = toText(input.getAttribute(DATABASE_INSTANCE_NAME_ATTRIBUTE));
|
|
912
|
+
if (instanceName !== null) {
|
|
913
|
+
return instanceName;
|
|
914
|
+
}
|
|
915
|
+
const namespace = toText(input.getAttribute(DATABASE_NAMESPACE_ATTRIBUTE));
|
|
916
|
+
if (namespace !== null) {
|
|
917
|
+
const separator = namespace.indexOf("|");
|
|
918
|
+
if (separator >= 0) {
|
|
919
|
+
return namespace.substring(0, separator);
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return null;
|
|
923
|
+
}
|
|
924
|
+
// ---- canonicalization ----------------------------------------------------
|
|
925
|
+
/**
|
|
926
|
+
* Canonicalize what a caller saw into a database endpoint, or null when it
|
|
927
|
+
* names no identifiable server. Rules, in order:
|
|
928
|
+
*
|
|
929
|
+
* 1. parse the address; port = explicit port ?? parsed port ?? engine
|
|
930
|
+
* default ?? null. A SQL Server named instance (`host\instance` in the
|
|
931
|
+
* address, else `instance`, see readDatabaseInstanceName) is kept as
|
|
932
|
+
* part of the host only while no port is known — a port already names
|
|
933
|
+
* the instance — and then takes no default port;
|
|
934
|
+
* 2. loopback / host-relative: for a client call → null (a per-caller
|
|
935
|
+
* sidecar, proxy or dev laptop); for a collector → the collector's own
|
|
936
|
+
* host.name when the collector is not ephemeral, else null;
|
|
937
|
+
* 3. Kubernetes DNS expansion (see applyKubernetesDnsRules);
|
|
938
|
+
* 4. cluster-local / private-network names and private IPs are qualified
|
|
939
|
+
* with the caller's `k8s.cluster.name` when it has one.
|
|
940
|
+
*/
|
|
941
|
+
export function canonicalizeDatabaseEndpoint(input) {
|
|
942
|
+
var _a, _b;
|
|
943
|
+
if (!input) {
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
946
|
+
const parsed = parseHostAndPort(input.address);
|
|
947
|
+
if (!parsed) {
|
|
948
|
+
return null;
|
|
949
|
+
}
|
|
950
|
+
const caller = input.caller || null;
|
|
951
|
+
const knownPort = (_a = toValidPort(input.port)) !== null && _a !== void 0 ? _a : parsed.port;
|
|
952
|
+
let instance = null;
|
|
953
|
+
if (knownPort === null) {
|
|
954
|
+
const reported = canonicalInstanceName(input.instance);
|
|
955
|
+
instance = parsed.instance || (reported ? reported : null);
|
|
956
|
+
}
|
|
957
|
+
const port = (_b = knownPort !== null && knownPort !== void 0 ? knownPort : (instance ? null : getDefaultDatabasePort(input.system))) !== null && _b !== void 0 ? _b : null;
|
|
958
|
+
let host = parsed.host;
|
|
959
|
+
if (isLoopbackDatabaseHost(host) || isHostRelativeDatabaseHost(host)) {
|
|
960
|
+
if (input.purpose !== "collector") {
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
if (!caller || caller.isEphemeral || !caller.hostName) {
|
|
964
|
+
return null;
|
|
965
|
+
}
|
|
966
|
+
const callerHost = parseHostAndPort(caller.hostName);
|
|
967
|
+
if (!callerHost ||
|
|
968
|
+
isLoopbackDatabaseHost(callerHost.host) ||
|
|
969
|
+
isHostRelativeDatabaseHost(callerHost.host)) {
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
host = callerHost.host;
|
|
973
|
+
}
|
|
974
|
+
host = applyKubernetesDnsRules(host, caller === null || caller === void 0 ? void 0 : caller.kubernetesNamespace, isKubernetesDatabaseCaller(caller));
|
|
975
|
+
const clusterName = isClusterScopedDatabaseHost(host)
|
|
976
|
+
? canonicalClusterName(caller === null || caller === void 0 ? void 0 : caller.kubernetesClusterName)
|
|
977
|
+
: "";
|
|
978
|
+
return buildEndpoint(joinHostInstance(host, instance), port, clusterName);
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* "local" for an endpoint that only resolves inside one network or cluster
|
|
982
|
+
* and carries nothing to say which: a single-label name, cluster-local or
|
|
983
|
+
* private-zone DNS or a private IP without a cluster qualifier, a link-local
|
|
984
|
+
* IP (per link, qualified or not) and loopback / host-relative names,
|
|
985
|
+
* defensively. Local endpoints are still keyed (it is what the span says)
|
|
986
|
+
* but never create a DatabaseServer row and never auto-match an alias.
|
|
987
|
+
*/
|
|
988
|
+
export function getDatabaseEndpointScope(endpoint) {
|
|
989
|
+
const host = endpoint && typeof endpoint.host === "string"
|
|
990
|
+
? splitDatabaseHostInstance(endpoint.host.trim().toLowerCase()).host
|
|
991
|
+
: "";
|
|
992
|
+
if (!host) {
|
|
993
|
+
return "local";
|
|
994
|
+
}
|
|
995
|
+
if (isLoopbackDatabaseHost(host) || isHostRelativeDatabaseHost(host)) {
|
|
996
|
+
return "local";
|
|
997
|
+
}
|
|
998
|
+
if (isSingleLabelName(host) || isLinkLocalIpHost(host)) {
|
|
999
|
+
return "local";
|
|
1000
|
+
}
|
|
1001
|
+
const hasCluster = canonicalClusterName(endpoint.kubernetesClusterName).length > 0;
|
|
1002
|
+
if (!hasCluster && isClusterScopedDatabaseHost(host)) {
|
|
1003
|
+
return "local";
|
|
1004
|
+
}
|
|
1005
|
+
return "global";
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* "host:port", "host" (unknown port), "[v6]:port", "host\instance"; plus
|
|
1009
|
+
* "@cluster" when qualified. The inverse is parseDatabaseEndpointString.
|
|
1010
|
+
* The cluster part is kept verbatim apart from trim + lowercase (EKS names
|
|
1011
|
+
* are ARNs containing ":" and "/").
|
|
1012
|
+
*/
|
|
1013
|
+
export function formatDatabaseEndpoint(endpoint) {
|
|
1014
|
+
const value = splitDatabaseHostInstance(endpoint && typeof endpoint.host === "string"
|
|
1015
|
+
? endpoint.host.trim().toLowerCase()
|
|
1016
|
+
: "");
|
|
1017
|
+
const hostPart = `${value.host.includes(":") ? `[${value.host}]` : value.host}${value.instance ? `\\${value.instance}` : ""}`;
|
|
1018
|
+
const port = toValidPort(endpoint === null || endpoint === void 0 ? void 0 : endpoint.port);
|
|
1019
|
+
const portPart = port !== null ? `:${port}` : "";
|
|
1020
|
+
const clusterName = canonicalClusterName(endpoint === null || endpoint === void 0 ? void 0 : endpoint.kubernetesClusterName);
|
|
1021
|
+
return `${hostPart}${portPart}${clusterName ? `@${clusterName}` : ""}`;
|
|
1022
|
+
}
|
|
1023
|
+
/*
|
|
1024
|
+
* `user@host`, not `host@cluster`: the part after the "@" reads as a server
|
|
1025
|
+
* (it has a port or a dot, or is an IP) while the part before it does not
|
|
1026
|
+
* read as an address that takes a qualifier (one with a port, an IP, a
|
|
1027
|
+
* cluster-local or private-zone name, a `.svc` name) — `admin@10.0.0.5:5432`,
|
|
1028
|
+
* `john.doe@db.example.com`. Cluster names such as `prod-eu`, and ARNs
|
|
1029
|
+
* (which never parse as a host), never look like a server.
|
|
1030
|
+
*/
|
|
1031
|
+
function looksLikeUserInfo(beforeAt, afterAt) {
|
|
1032
|
+
if (!beforeAt) {
|
|
1033
|
+
return false;
|
|
1034
|
+
}
|
|
1035
|
+
const server = parseHostAndPort(afterAt);
|
|
1036
|
+
if (!server ||
|
|
1037
|
+
!(server.port !== null ||
|
|
1038
|
+
server.host.includes(".") ||
|
|
1039
|
+
server.host.includes(":") ||
|
|
1040
|
+
isIpLiteralHost(server.host))) {
|
|
1041
|
+
return false;
|
|
1042
|
+
}
|
|
1043
|
+
// A bare word ("admin") is a user name.
|
|
1044
|
+
if (!NOT_A_BARE_WORD_REGEX.test(beforeAt)) {
|
|
1045
|
+
return true;
|
|
1046
|
+
}
|
|
1047
|
+
const address = parseHostAndPort(beforeAt);
|
|
1048
|
+
if (!address) {
|
|
1049
|
+
return true;
|
|
1050
|
+
}
|
|
1051
|
+
return (address.port === null &&
|
|
1052
|
+
!isIpLiteralHost(address.host) &&
|
|
1053
|
+
!isClusterScopedDatabaseHost(address.host) &&
|
|
1054
|
+
!KUBERNETES_SERVICE_DNS_REGEX.test(address.host));
|
|
1055
|
+
}
|
|
1056
|
+
function interpretDatabaseEndpointString(value, context, strictPort) {
|
|
1057
|
+
var _a, _b;
|
|
1058
|
+
const result = {
|
|
1059
|
+
endpoint: null,
|
|
1060
|
+
problem: null,
|
|
1061
|
+
beforeAt: "",
|
|
1062
|
+
afterAt: "",
|
|
1063
|
+
refusedHost: "",
|
|
1064
|
+
};
|
|
1065
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
1066
|
+
result.problem = "empty";
|
|
1067
|
+
return result;
|
|
1068
|
+
}
|
|
1069
|
+
const trimmed = value.trim();
|
|
1070
|
+
let addressPart = trimmed;
|
|
1071
|
+
let explicitClusterName = "";
|
|
1072
|
+
if (!URL_SCHEME_REGEX.test(trimmed)) {
|
|
1073
|
+
const atIndex = trimmed.lastIndexOf("@");
|
|
1074
|
+
if (atIndex >= 0) {
|
|
1075
|
+
addressPart = trimmed.substring(0, atIndex);
|
|
1076
|
+
result.beforeAt = addressPart.trim();
|
|
1077
|
+
result.afterAt = trimmed.substring(atIndex + 1).trim();
|
|
1078
|
+
explicitClusterName = canonicalClusterName(result.afterAt);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
if (explicitClusterName &&
|
|
1082
|
+
looksLikeUserInfo(result.beforeAt, result.afterAt)) {
|
|
1083
|
+
result.problem = "user-info";
|
|
1084
|
+
return result;
|
|
1085
|
+
}
|
|
1086
|
+
const hosts = parseAddressHosts(addressPart, strictPort);
|
|
1087
|
+
const parsed = hosts && hosts.length > 0 ? hosts[0] : null;
|
|
1088
|
+
if (!parsed) {
|
|
1089
|
+
result.problem =
|
|
1090
|
+
strictPort && parseHostAndPort(addressPart)
|
|
1091
|
+
? "port-out-of-range"
|
|
1092
|
+
: "invalid";
|
|
1093
|
+
return result;
|
|
1094
|
+
}
|
|
1095
|
+
if (isLoopbackDatabaseHost(parsed.host) ||
|
|
1096
|
+
isHostRelativeDatabaseHost(parsed.host)) {
|
|
1097
|
+
result.problem = "loopback";
|
|
1098
|
+
return result;
|
|
1099
|
+
}
|
|
1100
|
+
const knownPort = (_a = toValidPort(context === null || context === void 0 ? void 0 : context.port)) !== null && _a !== void 0 ? _a : parsed.port;
|
|
1101
|
+
const instance = knownPort === null && parsed.instance ? parsed.instance : null;
|
|
1102
|
+
const port = (_b = knownPort !== null && knownPort !== void 0 ? knownPort : (instance ? null : getDefaultDatabasePort(context === null || context === void 0 ? void 0 : context.system))) !== null && _b !== void 0 ? _b : null;
|
|
1103
|
+
const rowClusterName = canonicalClusterName(context === null || context === void 0 ? void 0 : context.kubernetesClusterName);
|
|
1104
|
+
/*
|
|
1105
|
+
* An explicit "@cluster" says the name was seen from inside that
|
|
1106
|
+
* cluster, so it is read the way a pod there reads it.
|
|
1107
|
+
*/
|
|
1108
|
+
const runsInKubernetes = explicitClusterName.length > 0 ||
|
|
1109
|
+
rowClusterName.length > 0 ||
|
|
1110
|
+
isNonBlankText(context === null || context === void 0 ? void 0 : context.kubernetesNamespace);
|
|
1111
|
+
const host = applyKubernetesDnsRules(parsed.host, context === null || context === void 0 ? void 0 : context.kubernetesNamespace, runsInKubernetes);
|
|
1112
|
+
const clusterScoped = isClusterScopedDatabaseHost(host);
|
|
1113
|
+
if (explicitClusterName && !clusterScoped) {
|
|
1114
|
+
result.problem = isSingleLabelName(host)
|
|
1115
|
+
? "qualifier-needs-namespace"
|
|
1116
|
+
: "qualifier-not-allowed";
|
|
1117
|
+
result.refusedHost = host;
|
|
1118
|
+
return result;
|
|
1119
|
+
}
|
|
1120
|
+
result.endpoint = buildEndpoint(joinHostInstance(host, instance), port, clusterScoped ? explicitClusterName || rowClusterName : "");
|
|
1121
|
+
return result;
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* Inverse of formatDatabaseEndpoint, for stored values AND user-typed
|
|
1125
|
+
* aliases. The cluster qualifier is split on the LAST "@" and is only valid
|
|
1126
|
+
* on a host that resolves inside one cluster or network
|
|
1127
|
+
* (isClusterScopedDatabaseHost, after the Kubernetes DNS rules): anything
|
|
1128
|
+
* else with an "@" — `admin@10.0.0.5:5432`, `db.example.com@prod` — is
|
|
1129
|
+
* null, never a silently dropped qualifier or a user name read as a host.
|
|
1130
|
+
* URL forms are not split: their "@" is userinfo. Kubernetes DNS rules use
|
|
1131
|
+
* the row's namespace for a single-label name, and read a two-label name as
|
|
1132
|
+
* `<service>.<namespace>` when the row (or the qualifier) is a Kubernetes
|
|
1133
|
+
* one; an unqualified cluster-scoped host takes the row's cluster, and the
|
|
1134
|
+
* port defaults from the engine. Loopback and host-relative names are null —
|
|
1135
|
+
* an alias must name a server.
|
|
1136
|
+
*/
|
|
1137
|
+
export function parseDatabaseEndpointString(value, context) {
|
|
1138
|
+
return interpretDatabaseEndpointString(value, context, false).endpoint;
|
|
1139
|
+
}
|
|
1140
|
+
function echo(value) {
|
|
1141
|
+
return value.length > MAX_ECHOED_VALUE_LENGTH
|
|
1142
|
+
? `${value.substring(0, MAX_ECHOED_VALUE_LENGTH)}…`
|
|
1143
|
+
: value;
|
|
1144
|
+
}
|
|
1145
|
+
const CLUSTER_QUALIFIER_EXAMPLE = "pg.shop.svc.cluster.local:5432@prod-eu";
|
|
1146
|
+
/**
|
|
1147
|
+
* The endpoint a person typed (the create form of a manually added
|
|
1148
|
+
* database, an alias on its Endpoints tab), with a message that says what
|
|
1149
|
+
* to change when it cannot be used:
|
|
1150
|
+
*
|
|
1151
|
+
* - `user@host` (`admin@10.0.0.5:5432`) is refused — the user name does
|
|
1152
|
+
* not belong in an endpoint;
|
|
1153
|
+
* - `@<cluster>` names the Kubernetes cluster of an address that only
|
|
1154
|
+
* resolves inside one (a cluster-local name, a `<service>.<namespace>`
|
|
1155
|
+
* short name, a private-zone name, a private IP) and is refused on
|
|
1156
|
+
* anything else;
|
|
1157
|
+
* - loopback and host-relative names are refused;
|
|
1158
|
+
* - `port` (the form's own port field) replaces any port in the address,
|
|
1159
|
+
* and must be 1..65535, as must a port typed in the address.
|
|
1160
|
+
*
|
|
1161
|
+
* Accepted values are canonicalized exactly like parseDatabaseEndpointString.
|
|
1162
|
+
* `knownClusterNames` (the project's Kubernetes cluster names) are offered
|
|
1163
|
+
* in the hint for an unqualified cluster-scoped endpoint.
|
|
1164
|
+
*/
|
|
1165
|
+
export function parseManualDatabaseEndpoint(value, context) {
|
|
1166
|
+
const rejected = (error) => {
|
|
1167
|
+
return { endpoint: null, error: error, clusterQualifierHint: null };
|
|
1168
|
+
};
|
|
1169
|
+
const typed = typeof value === "string" ? value.trim() : "";
|
|
1170
|
+
const hasPortField = (context === null || context === void 0 ? void 0 : context.port) !== undefined &&
|
|
1171
|
+
(context === null || context === void 0 ? void 0 : context.port) !== null &&
|
|
1172
|
+
!(typeof context.port === "string" && context.port.trim() === "");
|
|
1173
|
+
let portField = null;
|
|
1174
|
+
if (hasPortField) {
|
|
1175
|
+
portField = toValidPort(context.port);
|
|
1176
|
+
if (portField === null) {
|
|
1177
|
+
return rejected("Server port must be a whole number between 1 and 65535.");
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
const interpreted = interpretDatabaseEndpointString(value, {
|
|
1181
|
+
system: (context === null || context === void 0 ? void 0 : context.system) || "",
|
|
1182
|
+
kubernetesClusterName: context === null || context === void 0 ? void 0 : context.kubernetesClusterName,
|
|
1183
|
+
kubernetesNamespace: context === null || context === void 0 ? void 0 : context.kubernetesNamespace,
|
|
1184
|
+
port: portField,
|
|
1185
|
+
}, !hasPortField);
|
|
1186
|
+
switch (interpreted.problem) {
|
|
1187
|
+
case "empty":
|
|
1188
|
+
return rejected("Server address is required. Enter the host name or IP address applications use to reach this database, for example orders-db.example.com:5432.");
|
|
1189
|
+
case "user-info":
|
|
1190
|
+
return rejected(`"${echo(typed)}" looks like user@host. Remove "${echo(interpreted.beforeAt)}@": the database user does not belong in an endpoint. "@" is only used after an address that resolves inside one Kubernetes cluster, to name that cluster, for example ${CLUSTER_QUALIFIER_EXAMPLE}.`);
|
|
1191
|
+
case "qualifier-needs-namespace":
|
|
1192
|
+
return rejected(`"${echo(interpreted.refusedHost)}" is a single-label name, which only means one Service once its namespace is known. Enter it with the namespace, for example ${echo(interpreted.refusedHost)}.<namespace>@${echo(interpreted.afterAt)}.`);
|
|
1193
|
+
case "qualifier-not-allowed":
|
|
1194
|
+
return rejected(`"${echo(interpreted.refusedHost)}" resolves the same way everywhere, so it cannot take a Kubernetes cluster qualifier. Remove "@${echo(interpreted.afterAt)}". A cluster is only named for an address that resolves inside one cluster or private network: a cluster-local name such as pg.shop.svc.cluster.local, a <service>.<namespace> short name, a .internal or .local name, or a private IP address.`);
|
|
1195
|
+
case "loopback":
|
|
1196
|
+
return rejected(`"${echo(typed)}" is a loopback or host-local address, such as localhost or host.docker.internal. Every application reaches its own, so it cannot identify one database. Enter the host name or IP address the database is reached at from other machines.`);
|
|
1197
|
+
case "port-out-of-range":
|
|
1198
|
+
return rejected(`"${echo(typed)}" has a port outside 1-65535. Enter a port between 1 and 65535.`);
|
|
1199
|
+
case "invalid":
|
|
1200
|
+
return rejected(`"${echo(typed)}" is not a valid host[:port] endpoint. Enter a host name or IP address with an optional port, for example orders-db.example.com:5432. A SQL Server named instance is written host\\instance, and an address that resolves inside one Kubernetes cluster can name that cluster: ${CLUSTER_QUALIFIER_EXAMPLE}.`);
|
|
1201
|
+
default:
|
|
1202
|
+
break;
|
|
1203
|
+
}
|
|
1204
|
+
const endpoint = interpreted.endpoint;
|
|
1205
|
+
if (!endpoint) {
|
|
1206
|
+
return rejected(`"${echo(typed)}" is not a valid host[:port] endpoint.`);
|
|
1207
|
+
}
|
|
1208
|
+
let clusterQualifierHint = null;
|
|
1209
|
+
if (!endpoint.kubernetesClusterName &&
|
|
1210
|
+
isClusterScopedDatabaseHost(endpoint.host)) {
|
|
1211
|
+
const formatted = formatDatabaseEndpoint(endpoint);
|
|
1212
|
+
const clusters = Array.from(new Set((Array.isArray(context === null || context === void 0 ? void 0 : context.knownClusterNames)
|
|
1213
|
+
? context.knownClusterNames
|
|
1214
|
+
: [])
|
|
1215
|
+
.map(canonicalClusterName)
|
|
1216
|
+
.filter((name) => {
|
|
1217
|
+
return name.length > 0;
|
|
1218
|
+
})));
|
|
1219
|
+
clusterQualifierHint = `${formatted} only resolves inside one Kubernetes cluster or private network. Applications that report their cluster (k8s.cluster.name) are matched to ${formatted}@<cluster name> instead, so name the cluster the way they report it${clusters.length > 0
|
|
1220
|
+
? `, for example ${formatted}@${clusters[0]} (this project's clusters: ${clusters.join(", ")})`
|
|
1221
|
+
: ""}.`;
|
|
1222
|
+
}
|
|
1223
|
+
return {
|
|
1224
|
+
endpoint: endpoint,
|
|
1225
|
+
error: null,
|
|
1226
|
+
clusterQualifierHint: clusterQualifierHint,
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
// ---- managed cluster members ---------------------------------------------
|
|
1230
|
+
/**
|
|
1231
|
+
* The cluster a managed-service MEMBER host belongs to, as a host name, or
|
|
1232
|
+
* null for anything else. Drivers of replicated databases report the member
|
|
1233
|
+
* that served each call, so without this every member of one cluster would
|
|
1234
|
+
* become a database of its own. Today: MongoDB Atlas —
|
|
1235
|
+
* `cluster0-shard-00-01.ab1cd.mongodb.net` → `cluster0.ab1cd.mongodb.net`
|
|
1236
|
+
* (the cluster's SRV name). A `\instance` or a port plays no part.
|
|
1237
|
+
*/
|
|
1238
|
+
export function getDatabaseClusterHost(host) {
|
|
1239
|
+
if (typeof host !== "string") {
|
|
1240
|
+
return null;
|
|
1241
|
+
}
|
|
1242
|
+
const value = splitDatabaseHostInstance(host.trim().toLowerCase()).host;
|
|
1243
|
+
const labels = value.split(".");
|
|
1244
|
+
if (labels.length !== 4 ||
|
|
1245
|
+
`${labels[2]}.${labels[3]}` !== ATLAS_DOMAIN ||
|
|
1246
|
+
!DNS_LABEL_REGEX.test(labels[1])) {
|
|
1247
|
+
return null;
|
|
1248
|
+
}
|
|
1249
|
+
const member = labels[0];
|
|
1250
|
+
const marker = member.lastIndexOf(ATLAS_MEMBER_MARKER);
|
|
1251
|
+
if (marker <= 0) {
|
|
1252
|
+
return null;
|
|
1253
|
+
}
|
|
1254
|
+
if (!ATLAS_MEMBER_NUMBER_REGEX.test(member.substring(marker + ATLAS_MEMBER_MARKER.length))) {
|
|
1255
|
+
return null;
|
|
1256
|
+
}
|
|
1257
|
+
const cluster = member.substring(0, marker);
|
|
1258
|
+
return DNS_LABEL_REGEX.test(cluster)
|
|
1259
|
+
? `${cluster}.${labels[1]}.${ATLAS_DOMAIN}`
|
|
1260
|
+
: null;
|
|
1261
|
+
}
|
|
1262
|
+
// ---- row identity + naming -----------------------------------------------
|
|
1263
|
+
/*
|
|
1264
|
+
* Row identifiers start with the engine FAMILY (getDatabaseSystemFamily),
|
|
1265
|
+
* never the engine: a fork keys like the engine it forks ("mariadb" as
|
|
1266
|
+
* "mysql", "valkey" as "redis"). Which of the two a database is gets
|
|
1267
|
+
* decided — and corrected — over its lifetime (a span says "mysql", the
|
|
1268
|
+
* image then says "mariadb", a version string refines "mysql" to "tidb"),
|
|
1269
|
+
* and that must never re-key the row or mint a second one. Engines of
|
|
1270
|
+
* different families still key apart. An unknown engine is its own family.
|
|
1271
|
+
*/
|
|
1272
|
+
function identifierEngine(system) {
|
|
1273
|
+
return getDatabaseSystemFamily(system) || "";
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* `${family}|${formatted endpoint}` — the identifier of an endpoint row,
|
|
1277
|
+
* keyed on the engine family (see identifierEngine).
|
|
1278
|
+
*/
|
|
1279
|
+
export function buildDatabaseServerIdentifier(system, endpoint) {
|
|
1280
|
+
return `${identifierEngine(system)}|${formatDatabaseEndpoint(endpoint)}`;
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* The identifier of a workload-detected row, lowercased and keyed on the
|
|
1284
|
+
* engine family (see identifierEngine), so a workload re-classified within
|
|
1285
|
+
* its family (Redis → Valkey, MySQL → MariaDB) keeps its identifier:
|
|
1286
|
+
* kubernetes → `${family}|kubernetes:${cluster}/${namespace}/${kind}/${name}`
|
|
1287
|
+
* docker / podman → `${family}|${platform}:${host}/${name}` — no kind, so
|
|
1288
|
+
* compose labels that appear later cannot change the identity.
|
|
1289
|
+
*/
|
|
1290
|
+
export function buildWorkloadDatabaseServerIdentifier(input) {
|
|
1291
|
+
const segment = (value) => {
|
|
1292
|
+
return typeof value === "string" ? value.trim() : "";
|
|
1293
|
+
};
|
|
1294
|
+
const system = identifierEngine(input.system);
|
|
1295
|
+
const platform = segment(input.platform);
|
|
1296
|
+
const parentName = segment(input.parentName);
|
|
1297
|
+
const workloadName = segment(input.workloadName);
|
|
1298
|
+
if (platform === "kubernetes") {
|
|
1299
|
+
return `${system}|kubernetes:${parentName}/${segment(input.namespace)}/${segment(input.workloadKind)}/${workloadName}`.toLowerCase();
|
|
1300
|
+
}
|
|
1301
|
+
return `${system}|${platform}:${parentName}/${workloadName}`.toLowerCase();
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* "PostgreSQL db.prod:5432", "Redis cache/redis-master", "MongoDB mongo" —
|
|
1305
|
+
* the engine then the endpoint (no "@cluster") or the workload. At most 100
|
|
1306
|
+
* characters.
|
|
1307
|
+
*/
|
|
1308
|
+
export function buildDatabaseServerDisplayName(input) {
|
|
1309
|
+
const engine = getDatabaseSystemDisplayName(input === null || input === void 0 ? void 0 : input.system);
|
|
1310
|
+
let target = "";
|
|
1311
|
+
if ((input === null || input === void 0 ? void 0 : input.endpoint) && typeof input.endpoint.host === "string") {
|
|
1312
|
+
target = formatDatabaseEndpoint({
|
|
1313
|
+
host: input.endpoint.host,
|
|
1314
|
+
port: input.endpoint.port,
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
else {
|
|
1318
|
+
const workloadName = typeof (input === null || input === void 0 ? void 0 : input.workloadName) === "string" ? input.workloadName.trim() : "";
|
|
1319
|
+
const namespace = typeof (input === null || input === void 0 ? void 0 : input.namespace) === "string" ? input.namespace.trim() : "";
|
|
1320
|
+
if (workloadName) {
|
|
1321
|
+
target = namespace ? `${namespace}/${workloadName}` : workloadName;
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
const name = target ? `${engine} ${target}` : engine;
|
|
1325
|
+
return name.length > DISPLAY_NAME_MAX_LENGTH
|
|
1326
|
+
? name.substring(0, DISPLAY_NAME_MAX_LENGTH).trimEnd()
|
|
1327
|
+
: name;
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* The endpoint aliases of a Kubernetes-detected database, formatted:
|
|
1331
|
+
* - each Service × port: `${svc}.${ns}.svc.cluster.local:${port}@${cluster}`;
|
|
1332
|
+
* - each StatefulSet / operator member behind a headless Service:
|
|
1333
|
+
* `${pod}.${svc}.${ns}.svc.cluster.local:${port}@${cluster}`;
|
|
1334
|
+
* - the unqualified twin of each, ONLY when `includeUnqualified` (the
|
|
1335
|
+
* worker sets it when the project has exactly one Kubernetes cluster, so
|
|
1336
|
+
* an unqualified name cannot belong to anything else).
|
|
1337
|
+
* Ports are the engine default plus the declared container ports. No
|
|
1338
|
+
* two-label `${svc}.${ns}` forms are needed: a pod calling `svc.ns` is
|
|
1339
|
+
* canonicalized to the Service FQDN (applyKubernetesDnsRules), which is
|
|
1340
|
+
* already here. Invalid DNS labels are skipped. Deduped, in a stable order.
|
|
1341
|
+
* With a blank cluster name only the unqualified form can be produced, so
|
|
1342
|
+
* it is.
|
|
1343
|
+
*/
|
|
1344
|
+
export function buildKubernetesDatabaseAliases(input) {
|
|
1345
|
+
if (!input) {
|
|
1346
|
+
return [];
|
|
1347
|
+
}
|
|
1348
|
+
const namespace = canonicalDnsLabel(input.namespace);
|
|
1349
|
+
if (!namespace) {
|
|
1350
|
+
return [];
|
|
1351
|
+
}
|
|
1352
|
+
const clusterName = canonicalClusterName(input.clusterName);
|
|
1353
|
+
const ports = [];
|
|
1354
|
+
const candidatePorts = [
|
|
1355
|
+
getDefaultDatabasePort(input.system),
|
|
1356
|
+
...(Array.isArray(input.ports) ? input.ports : []),
|
|
1357
|
+
];
|
|
1358
|
+
for (const candidate of candidatePorts) {
|
|
1359
|
+
const port = toValidPort(candidate);
|
|
1360
|
+
if (port !== null && !ports.includes(port)) {
|
|
1361
|
+
ports.push(port);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
if (ports.length === 0) {
|
|
1365
|
+
ports.push(null);
|
|
1366
|
+
}
|
|
1367
|
+
const aliases = [];
|
|
1368
|
+
const seen = new Set();
|
|
1369
|
+
const push = (alias) => {
|
|
1370
|
+
if (!seen.has(alias)) {
|
|
1371
|
+
seen.add(alias);
|
|
1372
|
+
aliases.push(alias);
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
const addHost = (host) => {
|
|
1376
|
+
if (!isValidHostname(host)) {
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
for (const port of ports) {
|
|
1380
|
+
if (clusterName) {
|
|
1381
|
+
push(formatDatabaseEndpoint(buildEndpoint(host, port, clusterName)));
|
|
1382
|
+
}
|
|
1383
|
+
if (!clusterName || input.includeUnqualified) {
|
|
1384
|
+
push(formatDatabaseEndpoint(buildEndpoint(host, port, "")));
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
};
|
|
1388
|
+
for (const serviceName of Array.isArray(input.serviceNames)
|
|
1389
|
+
? input.serviceNames
|
|
1390
|
+
: []) {
|
|
1391
|
+
const service = canonicalDnsLabel(serviceName);
|
|
1392
|
+
if (service) {
|
|
1393
|
+
addHost(`${service}.${namespace}${KUBERNETES_CLUSTER_DOMAIN_SUFFIX}`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
for (const member of Array.isArray(input.podServiceNames)
|
|
1397
|
+
? input.podServiceNames
|
|
1398
|
+
: []) {
|
|
1399
|
+
const podName = canonicalDnsLabel(member === null || member === void 0 ? void 0 : member.podName);
|
|
1400
|
+
const serviceName = canonicalDnsLabel(member === null || member === void 0 ? void 0 : member.serviceName);
|
|
1401
|
+
if (podName && serviceName) {
|
|
1402
|
+
addHost(`${podName}.${serviceName}.${namespace}${KUBERNETES_CLUSTER_DOMAIN_SUFFIX}`);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
return aliases;
|
|
1406
|
+
}
|
|
1407
|
+
//# sourceMappingURL=DatabaseEndpoint.js.map
|