From 231a03c0875a2481e7b7737d142ca94ac18406e6 Mon Sep 17 00:00:00 2001 From: Jenny Date: Tue, 17 Jun 2025 19:00:48 +0200 Subject: [PATCH 1/4] wip get otel / ecs data and onboarding link --- .../server/routes/internal/setup/route.ts | 58 +++++++++++++++++++ .../observability_navigation/server/types.ts | 6 +- .../add_kubernetes_data.tsx | 37 ++++++++++++ .../components/dashboard/render_dashboard.tsx | 13 +++++ 4 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/add_kubernetes_data/add_kubernetes_data.tsx diff --git a/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts b/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts index 5fc8eab5b0e4d..af469652c502e 100644 --- a/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts +++ b/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts @@ -12,6 +12,7 @@ import { createServerRoute } from '../../../create_server_route'; const KUBERNETES = 'kubernetes'; const DOCKER = 'docker'; +const METRICBEAT = 'metricbeat'; const infraSideNavRoute = createServerRoute({ endpoint: 'GET /internal/observability_navigation', @@ -45,6 +46,63 @@ const infraSideNavRoute = createServerRoute({ ), ]); + const metricbeatData = await core.elasticsearch.client.asCurrentUser.search({ + index: 'metrics-kubernetes*', + ignore_unavailable: true, + allow_no_indices: true, + track_total_hits: true, + terminate_after: 1, + size: 0, + query: { + bool: { + should: [ + { term: { ['event.module']: KUBERNETES } }, + { term: { ['agent.type']: METRICBEAT } }, + ], + minimum_should_match: 1, + }, + }, + }); + + const otelData = await core.elasticsearch.client.asCurrentUser.search({ + index: 'metrics-*.otel-*', + ignore_unavailable: true, + allow_no_indices: true, + track_total_hits: true, + terminate_after: 1, + size: 0, + query: { + bool: { + filter: [{ term: { ['data_stream.dataset']: '*.otel' } }], + }, + }, + }); + + console.log('hasEcsData:', metricbeatData?.hits?.total?.value !== 0); + console.log('hasOtelData:', otelData?.hits?.total?.value !== 0); + + // TODO + const hasEcsData = metricbeatData?.hits?.total?.value !== 0; + const hasOtelData = otelData?.hits?.total?.value !== 0; + + // Maybe separate the ecs / otel cases in the future + // if ((hasEcsData || hasOtelData) && !installedPackage ) { + // return [ + // { + // id: KUBERNETES, + // title: KUBERNETES, + // subItems: [ + // { + // id: 'kubernetes', + // sideNavTitle: 'Add Kubernetes data', + // sideNavOrder: 100, + // type: 'dashboard', + // }, + // ], + // }, + // ]; + // } + if (!installedPackage && !navigationOverrides) { return []; } diff --git a/x-pack/platform/plugins/shared/observability_navigation/server/types.ts b/x-pack/platform/plugins/shared/observability_navigation/server/types.ts index 0347f458047e9..7fcc135268364 100644 --- a/x-pack/platform/plugins/shared/observability_navigation/server/types.ts +++ b/x-pack/platform/plugins/shared/observability_navigation/server/types.ts @@ -16,8 +16,10 @@ export interface ObservabilityNavigationServer { // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ObservabilityNavigationPluginSetup {} -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ObservabilityNavigationPluginStart {} + +export interface ObservabilityNavigationPluginStart { + core: CoreStart; +} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ObservabilityNavigationPluginSetupDependencies {} diff --git a/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/add_kubernetes_data/add_kubernetes_data.tsx b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/add_kubernetes_data/add_kubernetes_data.tsx new file mode 100644 index 0000000000000..0c0afc440a61d --- /dev/null +++ b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/add_kubernetes_data/add_kubernetes_data.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiLink } from '@elastic/eui'; +import type { ObservabilityOnboardingLocatorParams } from '@kbn/deeplinks-observability'; +import { OBSERVABILITY_ONBOARDING_LOCATOR } from '@kbn/deeplinks-observability'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import type { SharePublicStart } from '@kbn/share-plugin/public/plugin'; +import { i18n } from '@kbn/i18n'; + +const ADD_DATA_KUBERNETES_LABEL = i18n.translate('xpack.infra.metricsHeaderAddDataButtonLabel', { + defaultMessage: 'Add Kubernetes data', +}); + +export const AddKubernetesDataLink = () => { + const { share } = useKibana<{ share: SharePublicStart }>().services; + const onboardingLocator = share?.url.locators.get( + OBSERVABILITY_ONBOARDING_LOCATOR + ); + + return ( + + {ADD_DATA_KUBERNETES_LABEL} + + ); +}; diff --git a/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/dashboard/render_dashboard.tsx b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/dashboard/render_dashboard.tsx index 27cfe0985bf3d..4218e7cdfc32b 100644 --- a/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/dashboard/render_dashboard.tsx +++ b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/dashboard/render_dashboard.tsx @@ -12,8 +12,12 @@ import type { DashboardApi, DashboardCreationOptions } from '@kbn/dashboard-plug import { KUBERNETES_DASHBOARD_LOCATOR_ID } from '@kbn/observability-shared-plugin/common'; import type { SerializableRecord } from '@kbn/utility-types'; import type { DashboardState } from '@kbn/dashboard-plugin/common'; +import { EuiLoadingSpinner } from '@elastic/eui'; +import { FETCH_STATUS } from '@kbn/observability-shared-plugin/public'; import { useKibanaContextForPlugin } from '../../../../../hooks/use_kibana'; import { useDatePickerContext } from '../../hooks/use_date_picker'; +import { AddKubernetesDataLink } from '../add_kubernetes_data/add_kubernetes_data'; +import { useFetchDashboardById } from '../../hooks/use_fetch_dashboard_by_id'; export const RenderDashboard = ({ dashboardId }: { dashboardId: string }) => { const { @@ -61,6 +65,15 @@ export const RenderDashboard = ({ dashboardId }: { dashboardId: string }) => { dashboard.setQuery({ query: '', language: 'kuery' }); }, [dashboard, dashboardId, from, to]); + const { data: dashboardData, status } = useFetchDashboardById(dashboardId); + + if (!dashboardData && status === FETCH_STATUS.LOADING) { + return ; + } + if (!dashboardData && status !== FETCH_STATUS.LOADING && dashboardId.startsWith('kubernetes')) { + return ; + } + return ( Date: Mon, 23 Jun 2025 22:45:07 +0200 Subject: [PATCH 2/4] Install integration if there is data --- .../server/routes/internal/setup/route.ts | 220 ++++++++++-------- 1 file changed, 118 insertions(+), 102 deletions(-) diff --git a/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts b/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts index af469652c502e..18c8150165039 100644 --- a/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts +++ b/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts @@ -34,18 +34,6 @@ const infraSideNavRoute = createServerRoute({ const [fleetStart, core] = await Promise.all([plugins.fleet?.start(), context.core]); const packageClient = fleetStart?.packageService.asScoped(request); - const [installedPackage, ...navigationOverrides] = await Promise.all([ - packageClient?.getInstallation(KUBERNETES), - core.savedObjects.client.get( - OBSERVABILITY_NAVIGATION_OVERRIDES, - KUBERNETES - ), - core.savedObjects.client.get( - OBSERVABILITY_NAVIGATION_OVERRIDES, - DOCKER - ), - ]); - const metricbeatData = await core.elasticsearch.client.asCurrentUser.search({ index: 'metrics-kubernetes*', ignore_unavailable: true, @@ -85,102 +73,130 @@ const infraSideNavRoute = createServerRoute({ const hasEcsData = metricbeatData?.hits?.total?.value !== 0; const hasOtelData = otelData?.hits?.total?.value !== 0; + const [installedPackage, ...navigationOverrides] = await Promise.all([ + packageClient?.getInstallation(KUBERNETES), + core.savedObjects.client.get( + OBSERVABILITY_NAVIGATION_OVERRIDES, + KUBERNETES + ), + core.savedObjects.client.get( + OBSERVABILITY_NAVIGATION_OVERRIDES, + DOCKER + ), + ]); + + const packageInstalled = installedPackage ? [installedPackage] : []; + console.log('packageInstalled:', packageInstalled); + // Maybe separate the ecs / otel cases in the future - // if ((hasEcsData || hasOtelData) && !installedPackage ) { - // return [ - // { - // id: KUBERNETES, - // title: KUBERNETES, - // subItems: [ - // { - // id: 'kubernetes', - // sideNavTitle: 'Add Kubernetes data', - // sideNavOrder: 100, - // type: 'dashboard', - // }, - // ], - // }, - // ]; - // } + if ((hasEcsData || hasOtelData) && !installedPackage) { + // System package is always required + await packageClient?.ensureInstalledPackage({ pkgName: 'system' }); + // Kubernetes package is required for both classic kubernetes and otel + await packageClient?.ensureInstalledPackage({ pkgName: 'kubernetes' }); + const installedKubernetes = await packageClient?.getInstallation(KUBERNETES); + if (installedKubernetes) packageInstalled.push(installedKubernetes); + // Kubernetes otel package is required only for otel + if (hasOtelData) { + await packageClient?.ensureInstalledPackage({ pkgName: 'kubernetes_otel' }); + const installedOtelKubernetes = await packageClient?.getInstallation('kubernetes_otel'); + if (installedOtelKubernetes) packageInstalled.push(installedOtelKubernetes); + } + } - if (!installedPackage && !navigationOverrides) { + if ((!installedPackage || packageInstalled.length === 0) && !navigationOverrides) { return []; } + const otelInstalledPackage = + packageInstalled.find((pkg) => pkg.name === 'kubernetes_otel') || installedPackage; + // Mock data simulating the installed package's items returned by installedPackage.installed_kibana - const mockInstalledPackage = installedPackage - ? [ - { - id: 'kubernetes-0a672d50-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.cronjob', - sideNavTitle: 'Cron jobs', - sideNavOrder: 900, - type: 'dashboard', - }, - { - id: 'kubernetes-21694370-bcb2-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.statefulset', - sideNavTitle: 'Stateful sets', - sideNavOrder: 600, - type: 'dashboard', - }, - { - id: 'kubernetes-3912d9a0-bcb2-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.volume', - sideNavTitle: 'Volumes', - sideNavOrder: 500, - type: 'dashboard', - }, - { - id: 'kubernetes-3d4d9290-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.pod', - sideNavTitle: 'Pods', - sideNavOrder: 300, - type: 'dashboard', - }, - { - id: 'kubernetes-5be46210-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.deployment', - sideNavTitle: 'Deployments', - sideNavOrder: 400, - type: 'dashboard', - }, - { - id: 'kubernetes-85879010-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.daemonset', - sideNavTitle: 'Daemon sets', - sideNavOrder: 700, - type: 'dashboard', - }, - { - id: 'kubernetes-9bf990a0-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.job', - sideNavTitle: 'Jobs', - sideNavOrder: 800, - type: 'dashboard', - }, - { - id: 'kubernetes-b945b7b0-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.node', - sideNavTitle: 'Nodes', - sideNavOrder: 200, - type: 'dashboard', - }, - { - id: 'kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c', - sideNavTitle: 'Overview', - sideNavOrder: 100, - type: 'dashboard', - }, - { - id: 'kubernetes-ff1b3850-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.service', - sideNavTitle: 'Services', - sideNavOrder: 800, - type: 'dashboard', - }, - ] - : []; + const mockInstalledPackage = + installedPackage && packageInstalled.length > 0 + ? [ + { + id: 'kubernetes-0a672d50-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.cronjob', + sideNavTitle: 'Cron jobs', + sideNavOrder: 900, + type: 'dashboard', + }, + { + id: 'kubernetes-21694370-bcb2-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.statefulset', + sideNavTitle: 'Stateful sets', + sideNavOrder: 600, + type: 'dashboard', + }, + { + id: 'kubernetes-3912d9a0-bcb2-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.volume', + sideNavTitle: 'Volumes', + sideNavOrder: 500, + type: 'dashboard', + }, + { + id: 'kubernetes-3d4d9290-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.pod', + sideNavTitle: 'Pods', + sideNavOrder: 300, + type: 'dashboard', + }, + { + id: 'kubernetes-5be46210-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.deployment', + sideNavTitle: 'Deployments', + sideNavOrder: 400, + type: 'dashboard', + }, + { + id: 'kubernetes-85879010-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.daemonset', + sideNavTitle: 'Daemon sets', + sideNavOrder: 700, + type: 'dashboard', + }, + { + id: 'kubernetes-9bf990a0-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.job', + sideNavTitle: 'Jobs', + sideNavOrder: 800, + type: 'dashboard', + }, + { + id: 'kubernetes-b945b7b0-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.node', + sideNavTitle: 'Nodes', + sideNavOrder: 200, + type: 'dashboard', + }, + { + id: 'kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c', + sideNavTitle: 'Overview', + sideNavOrder: 100, + type: 'dashboard', + }, + { + id: 'kubernetes-ff1b3850-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.service', + sideNavTitle: 'Services', + sideNavOrder: 800, + type: 'dashboard', + }, + ...(otelInstalledPackage + ? [ + { + id: 'kubernetes_otel-cluster-overview', + entityType: 'k8s.overview', + sideNavTitle: 'Overview (Otel)', + sideNavOrder: 1000, + type: 'dashboard', + }, + ] + : []), + ] + : []; const integrationSubItems = mockInstalledPackage From 446a284fc0bd222559536cad4768edba46635435 Mon Sep 17 00:00:00 2001 From: Jenny Date: Wed, 25 Jun 2025 19:33:02 +0200 Subject: [PATCH 3/4] Add semconv config as source --- .../server/routes/internal/setup/route.ts | 202 +++++++++++------- 1 file changed, 121 insertions(+), 81 deletions(-) diff --git a/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts b/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts index 18c8150165039..fe51481788487 100644 --- a/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts +++ b/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts @@ -66,10 +66,7 @@ const infraSideNavRoute = createServerRoute({ }, }); - console.log('hasEcsData:', metricbeatData?.hits?.total?.value !== 0); - console.log('hasOtelData:', otelData?.hits?.total?.value !== 0); - - // TODO + // TODO Type fick for the response const hasEcsData = metricbeatData?.hits?.total?.value !== 0; const hasOtelData = otelData?.hits?.total?.value !== 0; @@ -86,10 +83,9 @@ const infraSideNavRoute = createServerRoute({ ]); const packageInstalled = installedPackage ? [installedPackage] : []; - console.log('packageInstalled:', packageInstalled); // Maybe separate the ecs / otel cases in the future - if ((hasEcsData || hasOtelData) && !installedPackage) { + if ((hasEcsData ?? hasOtelData) && !installedPackage) { // System package is always required await packageClient?.ensureInstalledPackage({ pkgName: 'system' }); // Kubernetes package is required for both classic kubernetes and otel @@ -97,7 +93,7 @@ const infraSideNavRoute = createServerRoute({ const installedKubernetes = await packageClient?.getInstallation(KUBERNETES); if (installedKubernetes) packageInstalled.push(installedKubernetes); // Kubernetes otel package is required only for otel - if (hasOtelData) { + if (hasOtelData && !hasEcsData) { await packageClient?.ensureInstalledPackage({ pkgName: 'kubernetes_otel' }); const installedOtelKubernetes = await packageClient?.getInstallation('kubernetes_otel'); if (installedOtelKubernetes) packageInstalled.push(installedOtelKubernetes); @@ -108,93 +104,131 @@ const infraSideNavRoute = createServerRoute({ return []; } - const otelInstalledPackage = - packageInstalled.find((pkg) => pkg.name === 'kubernetes_otel') || installedPackage; + const k8sEntitiesSemConv = [ + { + id: 'entity.k8s.cluster', // -> entityType + type: 'entity', + stability: 'development', + name: 'k8s.cluster', + brief: 'A Kubernetes Cluster.', + attributes: [{ ref: 'k8s.cluster.name' }, { ref: 'k8s.cluster.uid' }], + }, + { + id: 'entity.k8s.node', + type: 'entity', + stability: 'development', + name: 'k8s.node', + brief: 'A Kubernetes Node object.', + attributes: [ + { ref: 'k8s.node.name' }, + { ref: 'k8s.node.uid' }, + { + ref: 'k8s.node.label', + requirement_level: 'opt_in', + }, + { + ref: 'k8s.node.annotation', + requirement_level: 'opt_in', + }, + ], + }, + ]; + const otelMenuItems = !hasEcsData + ? k8sEntitiesSemConv.map((entity, index) => ({ + // id: `kubernetes_otel-${entity.id}`, + id: `kubernetes_otel-cluster-overview`, + entityType: entity.id, + sideNavTitle: entity.brief, + sideNavOrder: (index || 1) * 100, + type: 'dashboard', + })) + : []; // Mock data simulating the installed package's items returned by installedPackage.installed_kibana const mockInstalledPackage = installedPackage && packageInstalled.length > 0 ? [ - { - id: 'kubernetes-0a672d50-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.cronjob', - sideNavTitle: 'Cron jobs', - sideNavOrder: 900, - type: 'dashboard', - }, - { - id: 'kubernetes-21694370-bcb2-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.statefulset', - sideNavTitle: 'Stateful sets', - sideNavOrder: 600, - type: 'dashboard', - }, - { - id: 'kubernetes-3912d9a0-bcb2-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.volume', - sideNavTitle: 'Volumes', - sideNavOrder: 500, - type: 'dashboard', - }, - { - id: 'kubernetes-3d4d9290-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.pod', - sideNavTitle: 'Pods', - sideNavOrder: 300, - type: 'dashboard', - }, - { - id: 'kubernetes-5be46210-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.deployment', - sideNavTitle: 'Deployments', - sideNavOrder: 400, - type: 'dashboard', - }, - { - id: 'kubernetes-85879010-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.daemonset', - sideNavTitle: 'Daemon sets', - sideNavOrder: 700, - type: 'dashboard', - }, - { - id: 'kubernetes-9bf990a0-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.job', - sideNavTitle: 'Jobs', - sideNavOrder: 800, - type: 'dashboard', - }, - { - id: 'kubernetes-b945b7b0-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.node', - sideNavTitle: 'Nodes', - sideNavOrder: 200, - type: 'dashboard', - }, - { - id: 'kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c', - sideNavTitle: 'Overview', - sideNavOrder: 100, - type: 'dashboard', - }, - { - id: 'kubernetes-ff1b3850-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.service', - sideNavTitle: 'Services', - sideNavOrder: 800, - type: 'dashboard', - }, - ...(otelInstalledPackage + ...(hasOtelData && !hasEcsData ? [ { id: 'kubernetes_otel-cluster-overview', entityType: 'k8s.overview', sideNavTitle: 'Overview (Otel)', - sideNavOrder: 1000, + sideNavOrder: 100, type: 'dashboard', }, ] - : []), + : [ + { + id: 'kubernetes-0a672d50-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.cronjob', + sideNavTitle: 'Cron jobs', + sideNavOrder: 900, + type: 'dashboard', + }, + { + id: 'kubernetes-21694370-bcb2-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.statefulset', + sideNavTitle: 'Stateful sets', + sideNavOrder: 600, + type: 'dashboard', + }, + { + id: 'kubernetes-3912d9a0-bcb2-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.volume', + sideNavTitle: 'Volumes', + sideNavOrder: 500, + type: 'dashboard', + }, + { + id: 'kubernetes-3d4d9290-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.pod', + sideNavTitle: 'Pods', + sideNavOrder: 300, + type: 'dashboard', + }, + { + id: 'kubernetes-5be46210-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.deployment', + sideNavTitle: 'Deployments', + sideNavOrder: 400, + type: 'dashboard', + }, + { + id: 'kubernetes-85879010-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.daemonset', + sideNavTitle: 'Daemon sets', + sideNavOrder: 700, + type: 'dashboard', + }, + { + id: 'kubernetes-9bf990a0-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.job', + sideNavTitle: 'Jobs', + sideNavOrder: 800, + type: 'dashboard', + }, + { + id: 'kubernetes-b945b7b0-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.node', + sideNavTitle: 'Nodes', + sideNavOrder: 200, + type: 'dashboard', + }, + { + id: 'kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c', + sideNavTitle: 'Overview', + sideNavOrder: 100, + type: 'dashboard', + }, + { + id: 'kubernetes-ff1b3850-bcb1-11ec-b64f-7dd6e8e82013', + entityType: 'k8s.service', + sideNavTitle: 'Services', + sideNavOrder: 800, + type: 'dashboard', + }, + ]), ] : []; @@ -203,6 +237,12 @@ const infraSideNavRoute = createServerRoute({ .filter((p) => !!p.sideNavTitle) .sort((a, b) => (a.sideNavOrder ?? 0) - (b.sideNavOrder ?? 0)) ?? []; + integrationSubItems.push( + ...(otelMenuItems + .filter((p) => !!p.sideNavTitle) + .sort((a, b) => (a.sideNavOrder ?? 0) - (b.sideNavOrder ?? 0)) ?? []) + ); + const integrationNavigation = integrationSubItems.length > 0 ? [ From 70ab59851e44fd37dc2f3d0409865712e56393b4 Mon Sep 17 00:00:00 2001 From: Jenny Date: Thu, 26 Jun 2025 13:02:32 +0200 Subject: [PATCH 4/4] Remove ecs logic and leave OTel only --- .../server/routes/internal/setup/route.ts | 175 ++++-------------- .../components/page_content/page_content.tsx | 63 +------ .../public/pages/metrics/dashboard/index.tsx | 2 +- 3 files changed, 37 insertions(+), 203 deletions(-) diff --git a/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts b/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts index fe51481788487..4d856e2bbee85 100644 --- a/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts +++ b/x-pack/platform/plugins/shared/observability_navigation/server/routes/internal/setup/route.ts @@ -12,7 +12,6 @@ import { createServerRoute } from '../../../create_server_route'; const KUBERNETES = 'kubernetes'; const DOCKER = 'docker'; -const METRICBEAT = 'metricbeat'; const infraSideNavRoute = createServerRoute({ endpoint: 'GET /internal/observability_navigation', @@ -34,24 +33,6 @@ const infraSideNavRoute = createServerRoute({ const [fleetStart, core] = await Promise.all([plugins.fleet?.start(), context.core]); const packageClient = fleetStart?.packageService.asScoped(request); - const metricbeatData = await core.elasticsearch.client.asCurrentUser.search({ - index: 'metrics-kubernetes*', - ignore_unavailable: true, - allow_no_indices: true, - track_total_hits: true, - terminate_after: 1, - size: 0, - query: { - bool: { - should: [ - { term: { ['event.module']: KUBERNETES } }, - { term: { ['agent.type']: METRICBEAT } }, - ], - minimum_should_match: 1, - }, - }, - }); - const otelData = await core.elasticsearch.client.asCurrentUser.search({ index: 'metrics-*.otel-*', ignore_unavailable: true, @@ -61,14 +42,13 @@ const infraSideNavRoute = createServerRoute({ size: 0, query: { bool: { - filter: [{ term: { ['data_stream.dataset']: '*.otel' } }], + filter: [{ term: { ['data_stream.dataset']: 'k8sclusterreceiver.otel' } }], }, }, }); - // TODO Type fick for the response - const hasEcsData = metricbeatData?.hits?.total?.value !== 0; - const hasOtelData = otelData?.hits?.total?.value !== 0; + const totalHits = otelData?.hits?.total; + const hasOtelData = typeof totalHits === 'number' ? totalHits !== 0 : totalHits?.value !== 0; const [installedPackage, ...navigationOverrides] = await Promise.all([ packageClient?.getInstallation(KUBERNETES), @@ -82,31 +62,24 @@ const infraSideNavRoute = createServerRoute({ ), ]); - const packageInstalled = installedPackage ? [installedPackage] : []; + const otelPackageInstalled = installedPackage ? [installedPackage] : []; - // Maybe separate the ecs / otel cases in the future - if ((hasEcsData ?? hasOtelData) && !installedPackage) { - // System package is always required - await packageClient?.ensureInstalledPackage({ pkgName: 'system' }); - // Kubernetes package is required for both classic kubernetes and otel - await packageClient?.ensureInstalledPackage({ pkgName: 'kubernetes' }); - const installedKubernetes = await packageClient?.getInstallation(KUBERNETES); - if (installedKubernetes) packageInstalled.push(installedKubernetes); - // Kubernetes otel package is required only for otel - if (hasOtelData && !hasEcsData) { - await packageClient?.ensureInstalledPackage({ pkgName: 'kubernetes_otel' }); - const installedOtelKubernetes = await packageClient?.getInstallation('kubernetes_otel'); - if (installedOtelKubernetes) packageInstalled.push(installedOtelKubernetes); - } + if (hasOtelData && !installedPackage) { + // We will just install the otel package if it is not installed for now + await packageClient?.ensureInstalledPackage({ pkgName: 'kubernetes_otel' }); + const installedOtelKubernetes = await packageClient?.getInstallation('kubernetes_otel'); + if (installedOtelKubernetes) otelPackageInstalled.push(installedOtelKubernetes); } - if ((!installedPackage || packageInstalled.length === 0) && !navigationOverrides) { + if ((!installedPackage || otelPackageInstalled.length === 0) && !navigationOverrides) { return []; } + // It will come from the entities definiition later + // For now we will just use the entities defined in the semconv const k8sEntitiesSemConv = [ { - id: 'entity.k8s.cluster', // -> entityType + id: 'entity.k8s.cluster', // -> Use to map to entityType? type: 'entity', stability: 'development', name: 'k8s.cluster', @@ -133,123 +106,41 @@ const infraSideNavRoute = createServerRoute({ ], }, ]; - const otelMenuItems = !hasEcsData - ? k8sEntitiesSemConv.map((entity, index) => ({ - // id: `kubernetes_otel-${entity.id}`, - id: `kubernetes_otel-cluster-overview`, - entityType: entity.id, - sideNavTitle: entity.brief, - sideNavOrder: (index || 1) * 100, - type: 'dashboard', - })) - : []; + const otelMenuItems = k8sEntitiesSemConv.map((entity, index) => ({ + // id: `kubernetes_otel-${entity.id}`, + id: `kubernetes_otel-cluster-overview`, + entityType: entity.id, + sideNavTitle: entity.brief, + sideNavOrder: (index || 1) * 100, + type: 'dashboard', + })); // Mock data simulating the installed package's items returned by installedPackage.installed_kibana const mockInstalledPackage = - installedPackage && packageInstalled.length > 0 + installedPackage && otelPackageInstalled.length > 0 && hasOtelData ? [ - ...(hasOtelData && !hasEcsData - ? [ - { - id: 'kubernetes_otel-cluster-overview', - entityType: 'k8s.overview', - sideNavTitle: 'Overview (Otel)', - sideNavOrder: 100, - type: 'dashboard', - }, - ] - : [ - { - id: 'kubernetes-0a672d50-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.cronjob', - sideNavTitle: 'Cron jobs', - sideNavOrder: 900, - type: 'dashboard', - }, - { - id: 'kubernetes-21694370-bcb2-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.statefulset', - sideNavTitle: 'Stateful sets', - sideNavOrder: 600, - type: 'dashboard', - }, - { - id: 'kubernetes-3912d9a0-bcb2-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.volume', - sideNavTitle: 'Volumes', - sideNavOrder: 500, - type: 'dashboard', - }, - { - id: 'kubernetes-3d4d9290-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.pod', - sideNavTitle: 'Pods', - sideNavOrder: 300, - type: 'dashboard', - }, - { - id: 'kubernetes-5be46210-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.deployment', - sideNavTitle: 'Deployments', - sideNavOrder: 400, - type: 'dashboard', - }, - { - id: 'kubernetes-85879010-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.daemonset', - sideNavTitle: 'Daemon sets', - sideNavOrder: 700, - type: 'dashboard', - }, - { - id: 'kubernetes-9bf990a0-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.job', - sideNavTitle: 'Jobs', - sideNavOrder: 800, - type: 'dashboard', - }, - { - id: 'kubernetes-b945b7b0-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.node', - sideNavTitle: 'Nodes', - sideNavOrder: 200, - type: 'dashboard', - }, - { - id: 'kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c', - sideNavTitle: 'Overview', - sideNavOrder: 100, - type: 'dashboard', - }, - { - id: 'kubernetes-ff1b3850-bcb1-11ec-b64f-7dd6e8e82013', - entityType: 'k8s.service', - sideNavTitle: 'Services', - sideNavOrder: 800, - type: 'dashboard', - }, - ]), + { + id: 'kubernetes_otel-cluster-overview', + entityType: 'k8s.overview', + sideNavTitle: 'Overview (Otel)', + sideNavOrder: 100, + type: 'dashboard', + }, ] : []; - const integrationSubItems = - mockInstalledPackage + const otelNavigationItemsSorted = + [...otelMenuItems, ...mockInstalledPackage] .filter((p) => !!p.sideNavTitle) .sort((a, b) => (a.sideNavOrder ?? 0) - (b.sideNavOrder ?? 0)) ?? []; - integrationSubItems.push( - ...(otelMenuItems - .filter((p) => !!p.sideNavTitle) - .sort((a, b) => (a.sideNavOrder ?? 0) - (b.sideNavOrder ?? 0)) ?? []) - ); - const integrationNavigation = - integrationSubItems.length > 0 + otelNavigationItemsSorted.length > 0 ? [ { id: `${KUBERNETES.toLowerCase().replace(/[\.\s]/g, '-')}`, title: KUBERNETES, - subItems: integrationSubItems.map((item) => { + subItems: otelNavigationItemsSorted.map((item) => { return { id: `${item.sideNavTitle.toLowerCase().replace(/[\.\s]/g, '-')}`, title: item.sideNavTitle, diff --git a/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/page_content/page_content.tsx b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/page_content/page_content.tsx index 1c17da03cfbfa..68bcb12bab3e6 100644 --- a/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/page_content/page_content.tsx +++ b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/components/page_content/page_content.tsx @@ -5,44 +5,13 @@ * 2.0. */ -import React, { useState, useMemo } from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiFormRow, EuiLoadingSpinner, EuiSwitch } from '@elastic/eui'; +import React from 'react'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { useTimeRangeMetadataContext } from '../../../../../hooks/use_timerange_metadata'; import { RenderDashboard } from '../dashboard/render_dashboard'; -const OverviewDashboardsPerSchema = { - semconv: 'kubernetes_otel-cluster-overview', - ecs: 'kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c', -} as const; - -export const PageContent = ({ - dashboardId, - hasMultipleDashboards, -}: { - dashboardId: string; - hasMultipleDashboards: boolean; -}) => { +export const PageContent = ({ dashboardId }: { dashboardId: string }) => { const { data, status } = useTimeRangeMetadataContext(); - const [currentDashboardId, setCurrentDashboardId] = useState( - hasMultipleDashboards ? OverviewDashboardsPerSchema.semconv : dashboardId - ); - - const onChange = (e: { target: { checked: React.SetStateAction } }) => { - setCurrentDashboardId( - e.target.checked - ? OverviewDashboardsPerSchema.semconv - : dashboardId ?? OverviewDashboardsPerSchema.ecs - ); - }; - - const shouldRenderMultipleDashboardsToggle = useMemo( - () => - Object.keys(OverviewDashboardsPerSchema).every((key) => - data?.schemas.includes(key as keyof typeof OverviewDashboardsPerSchema) - ), - [data?.schemas] - ); if (status === 'loading') { return ; @@ -52,31 +21,5 @@ export const PageContent = ({ return null; } - if (shouldRenderMultipleDashboardsToggle) { - return ( - <> - - {i18n.translate('xpack.infra.pageContent.span.otelfocusedDashboardLabel', { - defaultMessage: 'OTel-focused dashboard', - })} - - } - > - - - - - ); - } - return ; }; diff --git a/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/index.tsx b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/index.tsx index cf4eec31a9fc5..c96516d7afd89 100644 --- a/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/index.tsx +++ b/x-pack/solutions/observability/plugins/infra/public/pages/metrics/dashboard/index.tsx @@ -77,7 +77,7 @@ export const Dashboard = () => { data-test-subj="infraKubernetesPage" > - +