kibana/x-pack/plugins/infra/public/plugin.ts
Mykola Harmash d0a0a1f9e6
[Infra IU] Disable Metrics Explorer for serverless (#167022)
Closes #163282 

## Summary

This PR:
* Adds a `featureFlags.metricsExplorerEnabled` property to the Infra
plugin config to enable and disable Metrics Explorer depending on the
offering type
* Prevents `MetricsExplorerViewsService` initialization for serveless
based on the feature flag
* Prevents creating Metrics Explorer frontend routes when in serverless
* Prevents registration of the MetricsExplorerViews saved object when in
serverless
* Prevents initialization of the `metrics_explorer_views` API routes
when in serverless

**Trying to access Metrics Explorer in serverless**
<img width="1829" alt="CleanShot 2023-09-22 at 12 59 35@2x"
src="2b039925-0f0b-4c07-be29-bbe910de7a34">

**Trying to access views API**
<img width="1829" alt="CleanShot 2023-09-22 at 13 00 00@2x"
src="15269ec2-becd-4ee3-9b5e-d916df28a7b8">

**`infra/metrics_explorer` API still works as per ticket requirements**
<img width="1829" alt="CleanShot 2023-09-22 at 13 00 06@2x"
src="fb23f912-c6fd-46c8-9084-c17c51e5b064">


## How to test
* Checkout locally
* Enable Infra in `serverless.oblt.yml`: `xpack.infra.enabled: true`
* Run Kibana in serverless mode
* Try accessing `/app/metrics/explorer` route and make sure it's not
available
* Make sure other Infra routes (`/app/metrics/inventory` and
`/app/metrics/hosts`) still load as expected
* In Kibana dev console make sure you get 404 for `GET
kbn:/api/infra/metrics_explorer_views`
* Also check that you don't see `metrics-explorer-view` saved object in
the response for `GET
kbn:/api/kibana/management/saved_objects/_allowed_types`
* Run Kibana in non-serverless mode and make sure Metrics Explorer is
accessible and works as usual

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
2023-09-29 11:27:19 +02:00

345 lines
12 KiB
TypeScript

/*
* 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 {
AppMountParameters,
AppUpdater,
CoreStart,
DEFAULT_APP_CATEGORIES,
PluginInitializerContext,
} from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
import { enableInfrastructureHostsView } from '@kbn/observability-plugin/public';
import { ObservabilityTriggerId } from '@kbn/observability-shared-plugin/common';
import { BehaviorSubject, combineLatest, from } from 'rxjs';
import { map } from 'rxjs/operators';
import { InfraPublicConfig } from '../common/plugin_config_types';
import { createInventoryMetricRuleType } from './alerting/inventory';
import { createLogThresholdRuleType } from './alerting/log_threshold';
import { createMetricThresholdRuleType } from './alerting/metric_threshold';
import { createLazyContainerMetricsTable } from './components/infrastructure_node_metrics_tables/container/create_lazy_container_metrics_table';
import { createLazyHostMetricsTable } from './components/infrastructure_node_metrics_tables/host/create_lazy_host_metrics_table';
import { createLazyPodMetricsTable } from './components/infrastructure_node_metrics_tables/pod/create_lazy_pod_metrics_table';
import { LOG_STREAM_EMBEDDABLE } from './components/log_stream/log_stream_embeddable';
import { LogStreamEmbeddableFactoryDefinition } from './components/log_stream/log_stream_embeddable_factory';
import {
InfraLocators,
LogsLocatorDefinition,
NodeLogsLocatorDefinition,
} from '../common/locators';
import { createMetricsFetchData, createMetricsHasData } from './metrics_overview_fetchers';
import { registerFeatures } from './register_feature';
import { InventoryViewsService } from './services/inventory_views';
import { MetricsExplorerViewsService } from './services/metrics_explorer_views';
import { TelemetryService } from './services/telemetry';
import {
InfraClientCoreSetup,
InfraClientCoreStart,
InfraClientPluginClass,
InfraClientSetupDeps,
InfraClientStartDeps,
InfraClientStartExports,
InfraClientStartServices,
} from './types';
import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_overview_fetchers';
export class Plugin implements InfraClientPluginClass {
public config: InfraPublicConfig;
private inventoryViews: InventoryViewsService;
private metricsExplorerViews?: MetricsExplorerViewsService;
private telemetry: TelemetryService;
private locators?: InfraLocators;
private kibanaVersion: string;
private readonly appUpdater$ = new BehaviorSubject<AppUpdater>(() => ({}));
constructor(context: PluginInitializerContext<InfraPublicConfig>) {
this.config = context.config.get();
this.inventoryViews = new InventoryViewsService();
this.metricsExplorerViews = this.config.featureFlags.metricsExplorerEnabled
? new MetricsExplorerViewsService()
: undefined;
this.telemetry = new TelemetryService();
this.kibanaVersion = context.env.packageInfo.version;
}
setup(core: InfraClientCoreSetup, pluginsSetup: InfraClientSetupDeps) {
if (pluginsSetup.home) {
registerFeatures(pluginsSetup.home);
}
pluginsSetup.uiActions.registerTrigger({
id: ObservabilityTriggerId.LogEntryContextMenu,
});
pluginsSetup.observability.observabilityRuleTypeRegistry.register(
createInventoryMetricRuleType()
);
pluginsSetup.observability.observabilityRuleTypeRegistry.register(
createMetricThresholdRuleType()
);
pluginsSetup.observability.dashboard.register({
appName: 'infra_logs',
hasData: getLogsHasDataFetcher(core.getStartServices),
fetchData: getLogsOverviewDataFetcher(core.getStartServices),
});
pluginsSetup.observability.dashboard.register({
appName: 'infra_metrics',
hasData: createMetricsHasData(core.getStartServices),
fetchData: createMetricsFetchData(core.getStartServices),
});
pluginsSetup.logsShared.logViews.setLogViewsStaticConfig({
messageFields: this.config.sources?.default?.fields?.message,
});
const startDep$AndHostViewFlag$ = combineLatest([
from(core.getStartServices()),
core.uiSettings.get$<boolean>(enableInfrastructureHostsView),
]);
/** !! Need to be kept in sync with the deepLinks in x-pack/plugins/infra/public/plugin.ts */
const infraEntries = [
{ label: 'Inventory', app: 'metrics', path: '/inventory' },
...(this.config.featureFlags.metricsExplorerEnabled
? [{ label: 'Metrics Explorer', app: 'metrics', path: '/explorer' }]
: []),
{ label: 'Hosts', isBetaFeature: true, app: 'metrics', path: '/hosts' },
];
pluginsSetup.observabilityShared.navigation.registerSections(
startDep$AndHostViewFlag$.pipe(
map(
([
[
{
application: { capabilities },
},
],
]) => [
...(capabilities.logs.show
? [
{
label: 'Logs',
sortKey: 200,
entries: [
{
label: 'Explorer',
app: 'observability-log-explorer',
path: '/',
isBetaFeature: true,
},
{ label: 'Stream', app: 'logs', path: '/stream' },
{ label: 'Anomalies', app: 'logs', path: '/anomalies' },
{ label: 'Categories', app: 'logs', path: '/log-categories' },
],
},
]
: []),
...(capabilities.infrastructure.show
? [
{
label: 'Infrastructure',
sortKey: 300,
entries: infraEntries,
},
]
: []),
]
)
)
);
pluginsSetup.embeddable.registerEmbeddableFactory(
LOG_STREAM_EMBEDDABLE,
new LogStreamEmbeddableFactoryDefinition(core.getStartServices)
);
// Register Locators
const logsLocator = pluginsSetup.share.url.locators.create(new LogsLocatorDefinition({ core }));
const nodeLogsLocator = pluginsSetup.share.url.locators.create(
new NodeLogsLocatorDefinition({ core })
);
pluginsSetup.observability.observabilityRuleTypeRegistry.register(
createLogThresholdRuleType(core, logsLocator)
);
core.application.register({
id: 'logs',
title: i18n.translate('xpack.infra.logs.pluginTitle', {
defaultMessage: 'Logs',
}),
euiIconType: 'logoObservability',
order: 8100,
appRoute: '/app/logs',
// !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/logs/page_content.tsx
deepLinks: [
{
id: 'stream',
title: i18n.translate('xpack.infra.logs.index.streamTabTitle', {
defaultMessage: 'Stream',
}),
path: '/stream',
},
{
id: 'anomalies',
title: i18n.translate('xpack.infra.logs.index.anomaliesTabTitle', {
defaultMessage: 'Anomalies',
}),
path: '/anomalies',
},
{
id: 'log-categories',
title: i18n.translate('xpack.infra.logs.index.logCategoriesBetaBadgeTitle', {
defaultMessage: 'Categories',
}),
path: '/log-categories',
},
{
id: 'settings',
title: i18n.translate('xpack.infra.logs.index.settingsTabTitle', {
defaultMessage: 'Settings',
}),
path: '/settings',
},
],
category: DEFAULT_APP_CATEGORIES.observability,
mount: async (params: AppMountParameters) => {
// mount callback should not use setup dependencies, get start dependencies instead
const [coreStart, plugins, pluginStart] = await core.getStartServices();
const { renderApp } = await import('./apps/logs_app');
return renderApp(coreStart, plugins, pluginStart, params);
},
});
// !! Need to be kept in sync with the routes in x-pack/plugins/infra/public/pages/metrics/index.tsx
const infraDeepLinks = [
{
id: 'inventory',
title: i18n.translate('xpack.infra.homePage.inventoryTabTitle', {
defaultMessage: 'Inventory',
}),
path: '/inventory',
},
{
id: 'metrics-hosts',
title: i18n.translate('xpack.infra.homePage.metricsHostsTabTitle', {
defaultMessage: 'Hosts',
}),
path: '/hosts',
},
...(this.config.featureFlags.metricsExplorerEnabled
? [
{
id: 'metrics-explorer',
title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', {
defaultMessage: 'Metrics Explorer',
}),
path: '/explorer',
},
]
: []),
{
id: 'settings',
title: i18n.translate('xpack.infra.homePage.settingsTabTitle', {
defaultMessage: 'Settings',
}),
path: '/settings',
},
];
core.application.register({
id: 'metrics',
title: i18n.translate('xpack.infra.metrics.pluginTitle', {
defaultMessage: 'Infrastructure',
}),
euiIconType: 'logoObservability',
order: 8200,
appRoute: '/app/metrics',
category: DEFAULT_APP_CATEGORIES.observability,
updater$: this.appUpdater$,
deepLinks: infraDeepLinks,
mount: async (params: AppMountParameters) => {
// mount callback should not use setup dependencies, get start dependencies instead
const [coreStart, plugins, pluginStart] = await core.getStartServices();
const { renderApp } = await import('./apps/metrics_app');
return renderApp(
coreStart,
{ ...plugins, kibanaVersion: this.kibanaVersion },
pluginStart,
this.config,
params
);
},
});
startDep$AndHostViewFlag$.subscribe(
([_startServices]: [[CoreStart, InfraClientStartDeps, InfraClientStartExports], boolean]) => {
this.appUpdater$.next(() => ({
deepLinks: infraDeepLinks,
}));
}
);
/* This exists purely to facilitate URL redirects from the old App ID ("infra"),
to our new App IDs ("metrics" and "logs"). With version 8.0.0 we can remove this. */
core.application.register({
id: 'infra',
appRoute: '/app/infra',
title: 'infra',
navLinkStatus: 3,
mount: async (params: AppMountParameters) => {
const { renderApp } = await import('./apps/legacy_app');
return renderApp(params);
},
});
// Setup telemetry events
this.telemetry.setup({ analytics: core.analytics });
this.locators = {
logsLocator,
nodeLogsLocator,
};
return {
locators: this.locators,
};
}
start(core: InfraClientCoreStart, plugins: InfraClientStartDeps) {
const getStartServices = (): InfraClientStartServices => [core, plugins, startContract];
const inventoryViews = this.inventoryViews.start({
http: core.http,
});
const metricsExplorerViews = this.metricsExplorerViews?.start({
http: core.http,
});
const telemetry = this.telemetry.start();
const startContract: InfraClientStartExports = {
inventoryViews,
metricsExplorerViews,
telemetry,
locators: this.locators!,
ContainerMetricsTable: createLazyContainerMetricsTable(getStartServices),
HostMetricsTable: createLazyHostMetricsTable(getStartServices),
PodMetricsTable: createLazyPodMetricsTable(getStartServices),
};
return startContract;
}
stop() {}
}