@openchoreo/backstage-plugin-openchoreo-observability 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/README.md +22 -0
- package/dist/api/FinOpsAgentApi.esm.js +43 -0
- package/dist/api/FinOpsAgentApi.esm.js.map +1 -0
- package/dist/api/ObservabilityApi.esm.js +568 -0
- package/dist/api/ObservabilityApi.esm.js.map +1 -0
- package/dist/api/ObserverUrlCache.esm.js +59 -0
- package/dist/api/ObserverUrlCache.esm.js.map +1 -0
- package/dist/api/RCAAgentApi.esm.js +104 -0
- package/dist/api/RCAAgentApi.esm.js.map +1 -0
- package/dist/components/Alerts/AlertRow.esm.js +203 -0
- package/dist/components/Alerts/AlertRow.esm.js.map +1 -0
- package/dist/components/Alerts/AlertsActions.esm.js +77 -0
- package/dist/components/Alerts/AlertsActions.esm.js.map +1 -0
- package/dist/components/Alerts/AlertsFilter.esm.js +95 -0
- package/dist/components/Alerts/AlertsFilter.esm.js.map +1 -0
- package/dist/components/Alerts/AlertsTable.esm.js +63 -0
- package/dist/components/Alerts/AlertsTable.esm.js.map +1 -0
- package/dist/components/Alerts/ObservabilityAlertsPage.esm.js +251 -0
- package/dist/components/Alerts/ObservabilityAlertsPage.esm.js.map +1 -0
- package/dist/components/Alerts/types.esm.js +4 -0
- package/dist/components/Alerts/types.esm.js.map +1 -0
- package/dist/components/CostAnalysis/CostAnalysisPage.esm.js +141 -0
- package/dist/components/CostAnalysis/CostAnalysisPage.esm.js.map +1 -0
- package/dist/components/CostAnalysis/CostAnalysisReport.esm.js +128 -0
- package/dist/components/CostAnalysis/CostAnalysisReport.esm.js.map +1 -0
- package/dist/components/CostAnalysis/CostAnalysisReportView.esm.js +517 -0
- package/dist/components/CostAnalysis/CostAnalysisReportView.esm.js.map +1 -0
- package/dist/components/CostAnalysis/CostAnalysisTable.esm.js +149 -0
- package/dist/components/CostAnalysis/CostAnalysisTable.esm.js.map +1 -0
- package/dist/components/CostAnalysis/FinOpsApplyButton.esm.js +112 -0
- package/dist/components/CostAnalysis/FinOpsApplyButton.esm.js.map +1 -0
- package/dist/components/CostAnalysis/index.esm.js +2 -0
- package/dist/components/CostAnalysis/index.esm.js.map +1 -0
- package/dist/components/Incidents/IncidentRow.esm.js +258 -0
- package/dist/components/Incidents/IncidentRow.esm.js.map +1 -0
- package/dist/components/Incidents/IncidentsActions.esm.js +77 -0
- package/dist/components/Incidents/IncidentsActions.esm.js.map +1 -0
- package/dist/components/Incidents/IncidentsFilter.esm.js +144 -0
- package/dist/components/Incidents/IncidentsFilter.esm.js.map +1 -0
- package/dist/components/Incidents/IncidentsTable.esm.js +68 -0
- package/dist/components/Incidents/IncidentsTable.esm.js.map +1 -0
- package/dist/components/Incidents/ObservabilityProjectIncidentsPage.esm.js +283 -0
- package/dist/components/Incidents/ObservabilityProjectIncidentsPage.esm.js.map +1 -0
- package/dist/components/Incidents/types.esm.js +8 -0
- package/dist/components/Incidents/types.esm.js.map +1 -0
- package/dist/components/Metrics/ChartTooltip.esm.js +53 -0
- package/dist/components/Metrics/ChartTooltip.esm.js.map +1 -0
- package/dist/components/Metrics/HTTPMetricsSection.esm.js +109 -0
- package/dist/components/Metrics/HTTPMetricsSection.esm.js.map +1 -0
- package/dist/components/Metrics/MetricGraphByComponent.esm.js +99 -0
- package/dist/components/Metrics/MetricGraphByComponent.esm.js.map +1 -0
- package/dist/components/Metrics/MetricsActions.esm.js +30 -0
- package/dist/components/Metrics/MetricsActions.esm.js.map +1 -0
- package/dist/components/Metrics/MetricsFilters.esm.js +49 -0
- package/dist/components/Metrics/MetricsFilters.esm.js.map +1 -0
- package/dist/components/Metrics/ObservabilityMetricsPage.esm.js +196 -0
- package/dist/components/Metrics/ObservabilityMetricsPage.esm.js.map +1 -0
- package/dist/components/Metrics/styles.esm.js +55 -0
- package/dist/components/Metrics/styles.esm.js.map +1 -0
- package/dist/components/Metrics/utils.esm.js +221 -0
- package/dist/components/Metrics/utils.esm.js.map +1 -0
- package/dist/components/RCA/RCAActions.esm.js +42 -0
- package/dist/components/RCA/RCAActions.esm.js.map +1 -0
- package/dist/components/RCA/RCAFilters.esm.js +100 -0
- package/dist/components/RCA/RCAFilters.esm.js.map +1 -0
- package/dist/components/RCA/RCAPage.esm.js +137 -0
- package/dist/components/RCA/RCAPage.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/EntityLinkContext.esm.js +9 -0
- package/dist/components/RCA/RCAReport/EntityLinkContext.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/FormattedText.esm.js +94 -0
- package/dist/components/RCA/RCAReport/FormattedText.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/RCAReportView.esm.js +308 -0
- package/dist/components/RCA/RCAReport/RCAReportView.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/AssessmentSection.esm.js +113 -0
- package/dist/components/RCA/RCAReport/sections/AssessmentSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/ChatPanelSection.esm.js +272 -0
- package/dist/components/RCA/RCAReport/sections/ChatPanelSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/ExcludedCausesSection.esm.js +48 -0
- package/dist/components/RCA/RCAReport/sections/ExcludedCausesSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/IncidentOverviewSection.esm.js +205 -0
- package/dist/components/RCA/RCAReport/sections/IncidentOverviewSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/InvestigationPathSection.esm.js +121 -0
- package/dist/components/RCA/RCAReport/sections/InvestigationPathSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/PatchTabContent.esm.js +593 -0
- package/dist/components/RCA/RCAReport/sections/PatchTabContent.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/QuickFixesPanelSection.esm.js +35 -0
- package/dist/components/RCA/RCAReport/sections/QuickFixesPanelSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/RecommendationsSection.esm.js +54 -0
- package/dist/components/RCA/RCAReport/sections/RecommendationsSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/RootCausesSection.esm.js +526 -0
- package/dist/components/RCA/RCAReport/sections/RootCausesSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/SystemTimelineSection.esm.js +70 -0
- package/dist/components/RCA/RCAReport/sections/SystemTimelineSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/sections/VisibilityImprovementsSection.esm.js +54 -0
- package/dist/components/RCA/RCAReport/sections/VisibilityImprovementsSection.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport/styles.esm.js +467 -0
- package/dist/components/RCA/RCAReport/styles.esm.js.map +1 -0
- package/dist/components/RCA/RCAReport.esm.js +109 -0
- package/dist/components/RCA/RCAReport.esm.js.map +1 -0
- package/dist/components/RCA/RCATable.esm.js +140 -0
- package/dist/components/RCA/RCATable.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/LogEntry.esm.js +182 -0
- package/dist/components/RuntimeLogs/LogEntry.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/LogsActions.esm.js +106 -0
- package/dist/components/RuntimeLogs/LogsActions.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/LogsFilter.esm.js +200 -0
- package/dist/components/RuntimeLogs/LogsFilter.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/LogsTable.esm.js +91 -0
- package/dist/components/RuntimeLogs/LogsTable.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.esm.js +220 -0
- package/dist/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/ObservabilityRuntimeLogsPage.esm.js +222 -0
- package/dist/components/RuntimeLogs/ObservabilityRuntimeLogsPage.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/styles.esm.js +256 -0
- package/dist/components/RuntimeLogs/styles.esm.js.map +1 -0
- package/dist/components/RuntimeLogs/types.esm.js +16 -0
- package/dist/components/RuntimeLogs/types.esm.js.map +1 -0
- package/dist/components/TimeRangeFilter/TimeRangeFilter.esm.js +280 -0
- package/dist/components/TimeRangeFilter/TimeRangeFilter.esm.js.map +1 -0
- package/dist/components/Traces/ObservabilityTracesPage.esm.js +165 -0
- package/dist/components/Traces/ObservabilityTracesPage.esm.js.map +1 -0
- package/dist/components/Traces/TracesActions.esm.js +37 -0
- package/dist/components/Traces/TracesActions.esm.js.map +1 -0
- package/dist/components/Traces/TracesFilters.esm.js +121 -0
- package/dist/components/Traces/TracesFilters.esm.js.map +1 -0
- package/dist/components/Traces/TracesTable.esm.js +137 -0
- package/dist/components/Traces/TracesTable.esm.js.map +1 -0
- package/dist/components/Traces/WaterfallView.esm.js +567 -0
- package/dist/components/Traces/WaterfallView.esm.js.map +1 -0
- package/dist/components/Traces/styles.esm.js +198 -0
- package/dist/components/Traces/styles.esm.js.map +1 -0
- package/dist/components/Traces/utils.esm.js +19 -0
- package/dist/components/Traces/utils.esm.js.map +1 -0
- package/dist/hooks/useAutoSelectFirstEnvironment.esm.js +25 -0
- package/dist/hooks/useAutoSelectFirstEnvironment.esm.js.map +1 -0
- package/dist/hooks/useComponentAlerts.esm.js +80 -0
- package/dist/hooks/useComponentAlerts.esm.js.map +1 -0
- package/dist/hooks/useDataPlaneNetPolProvider.esm.js +55 -0
- package/dist/hooks/useDataPlaneNetPolProvider.esm.js.map +1 -0
- package/dist/hooks/useDebouncedSearch.esm.js +24 -0
- package/dist/hooks/useDebouncedSearch.esm.js.map +1 -0
- package/dist/hooks/useFilters.esm.js +29 -0
- package/dist/hooks/useFilters.esm.js.map +1 -0
- package/dist/hooks/useFinOpsReport.esm.js +66 -0
- package/dist/hooks/useFinOpsReport.esm.js.map +1 -0
- package/dist/hooks/useFinOpsReports.esm.js +70 -0
- package/dist/hooks/useFinOpsReports.esm.js.map +1 -0
- package/dist/hooks/useGetComponentsByProject.esm.js +57 -0
- package/dist/hooks/useGetComponentsByProject.esm.js.map +1 -0
- package/dist/hooks/useGetEnvironmentsByNamespace.esm.js +53 -0
- package/dist/hooks/useGetEnvironmentsByNamespace.esm.js.map +1 -0
- package/dist/hooks/useGetNamespaceAndProjectByEntity.esm.js +40 -0
- package/dist/hooks/useGetNamespaceAndProjectByEntity.esm.js.map +1 -0
- package/dist/hooks/useMetrics.esm.js +107 -0
- package/dist/hooks/useMetrics.esm.js.map +1 -0
- package/dist/hooks/useProjectIncidents.esm.js +111 -0
- package/dist/hooks/useProjectIncidents.esm.js.map +1 -0
- package/dist/hooks/useProjectRuntimeLogs.esm.js +185 -0
- package/dist/hooks/useProjectRuntimeLogs.esm.js.map +1 -0
- package/dist/hooks/useRCAReportByAlert.esm.js +54 -0
- package/dist/hooks/useRCAReportByAlert.esm.js.map +1 -0
- package/dist/hooks/useRCAReports.esm.js +75 -0
- package/dist/hooks/useRCAReports.esm.js.map +1 -0
- package/dist/hooks/useRuntimeLogs.esm.js +147 -0
- package/dist/hooks/useRuntimeLogs.esm.js.map +1 -0
- package/dist/hooks/useSpanDetails.esm.js +71 -0
- package/dist/hooks/useSpanDetails.esm.js.map +1 -0
- package/dist/hooks/useTraceSpans.esm.js +80 -0
- package/dist/hooks/useTraceSpans.esm.js.map +1 -0
- package/dist/hooks/useTraces.esm.js +112 -0
- package/dist/hooks/useTraces.esm.js.map +1 -0
- package/dist/hooks/useUpdateIncident.esm.js +50 -0
- package/dist/hooks/useUpdateIncident.esm.js.map +1 -0
- package/dist/hooks/useUrlFilters.esm.js +63 -0
- package/dist/hooks/useUrlFilters.esm.js.map +1 -0
- package/dist/hooks/useUrlFiltersForAlerts.esm.js +80 -0
- package/dist/hooks/useUrlFiltersForAlerts.esm.js.map +1 -0
- package/dist/hooks/useUrlFiltersForIncidents.esm.js +87 -0
- package/dist/hooks/useUrlFiltersForIncidents.esm.js.map +1 -0
- package/dist/hooks/useUrlFiltersForRuntimeLogs.esm.js +135 -0
- package/dist/hooks/useUrlFiltersForRuntimeLogs.esm.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.esm.js +2 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/plugin.esm.js +107 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +8 -0
- package/dist/routes.esm.js.map +1 -0
- package/dist/types.esm.js +18 -0
- package/dist/types.esm.js.map +1 -0
- package/dist/utils/applyResourceChange.esm.js +79 -0
- package/dist/utils/applyResourceChange.esm.js.map +1 -0
- package/dist/utils/urlTimeRange.esm.js +52 -0
- package/dist/utils/urlTimeRange.esm.js.map +1 -0
- package/package.json +81 -0
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# openchoreo-observability
|
|
2
|
+
|
|
3
|
+
Welcome to the openchoreo-observability plugin!
|
|
4
|
+
|
|
5
|
+
_This plugin was created through the Backstage CLI_
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Logs**: Component-level and project-level runtime logs with filtering
|
|
10
|
+
- **Metrics**: Resource and HTTP metrics for components
|
|
11
|
+
- **Traces**: Distributed tracing for component requests
|
|
12
|
+
- **Alerts**: Component-level triggered alerts with severity, time range, and environment filters. Alerts can link to the project Incidents tab when incident management is enabled.
|
|
13
|
+
- **Incidents**: Project-level incidents with status, component, and environment filters. Incidents with AI RCA enabled can link to the RCA Reports tab.
|
|
14
|
+
- **RCA Reports**: Root cause analysis reports for incidents
|
|
15
|
+
|
|
16
|
+
## Getting started
|
|
17
|
+
|
|
18
|
+
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/openchoreo-observability](http://localhost:3000/openchoreo-observability).
|
|
19
|
+
|
|
20
|
+
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
|
|
21
|
+
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
|
|
22
|
+
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createApiRef } from '@backstage/core-plugin-api';
|
|
2
|
+
import { ObserverUrlCache } from './ObserverUrlCache.esm.js';
|
|
3
|
+
|
|
4
|
+
const finopsAgentApiRef = createApiRef({
|
|
5
|
+
id: "plugin.openchoreo-finops-agent.service"
|
|
6
|
+
});
|
|
7
|
+
class FinOpsAgentClient {
|
|
8
|
+
fetchApi;
|
|
9
|
+
urlCache;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.fetchApi = options.fetchApi;
|
|
12
|
+
this.urlCache = new ObserverUrlCache(options);
|
|
13
|
+
}
|
|
14
|
+
async updateActionStatuses(reportId, routing, update) {
|
|
15
|
+
const { finopsAgentUrl } = await this.urlCache.resolveUrls(
|
|
16
|
+
routing.namespaceName,
|
|
17
|
+
routing.environmentName
|
|
18
|
+
);
|
|
19
|
+
if (!finopsAgentUrl) {
|
|
20
|
+
throw new Error("FinOps service is not configured");
|
|
21
|
+
}
|
|
22
|
+
const response = await this.fetchApi.fetch(
|
|
23
|
+
`${finopsAgentUrl}/api/v1alpha1/reports/${encodeURIComponent(reportId)}`,
|
|
24
|
+
{
|
|
25
|
+
method: "PUT",
|
|
26
|
+
body: JSON.stringify(update),
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
"x-openchoreo-direct": "true"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
const error = await response.json().catch(() => ({}));
|
|
35
|
+
throw new Error(
|
|
36
|
+
error.detail || `Update report failed: ${response.statusText}`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { FinOpsAgentClient, finopsAgentApiRef };
|
|
43
|
+
//# sourceMappingURL=FinOpsAgentApi.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FinOpsAgentApi.esm.js","sources":["../../src/api/FinOpsAgentApi.ts"],"sourcesContent":["import {\n createApiRef,\n DiscoveryApi,\n FetchApi,\n} from '@backstage/core-plugin-api';\nimport { ObserverUrlCache } from './ObserverUrlCache';\n\nexport interface FinOpsRoutingContext {\n namespaceName: string;\n environmentName: string;\n}\n\nexport interface FinOpsAgentApi {\n updateActionStatuses(\n reportId: string,\n routing: FinOpsRoutingContext,\n update: { appliedIndices?: number[]; dismissedIndices?: number[] },\n ): Promise<void>;\n}\n\nexport const finopsAgentApiRef = createApiRef<FinOpsAgentApi>({\n id: 'plugin.openchoreo-finops-agent.service',\n});\n\nexport class FinOpsAgentClient implements FinOpsAgentApi {\n private readonly fetchApi: FetchApi;\n private readonly urlCache: ObserverUrlCache;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.fetchApi = options.fetchApi;\n this.urlCache = new ObserverUrlCache(options);\n }\n\n async updateActionStatuses(\n reportId: string,\n routing: FinOpsRoutingContext,\n update: { appliedIndices?: number[]; dismissedIndices?: number[] },\n ): Promise<void> {\n const { finopsAgentUrl } = await this.urlCache.resolveUrls(\n routing.namespaceName,\n routing.environmentName,\n );\n\n if (!finopsAgentUrl) {\n throw new Error('FinOps service is not configured');\n }\n\n const response = await this.fetchApi.fetch(\n `${finopsAgentUrl}/api/v1alpha1/reports/${encodeURIComponent(reportId)}`,\n {\n method: 'PUT',\n body: JSON.stringify(update),\n headers: {\n 'Content-Type': 'application/json',\n 'x-openchoreo-direct': 'true',\n },\n },\n );\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.detail || `Update report failed: ${response.statusText}`,\n );\n }\n }\n}\n"],"names":[],"mappings":";;;AAoBO,MAAM,oBAAoB,YAAA,CAA6B;AAAA,EAC5D,EAAA,EAAI;AACN,CAAC;AAEM,MAAM,iBAAA,CAA4C;AAAA,EACtC,QAAA;AAAA,EACA,QAAA;AAAA,EAEjB,YAAY,OAAA,EAA6D;AACvE,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AACxB,IAAA,IAAA,CAAK,QAAA,GAAW,IAAI,gBAAA,CAAiB,OAAO,CAAA;AAAA,EAC9C;AAAA,EAEA,MAAM,oBAAA,CACJ,QAAA,EACA,OAAA,EACA,MAAA,EACe;AACf,IAAA,MAAM,EAAE,cAAA,EAAe,GAAI,MAAM,KAAK,QAAA,CAAS,WAAA;AAAA,MAC7C,OAAA,CAAQ,aAAA;AAAA,MACR,OAAA,CAAQ;AAAA,KACV;AAEA,IAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,MAAA,MAAM,IAAI,MAAM,kCAAkC,CAAA;AAAA,IACpD;AAEA,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,KAAA;AAAA,MACnC,CAAA,EAAG,cAAc,CAAA,sBAAA,EAAyB,kBAAA,CAAmB,QAAQ,CAAC,CAAA,CAAA;AAAA,MACtE;AAAA,QACE,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,MAAM,CAAA;AAAA,QAC3B,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB,kBAAA;AAAA,UAChB,qBAAA,EAAuB;AAAA;AACzB;AACF,KACF;AAEA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,KAAA,GAAQ,MAAM,QAAA,CAAS,IAAA,GAAO,KAAA,CAAM,OAAO,EAAC,CAAE,CAAA;AACpD,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,KAAA,CAAM,MAAA,IAAU,CAAA,sBAAA,EAAyB,QAAA,CAAS,UAAU,CAAA;AAAA,OAC9D;AAAA,IACF;AAAA,EACF;AACF;;;;"}
|
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
import { createApiRef } from '@backstage/core-plugin-api';
|
|
2
|
+
import { ObserverUrlCache } from './ObserverUrlCache.esm.js';
|
|
3
|
+
|
|
4
|
+
const observabilityApiRef = createApiRef({
|
|
5
|
+
id: "plugin.openchoreo-observability.service"
|
|
6
|
+
});
|
|
7
|
+
const DIRECT_HEADER = { "x-openchoreo-direct": "true" };
|
|
8
|
+
class ObservabilityClient {
|
|
9
|
+
fetchApi;
|
|
10
|
+
urlCache;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.fetchApi = options.fetchApi;
|
|
13
|
+
this.urlCache = new ObserverUrlCache(options);
|
|
14
|
+
}
|
|
15
|
+
async getMetrics(environmentName, componentName, namespaceName, projectName, options) {
|
|
16
|
+
const { observerUrl } = await this.urlCache.resolveUrls(
|
|
17
|
+
namespaceName,
|
|
18
|
+
environmentName
|
|
19
|
+
);
|
|
20
|
+
const searchScope = {
|
|
21
|
+
namespace: namespaceName,
|
|
22
|
+
project: projectName,
|
|
23
|
+
component: componentName,
|
|
24
|
+
environment: environmentName
|
|
25
|
+
};
|
|
26
|
+
const baseBody = {
|
|
27
|
+
startTime: options?.startTime ?? new Date(Date.now() - 36e5).toISOString(),
|
|
28
|
+
endTime: options?.endTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
29
|
+
searchScope,
|
|
30
|
+
...options?.step ? { step: options.step } : {}
|
|
31
|
+
};
|
|
32
|
+
const fetchOptions = {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "Content-Type": "application/json", ...DIRECT_HEADER }
|
|
35
|
+
};
|
|
36
|
+
const metricType = options?.type ?? "resource";
|
|
37
|
+
const response = await this.fetchApi.fetch(
|
|
38
|
+
`${observerUrl}/api/v1/metrics/query`,
|
|
39
|
+
{
|
|
40
|
+
...fetchOptions,
|
|
41
|
+
body: JSON.stringify({ ...baseBody, metric: metricType })
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
const error = await this.parseError(response);
|
|
46
|
+
if (error.includes("Observability is not configured for component")) {
|
|
47
|
+
throw new Error("Observability is not enabled for this component");
|
|
48
|
+
}
|
|
49
|
+
throw new Error(
|
|
50
|
+
error || `Failed to fetch metrics: ${response.statusText}`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const data = await response.json();
|
|
54
|
+
switch (metricType) {
|
|
55
|
+
case "resource":
|
|
56
|
+
return {
|
|
57
|
+
cpuUsage: {
|
|
58
|
+
cpuUsage: data.cpuUsage ?? [],
|
|
59
|
+
cpuRequests: data.cpuRequests ?? [],
|
|
60
|
+
cpuLimits: data.cpuLimits ?? []
|
|
61
|
+
},
|
|
62
|
+
memoryUsage: {
|
|
63
|
+
memoryUsage: data.memoryUsage ?? [],
|
|
64
|
+
memoryRequests: data.memoryRequests ?? [],
|
|
65
|
+
memoryLimits: data.memoryLimits ?? []
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
case "http":
|
|
69
|
+
return {
|
|
70
|
+
networkThroughput: {
|
|
71
|
+
requestCount: data.requestCount ?? [],
|
|
72
|
+
successfulRequestCount: data.successfulRequestCount ?? [],
|
|
73
|
+
unsuccessfulRequestCount: data.unsuccessfulRequestCount ?? []
|
|
74
|
+
},
|
|
75
|
+
networkLatency: {
|
|
76
|
+
meanLatency: data.meanLatency ?? [],
|
|
77
|
+
latencyP50: data.latencyP50 ?? [],
|
|
78
|
+
latencyP90: data.latencyP90 ?? [],
|
|
79
|
+
latencyP99: data.latencyP99 ?? []
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
default:
|
|
83
|
+
throw new Error(`Unsupported metric type: ${metricType}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async getTraces(namespaceName, projectName, environmentName, componentName, options) {
|
|
87
|
+
const { observerUrl } = await this.urlCache.resolveUrls(
|
|
88
|
+
namespaceName,
|
|
89
|
+
environmentName
|
|
90
|
+
);
|
|
91
|
+
const response = await this.fetchApi.fetch(
|
|
92
|
+
`${observerUrl}/api/v1alpha1/traces/query`,
|
|
93
|
+
{
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: { "Content-Type": "application/json", ...DIRECT_HEADER },
|
|
96
|
+
body: JSON.stringify({
|
|
97
|
+
startTime: options?.startTime ?? new Date(Date.now() - 36e5).toISOString(),
|
|
98
|
+
endTime: options?.endTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
99
|
+
limit: options?.limit ?? 100,
|
|
100
|
+
sortOrder: options?.sortOrder ?? "desc",
|
|
101
|
+
searchScope: {
|
|
102
|
+
namespace: namespaceName,
|
|
103
|
+
project: projectName,
|
|
104
|
+
...componentName ? { component: componentName } : {},
|
|
105
|
+
environment: environmentName
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
if (!response.ok) {
|
|
111
|
+
const error = await this.parseError(response);
|
|
112
|
+
if (error.includes("Observability is not configured for component")) {
|
|
113
|
+
throw new Error("Observability is not enabled for this component");
|
|
114
|
+
}
|
|
115
|
+
throw new Error(
|
|
116
|
+
error || `Failed to fetch traces: ${response.statusText}`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const data = await response.json();
|
|
120
|
+
return {
|
|
121
|
+
traces: (data.traces ?? []).map((t) => ({
|
|
122
|
+
traceId: t.traceId ?? "",
|
|
123
|
+
traceName: t.traceName,
|
|
124
|
+
spanCount: t.spanCount ?? 0,
|
|
125
|
+
rootSpanId: t.rootSpanId,
|
|
126
|
+
rootSpanName: t.rootSpanName,
|
|
127
|
+
rootSpanKind: t.rootSpanKind,
|
|
128
|
+
startTime: t.startTime ?? "",
|
|
129
|
+
endTime: t.endTime ?? "",
|
|
130
|
+
durationNs: t.durationNs ?? 0,
|
|
131
|
+
hasErrors: t.hasErrors ?? false
|
|
132
|
+
})),
|
|
133
|
+
total: data.total ?? 0,
|
|
134
|
+
tookMs: data.tookMs ?? 0
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
async getTraceSpans(traceId, namespaceName, projectName, environmentName, componentName, options) {
|
|
138
|
+
const { observerUrl } = await this.urlCache.resolveUrls(
|
|
139
|
+
namespaceName,
|
|
140
|
+
environmentName
|
|
141
|
+
);
|
|
142
|
+
const response = await this.fetchApi.fetch(
|
|
143
|
+
`${observerUrl}/api/v1alpha1/traces/${encodeURIComponent(
|
|
144
|
+
traceId
|
|
145
|
+
)}/spans/query`,
|
|
146
|
+
{
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: { "Content-Type": "application/json", ...DIRECT_HEADER },
|
|
149
|
+
body: JSON.stringify({
|
|
150
|
+
startTime: options?.startTime ?? new Date(Date.now() - 36e5).toISOString(),
|
|
151
|
+
endTime: options?.endTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
152
|
+
limit: 1e3,
|
|
153
|
+
sortOrder: "asc",
|
|
154
|
+
searchScope: {
|
|
155
|
+
namespace: namespaceName,
|
|
156
|
+
project: projectName,
|
|
157
|
+
...componentName ? { component: componentName } : {},
|
|
158
|
+
environment: environmentName
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
const error = await this.parseError(response);
|
|
165
|
+
throw new Error(
|
|
166
|
+
error || `Failed to fetch spans for trace ${traceId}: ${response.statusText}`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
const data = await response.json();
|
|
170
|
+
return {
|
|
171
|
+
spans: (data.spans ?? []).map((s) => ({
|
|
172
|
+
spanId: s.spanId ?? "",
|
|
173
|
+
spanName: s.spanName ?? "",
|
|
174
|
+
spanKind: s.spanKind,
|
|
175
|
+
startTime: s.startTime ?? "",
|
|
176
|
+
endTime: s.endTime ?? "",
|
|
177
|
+
durationNs: s.durationNs ?? 0,
|
|
178
|
+
parentSpanId: s.parentSpanId,
|
|
179
|
+
status: s.status
|
|
180
|
+
})),
|
|
181
|
+
total: data.total ?? 0,
|
|
182
|
+
tookMs: data.tookMs ?? 0
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async getSpanDetails(traceId, spanId, namespaceName, environmentName) {
|
|
186
|
+
const { observerUrl } = await this.urlCache.resolveUrls(
|
|
187
|
+
namespaceName,
|
|
188
|
+
environmentName
|
|
189
|
+
);
|
|
190
|
+
const response = await this.fetchApi.fetch(
|
|
191
|
+
`${observerUrl}/api/v1alpha1/traces/${encodeURIComponent(
|
|
192
|
+
traceId
|
|
193
|
+
)}/spans/${encodeURIComponent(spanId)}`,
|
|
194
|
+
{
|
|
195
|
+
headers: { ...DIRECT_HEADER }
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
if (!response.ok) {
|
|
199
|
+
const error = await this.parseError(response);
|
|
200
|
+
throw new Error(
|
|
201
|
+
error || `Failed to fetch span details for span ${spanId}: ${response.statusText}`
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
const data = await response.json();
|
|
205
|
+
return {
|
|
206
|
+
spanId: data.spanId ?? "",
|
|
207
|
+
spanName: data.spanName ?? "",
|
|
208
|
+
spanKind: data.spanKind,
|
|
209
|
+
startTime: data.startTime ?? "",
|
|
210
|
+
endTime: data.endTime ?? "",
|
|
211
|
+
durationNs: data.durationNs ?? 0,
|
|
212
|
+
parentSpanId: data.parentSpanId,
|
|
213
|
+
attributes: data.attributes,
|
|
214
|
+
resourceAttributes: data.resourceAttributes
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
async getRCAReports(namespaceName, projectName, environmentName, options) {
|
|
218
|
+
const { rcaAgentUrl } = await this.urlCache.resolveUrls(
|
|
219
|
+
namespaceName,
|
|
220
|
+
environmentName
|
|
221
|
+
);
|
|
222
|
+
if (!rcaAgentUrl) {
|
|
223
|
+
throw new Error("RCA service is not configured");
|
|
224
|
+
}
|
|
225
|
+
const url = new URL(`${rcaAgentUrl}/api/v1/rca-agent/reports`);
|
|
226
|
+
url.searchParams.set("namespace", namespaceName);
|
|
227
|
+
url.searchParams.set("project", projectName);
|
|
228
|
+
url.searchParams.set("environment", environmentName);
|
|
229
|
+
if (options?.startTime)
|
|
230
|
+
url.searchParams.set("startTime", options.startTime);
|
|
231
|
+
if (options?.endTime) url.searchParams.set("endTime", options.endTime);
|
|
232
|
+
if (options?.status) url.searchParams.set("status", options.status);
|
|
233
|
+
if (options?.limit !== void 0)
|
|
234
|
+
url.searchParams.set("limit", String(options.limit));
|
|
235
|
+
let response;
|
|
236
|
+
try {
|
|
237
|
+
response = await this.fetchApi.fetch(url.toString(), {
|
|
238
|
+
headers: { ...DIRECT_HEADER }
|
|
239
|
+
});
|
|
240
|
+
} catch (err) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
`RCA service is unreachable: ${err instanceof Error ? err.message : err}`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
if (!response.ok) {
|
|
246
|
+
const error = await this.parseError(response);
|
|
247
|
+
if (error.includes("RCA service is not configured")) {
|
|
248
|
+
throw new Error("RCA service is not configured");
|
|
249
|
+
}
|
|
250
|
+
if (error.includes("Observability is not configured for component")) {
|
|
251
|
+
throw new Error("Observability is not enabled for this component");
|
|
252
|
+
}
|
|
253
|
+
throw new Error(
|
|
254
|
+
error || `Failed to fetch RCA reports: ${response.statusText}`
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
const data = await response.json();
|
|
258
|
+
return {
|
|
259
|
+
reports: data.reports || [],
|
|
260
|
+
totalCount: data.totalCount
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
async getRCAReport(reportId, environmentName, namespaceName) {
|
|
264
|
+
const { rcaAgentUrl } = await this.urlCache.resolveUrls(
|
|
265
|
+
namespaceName,
|
|
266
|
+
environmentName
|
|
267
|
+
);
|
|
268
|
+
if (!rcaAgentUrl) {
|
|
269
|
+
throw new Error("RCA service is not configured");
|
|
270
|
+
}
|
|
271
|
+
const url = new URL(
|
|
272
|
+
`${rcaAgentUrl}/api/v1/rca-agent/reports/${encodeURIComponent(reportId)}`
|
|
273
|
+
);
|
|
274
|
+
let response;
|
|
275
|
+
try {
|
|
276
|
+
response = await this.fetchApi.fetch(url.toString(), {
|
|
277
|
+
headers: { ...DIRECT_HEADER }
|
|
278
|
+
});
|
|
279
|
+
} catch (err) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`RCA service is unreachable: ${err instanceof Error ? err.message : err}`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (!response.ok) {
|
|
285
|
+
const error = await this.parseError(response);
|
|
286
|
+
if (error.includes("RCA service is not configured")) {
|
|
287
|
+
throw new Error("RCA service is not configured");
|
|
288
|
+
}
|
|
289
|
+
if (error.includes("RCA report not found")) {
|
|
290
|
+
throw new Error("RCA report not found");
|
|
291
|
+
}
|
|
292
|
+
throw new Error(
|
|
293
|
+
error || `Failed to fetch RCA report: ${response.statusText}`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
const data = await response.json();
|
|
297
|
+
return data;
|
|
298
|
+
}
|
|
299
|
+
async getIncidents(namespaceName, projectName, environmentName, componentName, options) {
|
|
300
|
+
const { observerUrl } = await this.urlCache.resolveUrls(
|
|
301
|
+
namespaceName,
|
|
302
|
+
environmentName
|
|
303
|
+
);
|
|
304
|
+
const response = await this.fetchApi.fetch(
|
|
305
|
+
`${observerUrl}/api/v1alpha1/incidents/query`,
|
|
306
|
+
{
|
|
307
|
+
method: "POST",
|
|
308
|
+
headers: { "Content-Type": "application/json", ...DIRECT_HEADER },
|
|
309
|
+
body: JSON.stringify({
|
|
310
|
+
startTime: options?.startTime ?? new Date(Date.now() - 36e5).toISOString(),
|
|
311
|
+
endTime: options?.endTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
312
|
+
limit: options?.limit ?? 100,
|
|
313
|
+
sortOrder: options?.sortOrder ?? "desc",
|
|
314
|
+
searchScope: {
|
|
315
|
+
namespace: namespaceName,
|
|
316
|
+
project: projectName,
|
|
317
|
+
...componentName ? { component: componentName } : {},
|
|
318
|
+
environment: environmentName
|
|
319
|
+
}
|
|
320
|
+
})
|
|
321
|
+
}
|
|
322
|
+
);
|
|
323
|
+
if (!response.ok) {
|
|
324
|
+
const error = await this.parseError(response);
|
|
325
|
+
if (error.includes("Observability is not configured for component")) {
|
|
326
|
+
throw new Error("Observability is not enabled for this component");
|
|
327
|
+
}
|
|
328
|
+
throw new Error(
|
|
329
|
+
error || `Failed to fetch incidents: ${response.statusText}`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
const data = await response.json();
|
|
333
|
+
const validStatuses = ["active", "acknowledged", "resolved"];
|
|
334
|
+
return {
|
|
335
|
+
incidents: (data.incidents ?? []).filter((i) => validStatuses.includes(i.status)).map(
|
|
336
|
+
(i) => ({
|
|
337
|
+
incidentId: i.incidentId ?? "",
|
|
338
|
+
alertId: i.alertId ?? "",
|
|
339
|
+
status: i.status,
|
|
340
|
+
description: i.description,
|
|
341
|
+
notes: i.notes,
|
|
342
|
+
timestamp: i.timestamp,
|
|
343
|
+
triggeredAt: i.triggeredAt,
|
|
344
|
+
acknowledgedAt: i.acknowledgedAt,
|
|
345
|
+
resolvedAt: i.resolvedAt,
|
|
346
|
+
incidentTriggerAiRca: i.incidentTriggerAiRca ?? false,
|
|
347
|
+
incidentTriggerAiCostAnalysis: i.incidentTriggerAiCostAnalysis ?? false,
|
|
348
|
+
projectName: i.labels?.projectName,
|
|
349
|
+
componentName: i.labels?.componentName,
|
|
350
|
+
environmentName: i.labels?.environmentName,
|
|
351
|
+
namespaceName: i.labels?.namespaceName
|
|
352
|
+
})
|
|
353
|
+
),
|
|
354
|
+
total: data.total ?? 0
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
async getAlerts(namespaceName, projectName, environmentName, componentName, options) {
|
|
358
|
+
const { observerUrl } = await this.urlCache.resolveUrls(
|
|
359
|
+
namespaceName,
|
|
360
|
+
environmentName
|
|
361
|
+
);
|
|
362
|
+
const response = await this.fetchApi.fetch(
|
|
363
|
+
`${observerUrl}/api/v1alpha1/alerts/query`,
|
|
364
|
+
{
|
|
365
|
+
method: "POST",
|
|
366
|
+
headers: { "Content-Type": "application/json", ...DIRECT_HEADER },
|
|
367
|
+
body: JSON.stringify({
|
|
368
|
+
startTime: options?.startTime ?? new Date(Date.now() - 36e5).toISOString(),
|
|
369
|
+
endTime: options?.endTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
370
|
+
limit: options?.limit ?? 100,
|
|
371
|
+
sortOrder: options?.sortOrder ?? "desc",
|
|
372
|
+
searchScope: {
|
|
373
|
+
namespace: namespaceName,
|
|
374
|
+
project: projectName,
|
|
375
|
+
component: componentName,
|
|
376
|
+
environment: environmentName
|
|
377
|
+
}
|
|
378
|
+
})
|
|
379
|
+
}
|
|
380
|
+
);
|
|
381
|
+
if (!response.ok) {
|
|
382
|
+
const error = await this.parseError(response);
|
|
383
|
+
if (error.includes("Observability is not configured for component")) {
|
|
384
|
+
throw new Error("Observability is not enabled for this component");
|
|
385
|
+
}
|
|
386
|
+
throw new Error(
|
|
387
|
+
error || `Failed to fetch alerts: ${response.statusText}`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
const data = await response.json();
|
|
391
|
+
return {
|
|
392
|
+
alerts: (data.alerts ?? []).map(
|
|
393
|
+
(a) => ({
|
|
394
|
+
timestamp: a.timestamp,
|
|
395
|
+
alertId: a.alertId ?? "",
|
|
396
|
+
alertValue: a.alertValue,
|
|
397
|
+
ruleName: a.metadata?.alertRule?.name,
|
|
398
|
+
ruleDescription: a.metadata?.alertRule?.description,
|
|
399
|
+
severity: a.metadata?.alertRule?.severity,
|
|
400
|
+
sourceType: a.metadata?.alertRule?.source?.type,
|
|
401
|
+
sourceQuery: a.metadata?.alertRule?.source?.query,
|
|
402
|
+
sourceMetric: a.metadata?.alertRule?.source?.metric,
|
|
403
|
+
projectName: a.metadata?.labels?.projectName,
|
|
404
|
+
componentName: a.metadata?.labels?.componentName,
|
|
405
|
+
environmentName: a.metadata?.labels?.environmentName,
|
|
406
|
+
namespaceName: a.metadata?.labels?.namespaceName,
|
|
407
|
+
notificationChannels: a.notificationChannels ?? [],
|
|
408
|
+
incidentEnabled: a.incidentEnabled ?? false
|
|
409
|
+
})
|
|
410
|
+
),
|
|
411
|
+
total: data.total ?? 0
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
async getRuntimeLogs(namespaceName, projectName, environmentName, componentName, options) {
|
|
415
|
+
const { observerUrl } = await this.urlCache.resolveUrls(
|
|
416
|
+
namespaceName,
|
|
417
|
+
environmentName
|
|
418
|
+
);
|
|
419
|
+
const response = await this.fetchApi.fetch(
|
|
420
|
+
`${observerUrl}/api/v1/logs/query`,
|
|
421
|
+
{
|
|
422
|
+
method: "POST",
|
|
423
|
+
headers: { "Content-Type": "application/json", ...DIRECT_HEADER },
|
|
424
|
+
body: JSON.stringify({
|
|
425
|
+
startTime: options?.startTime || new Date(Date.now() - 60 * 60 * 1e3).toISOString(),
|
|
426
|
+
endTime: options?.endTime || (/* @__PURE__ */ new Date()).toISOString(),
|
|
427
|
+
limit: options?.limit || 100,
|
|
428
|
+
sortOrder: options?.sortOrder || "desc",
|
|
429
|
+
...options?.logLevels && options.logLevels.length > 0 && { logLevels: options.logLevels },
|
|
430
|
+
...options?.searchQuery && {
|
|
431
|
+
searchPhrase: options.searchQuery
|
|
432
|
+
},
|
|
433
|
+
searchScope: {
|
|
434
|
+
namespace: namespaceName,
|
|
435
|
+
project: projectName,
|
|
436
|
+
...componentName ? { component: componentName } : {},
|
|
437
|
+
environment: environmentName
|
|
438
|
+
}
|
|
439
|
+
})
|
|
440
|
+
}
|
|
441
|
+
);
|
|
442
|
+
if (!response.ok) {
|
|
443
|
+
const error = await this.parseError(response);
|
|
444
|
+
if (error.includes("Observability is not configured for component")) {
|
|
445
|
+
throw new Error("Observability is not enabled for this component");
|
|
446
|
+
}
|
|
447
|
+
throw new Error(
|
|
448
|
+
error || `Failed to fetch runtime logs: ${response.status} ${response.statusText}`
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
const data = await response.json();
|
|
452
|
+
return data;
|
|
453
|
+
}
|
|
454
|
+
async updateIncidentStatus(incidentId, status, namespaceName, environmentName) {
|
|
455
|
+
const { observerUrl } = await this.urlCache.resolveUrls(
|
|
456
|
+
namespaceName,
|
|
457
|
+
environmentName
|
|
458
|
+
);
|
|
459
|
+
const response = await this.fetchApi.fetch(
|
|
460
|
+
`${observerUrl}/api/v1alpha1/incidents/${encodeURIComponent(incidentId)}`,
|
|
461
|
+
{
|
|
462
|
+
method: "PUT",
|
|
463
|
+
headers: { "Content-Type": "application/json", ...DIRECT_HEADER },
|
|
464
|
+
body: JSON.stringify({ status })
|
|
465
|
+
}
|
|
466
|
+
);
|
|
467
|
+
if (!response.ok) {
|
|
468
|
+
const error = await this.parseError(response);
|
|
469
|
+
throw new Error(
|
|
470
|
+
error || `Failed to update incident: ${response.statusText}`
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
async getFinOpsReports(namespaceName, projectName, environmentName, options) {
|
|
475
|
+
const { finopsAgentUrl } = await this.urlCache.resolveUrls(
|
|
476
|
+
namespaceName,
|
|
477
|
+
environmentName
|
|
478
|
+
);
|
|
479
|
+
if (!finopsAgentUrl) {
|
|
480
|
+
throw new Error("FinOps service is not configured");
|
|
481
|
+
}
|
|
482
|
+
const url = new URL(`${finopsAgentUrl}/api/v1alpha1/reports`);
|
|
483
|
+
url.searchParams.set("namespace", namespaceName);
|
|
484
|
+
url.searchParams.set("project", projectName);
|
|
485
|
+
url.searchParams.set("environment", environmentName);
|
|
486
|
+
if (options?.startTime)
|
|
487
|
+
url.searchParams.set("startTime", options.startTime);
|
|
488
|
+
if (options?.endTime) url.searchParams.set("endTime", options.endTime);
|
|
489
|
+
if (options?.status) url.searchParams.set("status", options.status);
|
|
490
|
+
if (options?.limit !== void 0)
|
|
491
|
+
url.searchParams.set("limit", String(options.limit));
|
|
492
|
+
if (options?.sort) url.searchParams.set("sort", options.sort);
|
|
493
|
+
let response;
|
|
494
|
+
try {
|
|
495
|
+
response = await this.fetchApi.fetch(url.toString(), {
|
|
496
|
+
headers: { ...DIRECT_HEADER }
|
|
497
|
+
});
|
|
498
|
+
} catch (err) {
|
|
499
|
+
throw new Error(
|
|
500
|
+
`FinOps service is unreachable: ${err instanceof Error ? err.message : err}`
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
if (!response.ok) {
|
|
504
|
+
const error = await this.parseError(response);
|
|
505
|
+
if (error.includes("FinOps service is not configured")) {
|
|
506
|
+
throw new Error("FinOps service is not configured");
|
|
507
|
+
}
|
|
508
|
+
if (error.includes("Observability is not configured for component")) {
|
|
509
|
+
throw new Error("Observability is not enabled for this component");
|
|
510
|
+
}
|
|
511
|
+
throw new Error(
|
|
512
|
+
error || `Failed to fetch FinOps reports: ${response.statusText}`
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
const data = await response.json();
|
|
516
|
+
return {
|
|
517
|
+
reports: data.reports || [],
|
|
518
|
+
totalCount: data.totalCount
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
async getFinOpsReport(reportId, environmentName, namespaceName) {
|
|
522
|
+
const { finopsAgentUrl } = await this.urlCache.resolveUrls(
|
|
523
|
+
namespaceName,
|
|
524
|
+
environmentName
|
|
525
|
+
);
|
|
526
|
+
if (!finopsAgentUrl) {
|
|
527
|
+
throw new Error("FinOps service is not configured");
|
|
528
|
+
}
|
|
529
|
+
const url = new URL(
|
|
530
|
+
`${finopsAgentUrl}/api/v1alpha1/reports/${encodeURIComponent(reportId)}`
|
|
531
|
+
);
|
|
532
|
+
let response;
|
|
533
|
+
try {
|
|
534
|
+
response = await this.fetchApi.fetch(url.toString(), {
|
|
535
|
+
headers: { ...DIRECT_HEADER }
|
|
536
|
+
});
|
|
537
|
+
} catch (err) {
|
|
538
|
+
throw new Error(
|
|
539
|
+
`FinOps service is unreachable: ${err instanceof Error ? err.message : err}`
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
if (!response.ok) {
|
|
543
|
+
const error = await this.parseError(response);
|
|
544
|
+
if (error.includes("FinOps service is not configured")) {
|
|
545
|
+
throw new Error("FinOps service is not configured");
|
|
546
|
+
}
|
|
547
|
+
if (error.includes("FinOps report not found")) {
|
|
548
|
+
throw new Error("FinOps report not found");
|
|
549
|
+
}
|
|
550
|
+
throw new Error(
|
|
551
|
+
error || `Failed to fetch FinOps report: ${response.statusText}`
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
const data = await response.json();
|
|
555
|
+
return data;
|
|
556
|
+
}
|
|
557
|
+
async parseError(response) {
|
|
558
|
+
try {
|
|
559
|
+
const error = await response.json();
|
|
560
|
+
return error.error || error.message || `${response.status} ${response.statusText}`;
|
|
561
|
+
} catch {
|
|
562
|
+
return `${response.status} ${response.statusText}`;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export { ObservabilityClient, observabilityApiRef };
|
|
568
|
+
//# sourceMappingURL=ObservabilityApi.esm.js.map
|