From 4957c53ee7330750ed2b1de94b9febcd40659e39 Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Fri, 27 Jan 2023 19:10:08 +0100 Subject: [PATCH] Add ingest pipeline tab to Stack Monitoring --- x-pack/plugins/monitoring/kibana.json | 4 +- .../elasticsearch/elasticsearch_template.tsx | 26 +++ .../elasticsearch/ingest_pipeline_modal.tsx | 162 ++++++++++++++++++ .../application/pages/page_template.tsx | 10 +- x-pack/plugins/monitoring/public/types.ts | 4 + 5 files changed, 202 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/monitoring/public/application/pages/elasticsearch/ingest_pipeline_modal.tsx diff --git a/x-pack/plugins/monitoring/kibana.json b/x-pack/plugins/monitoring/kibana.json index bbb6eb374e91d..0781ecdc170c3 100644 --- a/x-pack/plugins/monitoring/kibana.json +++ b/x-pack/plugins/monitoring/kibana.json @@ -16,7 +16,9 @@ "triggersActionsUi", "alerting", "actions", - "encryptedSavedObjects" + "encryptedSavedObjects", + "dashboard", + "fleet" ], "server": true, "ui": true, diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/elasticsearch_template.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/elasticsearch_template.tsx index aa7ca97219206..b034478f720c3 100644 --- a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/elasticsearch_template.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/elasticsearch_template.tsx @@ -6,10 +6,15 @@ */ import React from 'react'; import { i18n } from '@kbn/i18n'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; import { includes } from 'lodash'; +import { DashboardStart } from '@kbn/dashboard-plugin/public'; +import { FleetStart } from '@kbn/fleet-plugin/public'; +import { EuiIcon, EuiToolTip } from '@elastic/eui'; import { PageTemplate } from '../page_template'; import { TabMenuItem, PageTemplateProps } from '../page_template'; import { ML_SUPPORTED_LICENSES } from '../../../../common/constants'; +import { ingestPipelineTabOnClick } from './ingest_pipeline_modal'; interface ElasticsearchTemplateProps extends PageTemplateProps { cluster?: any; @@ -19,6 +24,8 @@ export const ElasticsearchTemplate: React.FC = ({ cluster, ...props }) => { + const { services } = useKibana<{ dashboard?: DashboardStart; fleet?: FleetStart }>(); + const tabs: TabMenuItem[] = [ { id: 'overview', @@ -43,6 +50,25 @@ export const ElasticsearchTemplate: React.FC = ({ }, ]; + if (services.dashboard) { + tabs.push({ + id: 'ingest_pipeines', + label: i18n.translate('xpack.monitoring.esNavigation.ingestPipelinesLinkText', { + defaultMessage: 'Ingest Pipelines', + }), + prepend: ( + + + + ), + onClick: () => ingestPipelineTabOnClick(services), + }); + } + if (cluster && mlIsSupported(cluster.license)) { tabs.push({ id: 'ml', diff --git a/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ingest_pipeline_modal.tsx b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ingest_pipeline_modal.tsx new file mode 100644 index 0000000000000..2d1e25859c39e --- /dev/null +++ b/x-pack/plugins/monitoring/public/application/pages/elasticsearch/ingest_pipeline_modal.tsx @@ -0,0 +1,162 @@ +/* + * 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, { useState } from 'react'; +import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import type { CoreStart } from '@kbn/core/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { EuiCallOut, EuiConfirmModal, EuiSpacer } from '@elastic/eui'; +import { DashboardStart } from '@kbn/dashboard-plugin/public'; +import { FleetStart } from '@kbn/fleet-plugin/public'; + +const INGEST_PIPELINE_DASHBOARD_ID = 'elasticsearch-metrics-ingest-pipelines'; + +/** + * If the ingest pipeline dashboard is installed, navigate to it. Otherwise, prompt the user to install the package + * first, then navigate. If user does not have permission to install packages, show a message. + * @param services + * @returns + */ +export const ingestPipelineTabOnClick = async ( + services: Partial +) => { + const dashboard = await services.savedObjects!.client.get( + 'dashboard', + INGEST_PIPELINE_DASHBOARD_ID + ); + const dashboardFound = !dashboard.error && dashboard.attributes; + + const navigateToDashboard = () => + services.dashboard!.locator!.navigate({ + dashboardId: INGEST_PIPELINE_DASHBOARD_ID, + }); + + if (!dashboardFound) { + const installPackage = () => + services.http!.post('/api/fleet/epm/packages/elasticsearch', { + query: { + prerelease: true, + }, + }); + + const ref = services.overlays!.openModal( + toMountPoint( + ref.close()} + />, + { + theme$: services.theme?.theme$, + } + ) + ); + + return await ref.onClose; + } else { + return navigateToDashboard(); + } +}; + +/** + * Modal to prompt the user to either install the Elasticsearch integration or contact an admin. + */ +export const IngestPipelineModal = ({ + canInstallPackages, + closeModal, + installPackage, + navigateToDashboard, +}: { + closeModal: () => void; + canInstallPackages: boolean; + installPackage: () => Promise; + navigateToDashboard: () => void; +}) => { + const [installing, setInstalling] = useState(false); + const [error, setError] = useState(); + + if (!canInstallPackages) { + return ( + +

+ +

+
+ ); + } + + return ( + { + setInstalling(true); + try { + await installPackage(); + closeModal(); + navigateToDashboard(); + } catch (e) { + setError(e.body?.error || e.message); + } + }} + > + {error && ( + <> + + } + color="danger" + iconType="alert" + /> + + + )} +

+ +

+
+ ); +}; diff --git a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx index 8912b38f57a20..897d679785be7 100644 --- a/x-pack/plugins/monitoring/public/application/pages/page_template.tsx +++ b/x-pack/plugins/monitoring/public/application/pages/page_template.tsx @@ -38,7 +38,9 @@ export interface TabMenuItem { id: string; label: string; testSubj?: string; - route: string; + route?: string; + onClick?: () => void; + prepend?: React.ReactNode; } export interface PageTemplateProps { title: string; @@ -139,8 +141,10 @@ export const PageTemplate: React.FC = ({ disabled={isDisabledTab(product)} title={item.label} data-test-subj={item.testSubj} - href={createHref(item.route)} - isSelected={isTabSelected(item.route)} + href={item.route ? createHref(item.route) : undefined} + isSelected={item.route ? isTabSelected(item.route) : undefined} + onClick={item.onClick} + prepend={item.prepend} > {item.label} diff --git a/x-pack/plugins/monitoring/public/types.ts b/x-pack/plugins/monitoring/public/types.ts index af232cbbf302a..6eaf446b59ed8 100644 --- a/x-pack/plugins/monitoring/public/types.ts +++ b/x-pack/plugins/monitoring/public/types.ts @@ -13,6 +13,8 @@ import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; export type { MonitoringConfig } from '../server'; export type { MLJobs } from '../server/lib/elasticsearch/get_ml_jobs'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import { DashboardStart } from '@kbn/dashboard-plugin/public'; +import { FleetStart } from '@kbn/fleet-plugin/public'; export interface MonitoringStartPluginDependencies { navigation: NavigationStart; @@ -20,6 +22,8 @@ export interface MonitoringStartPluginDependencies { triggersActionsUi: TriggersAndActionsUIPublicPluginStart; usageCollection: UsageCollectionSetup; dataViews: DataViewsPublicPluginStart; + dashboard?: DashboardStart; + fleet?: FleetStart; } interface LegacyStartDependencies {